Android:在ViewModel

时间:2017-11-18 14:14:21

标签: android kotlin android-architecture-components

在ViewModel中获取Android Room DAO的最佳方法是什么?

基于paging library示例,我编写了这个ViewModel:

class MyViewModel(myDao: MyDao) : ViewModel() {

    val data = myDao.get().create(
            /* initial load position */ 0,
            PagedList.Config.Builder()
                    .setPageSize(50)
                    .setPrefetchDistance(50)
                    .build())
}

然后我尝试获取一个实例

val viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)

尝试运行此操作时出现异常:

java.lang.RuntimeException: Unable to start activity ComponentInfo{....MyActivity}: java.lang.RuntimeException: Cannot create an instance of class ...MyViewModel
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2817)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
    at android.app.ActivityThread.-wrap11(Unknown Source:0)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
    at android.os.Handler.dispatchMessage(Handler.java:105)
    at android.os.Looper.loop(Looper.java:164)
    at android.app.ActivityThread.main(ActivityThread.java:6541)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
 Caused by: java.lang.RuntimeException: Cannot create an instance of class ...MyViewModel
    at android.arch.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.java:145)
    at android.arch.lifecycle.ViewModelProviders$DefaultFactory.create(ViewModelProviders.java:158)
    at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:128)
    at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:96)
    ...
 Caused by: java.lang.InstantiationException: java.lang.Class<...MyViewModel> has no zero argument constructor

paging library示例中,不清楚视图模型如何获取DAO的副本,显然它失败了。问题是,如果我错过了某些内容,或者示例是否不完整?

Google搜索异常我发现使用ViewModelProvider.Factory的建议,只有示例没有使用它。在示例代码中,视图模型如下所示:

class MyViewModel extends ViewModel {
    public final LiveData<PagedList<User>> usersList;
    public MyViewModel(UserDao userDao) {
        usersList = userDao.usersByLastName().create(
                /* initial load position */ 0,
                new PagedList.Config.Builder()
                        .setPageSize(50)
                        .setPrefetchDistance(50)
                        .build());
    }
}

并且像这样被追踪

MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);

我的依赖

def roomVersion = "1.0.0"
implementation "android.arch.persistence.room:runtime:$roomVersion"
annotationProcessor "android.arch.persistence.room:compiler:$roomVersion"
kapt "android.arch.persistence.room:compiler:$roomVersion"
implementation "android.arch.paging:runtime:1.0.0-alpha3"

3 个答案:

答案 0 :(得分:1)

我通过谷歌找到了这个paging example。基于此我编写了这个视图模型(参见CheeseViewModel):

class MyViewModel(app: Application) : AndroidViewModel(app) {

    val data = MyDatabase.get(app).dao.get().create(
            /* initial load position */ 0,
            PagedList.Config.Builder()
                    .setPageSize(50)
                    .setPrefetchDistance(50)
                    .build())
}

我将其添加到我的数据库类(请参阅CheeseDb):

companion object {

    // Google example noted that this might not be the best
    // solution and to use a dependency injection framework instead.

    private var instance: MyDatabase? = null

    @Synchronized
    fun get(context: Context): MyDatabase {
        return instance ?: Room.databaseBuilder(context.applicationContext,
                MyDatabase::class.java, "myDB")
                .build()
                .also { instance = it }
    }
}

这样就回答了如何在视图模型中获取DAO实例的问题。关于另一个问题,我想paging library示例不完整。

答案 1 :(得分:1)

在您的代码中,您在ViewModel类中有参数化构造函数,因为您需要在初始化ViewModel时传递参数。

以下是java中的代码

public class UserInfoViewModel extends AndroidViewModel {

    LiveData<PagedList<UserInfo>> usersForList;
    private PersonRepository personRepository;  

    public UserInfoViewModel(@NonNull Application application) {
        super(application);
        personRepository = new PersonRepository(application);
    }  

    public static class Factory extends ViewModelProvider.NewInstanceFactory {

        @NonNull
        private final Application mApplication;

        public Factory(@NonNull Application application) {
            mApplication = application;
        }

        @Override
        public <T extends ViewModel> T create(Class<T> modelClass) {
            //noinspection unchecked
            return (T) new UserInfoViewModel(mApplication);
        }
    }

    public void init() {

            usersForList = personRepository.getAllPersonsForList().create(0,
                    new PagedList.Config.Builder()
                            .setEnablePlaceholders(true)
                            .setPageSize(50)
                            .setPrefetchDistance(10)
                            .build());

    }
}

在您的活动中,您可以访问ViewModel,如下面的代码所示

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        UserInfoViewModel.Factory factory = new UserInfoViewModel.Factory(
                this.getApplication());
        userInfoViewModel = ViewModelProviders.of(this,factory)
                .get(UserInfoViewModel.class);
        userInfoViewModel.init();
    }

这是我使用DAO对象在存储库中的存储库对象,如下面的代码所示

<强> PersonRepository.java

public class PersonRepository {

    private final ChatListDAO personDAO;

    public PersonRepository(Context context) {
        personDAO = DatabaseCreator.getChatDatabase(context).ChatDatabase();
    }
}

DAO.java类

@Dao
public interface DAO{

    @Insert(onConflict = OnConflictStrategy.REPLACE)
     long insertPerson(UserInfo person);

    @Update
     void updatePerson(UserInfo person);

    @Delete
     void deletePerson(UserInfo person);

    @Query("SELECT * FROM person")
     LiveData<List<UserInfo>> getAllPersons();

    @Query("SELECT count(*) FROM person")
     LiveData<Integer> getAllPersonsCount();


    @Query("SELECT * FROM person where number = :mobileIn")
     LiveData<UserInfo> getPersonByMobile(String mobileIn);


    @Query("SELECT * FROM person")
    public abstract LivePagedListProvider<Integer,UserInfo> getUsersForList();
}

答案 2 :(得分:-1)

你的班级

  

类MyViewModel(myDao:MyDao)

有一个带有一个参数的构造函数(MyDao类型名为myDao,例如myDao:MyDao)

当你在他的线上打电话时,

  

val viewModel = ViewModelProviders.of(this).get(MyViewModel :: class.java)

您没有传递构造函数的参数

如果您需要更多信息,请查看此链接: https://kotlinlang.org/docs/reference/classes.html#constructors