Android: Obtenga un DAO en un ViewModel

¿Cuál es la mejor manera de get un DAO de Android Room en un ViewModel?

Basado en el ejemplo de la biblioteca de pagination , escribí este ViewModel:

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

Luego trato de get una instancia

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

Al tratar de ejecutar esto, recibo una exception:

 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 

En el ejemplo de la biblioteca de pagination no es evidente cómo el model de vista obtiene una copy del DAO, y aparentemente falla. La pregunta es si me he perdido algo o el ejemplo está incompleto.

Googleando la exception, encuentro sugerencias para usar ViewModelProvider.Factory, solo el ejemplo no usó esto. En el código de ejemplo, el model de vista se ve así:

 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()); } } 

Y se recupera así

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

Mis dependencies

 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" 

Encontré este ejemplo de pagination por google. Basado en eso escribí este model de vista (vea 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()) } 

Y agregué esto a mi class DB (ver 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 } } } 

Entonces, eso responde la pregunta sobre cómo get una instancia de DAO en un model de vista. En cuanto a la otra pregunta, supongo que el ejemplo de la biblioteca de pagination estaba incompleto.

Tu class

class MyViewModel (myDao: MyDao)

tiene un constructor con un parámetro (tipo MyDao con el nombre myDao, p. ej. MyDao: MyDao)

cuando lo llamas en su línea,

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

no está pasando un parámetro para el constructor

Consulte este enlace si necesita más información: https://kotlinlang.org/docs/reference/classes.html#constructors

En su código, tiene un constructor parametrizado en su class ViewModel para el que necesita pasar el parámetro al inicializar ViewModel.

A continuación está el código en 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()); } } 

En su actividad, puede acceder al ViewModel como se muestra en el siguiente código

  @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(); } 

Aquí está mi object de repository en repository usando el object DAO como se muestra en el siguiente código

PersonRepository.java

 public class PersonRepository { private final ChatListDAO personDAO; public PersonRepository(Context context) { personDAO = DatabaseCreator.getChatDatabase(context).ChatDatabase(); } } 

Clase 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(); } 
  • ¿Por qué son válidos tanto val box1 como val box2 en Kotlin?
  • Dagger 2 no genera la class de componente (Android, Kotlin)
  • ¿Cómo hacer reference al constructor de un Kotlin sellado?
  • ¿Es posible el cruce de intersecciones en Kotlin?
  • ¿Cuál es el propósito de la palabra key `externa` en Kotlin?
  • RxJava usando Kotlin - cómo sincronizar 2 methods asíncronos, refactorizar desde Java
  • Cuadro de dialog de finalización de Kotlin Ventana emergente después de escribir "object".
  • Referencia no resuelta: Kotlin necesita 2 versiones después de limpiar para recoger el código cuando usa kapt
  • java.lang.IllegalStateException ¿No está seguro de que sea con el tipo de datos?
  • ¿Tiene Kotlin una function de "enumerar" como Python?
  • Cuándo es posible omitir el tipo de devolución en Kotlin