Android Hilt问题:ViewModel依赖注入报错,提示无无参构造方法
Hilt ViewModel注入崩溃问题:找不到无参构造方法
问题描述
严格按照Hilt ViewModel文档实现Android基础依赖注入,但应用启动直接崩溃,报错提示找不到ViewModel的无参构造方法,尽管已经通过构造方法注入了HealthConnectClient依赖。
错误栈追踪
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.myHealthConnect.myweightaverage/com.myHealthConnect.myweightaverage.activity.MainActivity}: java.lang.RuntimeException: Cannot create an instance of class com.myHealthConnect.myweightaverage.viewmodel.MainActivityViewModel at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4169) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4325) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:101) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2574) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loopOnce(Looper.java:226) at android.os.Looper.loop(Looper.java:313) at android.app.ActivityThread.main(ActivityThread.java:8757) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067) Caused by: java.lang.RuntimeException: Cannot create an instance of class com.myHealthConnect.myweightaverage.viewmodel.MainActivityViewModel at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.kt:204) at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:324) at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:306) at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:280) at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.kt:128) at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:187) at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:153) at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:53) at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:35) at com.myHealthConnect.myweightaverage.activity.MainActivity.getViewModel(MainActivity.kt:21) at com.myHealthConnect.myweightaverage.activity.MainActivity.onCreate(MainActivity.kt:29) at android.app.Activity.performCreate(Activity.java:8591) at android.app.Activity.performCreate(Activity.java:8570) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1384) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4150) ... 12 more Caused by: java.lang.NoSuchMethodException: com.myHealthConnect.myweightaverage.viewmodel.MainActivityViewModel.<init> [] at java.lang.Class.getConstructor0(Class.java:2363) at java.lang.Class.getDeclaredConstructor(Class.java:2201) at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.kt:202) ... 26 more
相关代码文件
项目级build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { id 'com.android.application' version '7.3.0' apply false id 'com.android.library' version '7.3.0' apply false id 'org.jetbrains.kotlin.android' version '1.8.0' apply false id 'com.google.dagger.hilt.android' version '2.45' apply false }
应用级build.gradle
plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' id 'kotlin-kapt' id("com.google.dagger.hilt.android") version "2.45" apply false } android { namespace 'com.myHealthConnect.myweightaverage' compileSdk 33 defaultConfig { applicationId "com.myHealthConnect.myweightaverage" minSdk 26 targetSdk 33 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' } dataBinding { enabled = true } } dependencies { 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' implementation 'androidx.health.connect:connect-client:1.0.0-alpha11' implementation "androidx.activity:activity-ktx:1.7.0" testImplementation 'junit:junit:4.13.2' implementation("com.google.dagger:hilt-android:2.45") kapt("com.google.dagger:hilt-android-compiler:2.45") androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' } // Allow references to generated code kapt { correctErrorTypes = true javacOptions { option("-Adagger.hilt.android.internal.disableAndroidSuperclassValidation=true") } }
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <!-- Check whether Health Connect is installed or not --> <queries> <package android:name="com.google.android.apps.healthdata" /> </queries> <uses-permission android:name="android.permission.health.READ_WEIGHT"/> <application android:name=".application.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.MyWeightAverage" android:appComponentFactory="androidx.core.app.CoreComponentFactory" tools:replace="android:appComponentFactory" tools:targetApi="31"> <activity android:name=".activity.MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> <action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" /> </intent-filter> <meta-data android:name="android.app.lib_name" android:value=""/> </activity> </application> </manifest>
MyApplication.kt
import android.app.Application import dagger.hilt.android.HiltAndroidApp @HiltAndroidApp class MyApplication : Application()
AppModule.kt
import android.content.Context import androidx.health.connect.client.HealthConnectClient import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.components.ViewModelComponent import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.scopes.ViewModelScoped import dagger.hilt.components.SingletonComponent import javax.inject.Singleton @Module @InstallIn(ViewModelComponent::class) object AppModule { @Provides @ViewModelScoped fun provideHealthConnectClient(@ApplicationContext appContext: Context) = if (HealthConnectClient.isProviderAvailable(appContext)) { HealthConnectClient.getOrCreate(appContext) } else { throw NullPointerException() } }
MainActivity.kt
import android.os.Bundle import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil import com.myHealthConnect.myweightaverage.R import com.myHealthConnect.myweightaverage.databinding.ActivityMainBinding import com.myHealthConnect.myweightaverage.viewmodel.MainActivityViewModel import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class MainActivity : AppCompatActivity() { private val viewModel: MainActivityViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main) binding.viewModel = viewModel binding.lifecycleOwner = this } }
MainActivityViewModel.kt
import androidx.health.connect.client.HealthConnectClient import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject @HiltViewModel class MainActivityViewModel @Inject constructor( val healthConnectClient: HealthConnectClient ) : ViewModel() { init { } }
解决方案
1. 修复Hilt插件配置(核心问题)
错误根源是应用级build.gradle中Hilt插件被设置为apply false,导致Hilt无法生成ViewModel注入所需的代码,系统只能尝试用默认的NewInstanceFactory创建ViewModel,而你的ViewModel没有无参构造方法,因此崩溃。
修改应用级build.gradle的plugins块:
plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' id 'kotlin-kapt' // 移除apply false,让插件在当前模块生效 id("com.google.dagger.hilt.android") version "2.45" }
2. 修复MainActivity的布局重复设置问题
MainActivity中调用了两次setContentView,会导致布局重复初始化,保留DataBinding的初始化代码即可:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 移除重复的setContentView调用 // setContentView(R.layout.activity_main) val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main) binding.viewModel = viewModel binding.lifecycleOwner = this }
3. 可选:优化AppModule的异常处理
当前provideHealthConnectClient在Health Connect不可用时抛出NullPointerException,建议换成更明确的自定义异常或者返回null(配合@Nullable注解),方便上层处理:
@Provides @ViewModelScoped fun provideHealthConnectClient(@ApplicationContext appContext: Context): HealthConnectClient? = if (HealthConnectClient.isProviderAvailable(appContext)) { HealthConnectClient.getOrCreate(appContext) } else { null }
修改完成后,执行Clean Project -> Rebuild Project,再运行应用即可解决崩溃问题。
内容的提问来源于stack exchange,提问作者Harry Angus
相关产品推荐
相关产品推荐

