Android: 在 ViewModel 中获取一个 DAO
Android: Get a DAO in a ViewModel
在 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 的副本,显然它失败了。问题是我是否遗漏了什么,或者示例不完整?
谷歌搜索异常我发现建议使用 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"
我找到这个 paging example by google. Based on that I wrote this view model (see 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())
}
然后我将其添加到我的数据库 class(参见 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 示例不完整。
在您的代码中,您的 ViewModel class 中有参数化构造函数,因此您需要在初始化 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());
}
}
在您的 Activity 中,您可以访问 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 class
@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();
}
在 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 的副本,显然它失败了。问题是我是否遗漏了什么,或者示例不完整?
谷歌搜索异常我发现建议使用 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"
我找到这个 paging example by google. Based on that I wrote this view model (see 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())
}
然后我将其添加到我的数据库 class(参见 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 示例不完整。
在您的代码中,您的 ViewModel class 中有参数化构造函数,因此您需要在初始化 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());
}
}
在您的 Activity 中,您可以访问 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 class
@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();
}