androidkotlinrepositoryviewmodelviewmodelproviders

viewModel with repository even following docs - error java.lang.RuntimeException: Cannot create an instance of class


Complete project:

https://github.com/giseletoledo/MonitorSaude/tree/master/app/src/main/java/com/oceantech/monitorsaude

Hi, I tried many solutions, the first was the official documentation developer android, but didn't work, I don't know if the problem it's because I have one repository with datasource been passed as args, my structure and dependencies. I even create an Application because the code of the docs need Application and don't explain how to generate or if exists:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:name=".MyApplication"
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MonitorSaude"
        tools:targetApi="31">
        <activity
            android:name=".MedicamentoActivity"
            android:theme="@style/Theme.MonitorSaude.NoActionBar"
            android:exported="false">
            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>
    </application>

</manifest>

//Application
import android.app.Application;

import org.jetbrains.annotations.NotNull;

public class MyApplication extends Application {
    @NotNull
    public final MedicamentoRepository medicamentoRepository;

    public MyApplication(@NotNull MedicamentoRepository medicamentoRepository) {
        this.medicamentoRepository = medicamentoRepository;
    }
}


dependencies {

    def room_version = '2.5.1'

    implementation "androidx.room:room-runtime:$room_version"
    kapt "androidx.room:room-compiler:$room_version"

    //Lifecycle components
    implementation 'androidx.activity:activity-ktx:1.7.1'
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1'
    implementation 'androidx.lifecycle:lifecycle-common-java8:2.6.1'
    implementation 'androidx.fragment:fragment-ktx:1.5.7'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    implementation 'androidx.core:core-ktx:1.10.0'


    implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
    implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'

    // Kotlin Extensions and Coroutines support for Room
    implementation "androidx.room:room-ktx:$room_version"

    implementation 'com.google.android.material:material:1.8.0'
    implementation 'androidx.core:core-ktx:1.10.0'
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.8.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
class MedicamentoRepository(private val dataSource: MedicamentoDataSource) {

//DataSource
class MedicamentoDataSource(private val medicationDao: MedicamentoDao) {


//ViewModel

class MedicamentoViewModel(private val medicamentoRepository: MedicamentoRepository, private val savedStateHandle: SavedStateHandle) : ViewModel() {

  private val _medicamentos = MutableLiveData<List<Medicamento>>()
  val medicamentos: LiveData<List<Medicamento>> = _medicamentos


  private val _datasSelecionadas = MutableLiveData<List<String>>()
  val datasSelecionadas: LiveData<List<String>> = _datasSelecionadas

  private val _horariosSelecionados = MutableLiveData<List<String>>()
  val horariosSelecionados: LiveData<List<String>> = _horariosSelecionados

  fun resetMedicamento() {
      // Resete todas as propriedades para o valor inicial
      _medicamentos.value = emptyList()

      // Chame o método para limpar as listas de datas e horários
      limparListas()
  }

  fun inserirMedicamento(medicamento: Medicamento) {
      viewModelScope.launch {
          medicamentoRepository.insert(medicamento.copy(datas = datasSelecionadas.value ?: emptyList(), horarios = horariosSelecionados.value ?: emptyList()))
      }
      limparListas()
  }

  // Limpa as listas de datas e horários
  private fun limparListas() {
      _datasSelecionadas.value = emptyList()
      _horariosSelecionados.value = emptyList()
  }

  fun onHorarioSelecionado(timePicker: String) {
      val horario = "${timePicker}"
      addHorario(horario)
  }

  fun onDataSelecionada(date: Date) {
      val dataFormatada = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()).format(date)
      addData(dataFormatada)
  }

  fun loadMedications() {
      viewModelScope.launch {
          _medicamentos.value = medicamentoRepository.getAll()
      }
  }

  private fun addData(data: String) {
      _datasSelecionadas.value = (_datasSelecionadas.value ?: emptyList()) + listOf(data)
  }

  private fun addHorario(horario: String) {
      _horariosSelecionados.value = (_horariosSelecionados.value ?: emptyList()) + listOf(horario)
  }

  suspend fun getById(id: Int): Medicamento? = medicamentoRepository.getById(id)

  fun deleteMedication(medicamento: Medicamento) {
      viewModelScope.launch {
          medicamentoRepository.delete(medicamento)
      }
  }

  // Define ViewModel factory in a companion object
  companion object {

      val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
          @Suppress("UNCHECKED_CAST")
          override fun <T : ViewModel> create(
              modelClass: Class<T>,
              extras: CreationExtras
          ): T {
              // Get the Application object from extras
              val application = checkNotNull(extras[APPLICATION_KEY])
              // Create a SavedStateHandle for this ViewModel from extras
              val savedStateHandle = extras.createSavedStateHandle()

              return MedicamentoViewModel(
                  (application as MyApplication).medicamentoRepository,
                  savedStateHandle
              ) as T
          }
      }
  }
} ``` 




Solution

  • You have incorrectly declared the MyApplication class. As a component of the Android SDK, the application is automatically created by Android. Therefore, you cannot define custom constructors here, as Android only recognizes the default constructor without arguments.

    To initialize properties, you should use the onCreate() callback method, which is called by the Android system when creating your Application class.

    You can use this part of code to define MyApplication class.

    package com.oceantech.monitorsaude;
    
    import android.app.Application;
    
    import com.oceantech.monitorsaude.database.AppDatabase;
    import com.oceantech.monitorsaude.database.dao.MedicamentoDao;
    
    public class MyApplication extends Application {
    
        private MedicamentoDao medicamentoDao;
    
        private MedicamentoDataSource medicamentoDataSource;
    
        public MedicamentoRepository medicamentoRepository;
    
        @Override
        public void onCreate() {
            super.onCreate();
            this.medicamentoDao = AppDatabase.Companion.getInstance(this).medicamentoDao();
            this.medicamentoDataSource = new MedicamentoDataSource(medicamentoDao);
            this.medicamentoRepository = new MedicamentoRepository(medicamentoDataSource);
        }
    
    }
    

    Also I would like to recommend you to consider DI (Dependency Injection) frameworks to not create dependencies inside the application class, and provide it from outside instead. You can check Dagger/Hilt for example. This will make your code more suitable for tests and more maintainable.