You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

HiltViewModel报NoSuchMethodException:无空构造方法求助

Android Hilt ViewModel 初始化错误排查与解决

错误信息

Caused by: java.lang.NoSuchMethodException: com.example.nameful.ui.main.MovieListViewModel.<init> []
    at java.lang.Class.getConstructor0(Class.java:2332)
    at java.lang.Class.getDeclaredConstructor(Class.java:2170)
    at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.kt:202)
    ... 94 more

问题背景

开发电影列表应用时触发上述错误,提示MovieListViewModel缺少空构造方法,但该ViewModel依赖的GetMovieListUseCase无法通过空构造实例化。已尝试将Compose中获取ViewModel的viewModel()改为hiltViewModel(),问题仍未解决。


相关代码片段

MovieListViewModel.kt

package com.example.nameful.ui.main

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import androidx.paging.cachedIn
import com.example.nameful.data.package000.GetMovieListUseCase
import com.example.nameful.data.repository.MoviesRepository
import com.example.nameful.data.repository.MoviesRepositoryImpl
import com.example.nameful.model.basic.Movie
import dagger.Provides
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class MovieListViewModel @Inject constructor(
   private val getMovieListUseCase: GetMovieListUseCase
) : ViewModel() {


   var movieList: Flow<PagingData<Movie>>
   var favoriteMovieList: Flow<List<Movie>>

   init {
       movieList = loadMovies()
       favoriteMovieList = loadFavoriteMovies()
   }


   /**
    * Gets a list of popular movies
    */
   private fun loadMovies(): Flow<PagingData<Movie>> {
       var movieListFromRepo: Flow<PagingData<Movie>> = emptyFlow()
       viewModelScope.launch {
           movieListFromRepo = getMovieListUseCase.getMostPopularMovies()
               .cachedIn(viewModelScope)
       }


       return movieListFromRepo
   }

   /**
    * Gets favorite movie list
    */
   private fun loadFavoriteMovies(): Flow<List<Movie>> {
       viewModelScope.launch {
           favoriteMovieList = getMovieListUseCase.getFavoriteMovies()
       }
       return favoriteMovieList
   }

   fun reloadMovies() {
       viewModelScope.launch {
           getMovieListUseCase.reloadMovies()
       }
   }

   private var movieListScrollState = 0
   fun saveMovieListScrollState(scrollIndex: Int) {
       movieListScrollState = scrollIndex
   }
   fun getMovieListScrollState():Int {
       return movieListScrollState
   }
}

GetMovieListUseCase.kt

package com.example.nameful.data.package000

import androidx.paging.PagingData
import com.example.nameful.data.repository.MoviesRepository
import com.example.nameful.data.repository.MoviesRepositoryImpl
import com.example.nameful.model.basic.Movie
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject

class GetMovieListUseCase @Inject constructor(
   private val popularMoviesRepository: MoviesRepository
) {


   suspend fun getMostPopularMovies(): Flow<PagingData<Movie>> {
       return popularMoviesRepository.getMostPopularMovies()
   }

   suspend fun getFavoriteMovies(): Flow<List<Movie>> {
       return popularMoviesRepository.getFavoriteMovies()
   }

   suspend fun reloadMovies() {
       popularMoviesRepository.reloadMovies()
   }

}

MoviesRepositoryImpl.kt

package com.example.nameful.data.repository

import androidx.paging.ExperimentalPagingApi
import androidx.paging.PagingData
import androidx.paging.map
import com.example.nameful.data.Variables
import com.example.nameful.data.api.MovieRemoteMediator
import com.example.nameful.data.database.LocalDataSourceModule
import com.example.nameful.data.database.MovieDataSource
import com.example.nameful.data.database.NetworkDataSourceModule
import com.example.nameful.model.basic.Movie
import com.example.nameful.model.basic.Provider
import com.example.nameful.model.basic.Role
import com.example.nameful.model.basic.Trailer
import kotlinx.coroutines.flow.*
import javax.inject.Inject

class MoviesRepositoryImpl @Inject constructor(
   @LocalDataSourceModule
   private val movieLocalDataSource: MovieDataSource,
   @NetworkDataSourceModule
   private val movieNetworkDataSource: MovieDataSource,
   dataRefreshManagerImpl: DataRefreshManager
) : MoviesRepository {


   @ExperimentalPagingApi
   val moviesRemoteMediator = MovieRemoteMediator(
       movieLocalDataSource,
       movieNetworkDataSource,
       dataRefreshManagerImpl
   )


   @ExperimentalPagingApi
   override suspend fun getMostPopularMovies(): Flow<PagingData<Movie>> {

       // return the paging source
       val mostPopularMovies =
           movieLocalDataSource.getPagedMovies(moviesRemoteMediator).map { pagingData ->
               pagingData.map {
                   it.poster = Variables.imageMidUrl+ it.poster
                   it.toDomain()
               }
           }
       return mostPopularMovies
   }

   @ExperimentalPagingApi
   override suspend fun reloadMovies() {
       moviesRemoteMediator.reload()
   }

   /**
    * Only local data source keeps favorite movies
    */
   override suspend fun getFavoriteMovies(): Flow<List<Movie>> {
       return movieLocalDataSource.getFavoriteMovies()
   }

   override suspend fun getMovieById(movieId: Int): Flow<Movie?> {
       val fullMovieDataFlow = movieLocalDataSource.getMovieById(movieId)
           .distinctUntilChanged()
           .map {
               if (it == null) {
                   insertFreshMovieDataToCache(movieId)
               } else {
                   if (it.trailer == null) {
                       insertTrailerToCache(it.movie_id)
                   }
               }
               it

           }
       return fullMovieDataFlow
   }

   override suspend fun toggleFavorite(movieId: Int) {
       movieLocalDataSource.toggleFavorite(movieId)
   }

   private suspend fun insertTrailerToCache(movieId: Int) {
       val trailer: Flow<Trailer?> = movieNetworkDataSource.getTrailer(movieId)
       if (trailer.count() > 0) {
           movieLocalDataSource.insertTrailer(movieId, trailer.first())
       }
   }

   private suspend fun insertFreshMovieDataToCache(movieId:Int) {
       val fullMovieData = movieNetworkDataSource.getMovieById(movieId)
       movieLocalDataSource.insertMovie(fullMovieData)
   }


   override suspend fun getMovieCast(movieId: Int): Flow<List<Role?>> {
       // get character list from the source of truth = database
       val movieCast = movieLocalDataSource.getMovieCast(movieId).map {
           when {
               it.isEmpty() -> {
                   insertCastToCache(movieId)
                   it
               }
               else -> {
                   it
               }
           }
       }
       return movieCast
   }

   private suspend fun insertCastToCache(movieId: Int) {
       // get cast list from network data source
       val castList: List<Role?> = movieNetworkDataSource.getFreshMovieCast(movieId)
       // insert into local data source
       movieLocalDataSource.insertCast(castList)
   }

   override suspend fun getMovieProviders(movieId: Int): Flow<List<Provider?>> {
       // get providers for this movie from the source of truth = database
       val movieProviders = movieLocalDataSource.getProviders(movieId).map {
           when {
               it.isEmpty() -> {
                   insertMovieProvidersToCache(movieId)
                   it
               }
               else -> {
                   it
               }
           }
       }
       return movieProviders
   }

   private suspend fun insertMovieProvidersToCache(movieId: Int) {
       // get providers from network data source
       val providerList: List<Provider?> = movieNetworkDataSource.getFreshProviders(movieId)
       // insert into local data source
       movieLocalDataSource.insertProviders(providerList)
   }

}

核心DI模块节选

AppModule.kt

package com.example.nameful

import android.app.Application
import android.content.Context
import com.example.nameful.data.repository.MoviesRepository
import com.example.nameful.data.repository.MoviesRepositoryImpl
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
class AppModule {

    @Singleton
    @Provides
    fun provideContext(application: Application): Context = application.applicationContext

    @Singleton
    @Provides
    fun provideMoviesRepository(moviesRepositoryImpl: MoviesRepositoryImpl): MoviesRepository {
        return moviesRepositoryImpl
    }
}

DataRefreshManagerBinding.kt

package com.example.nameful.data.datarefreshmanager

import com.example.nameful.data.repository.DataRefreshManager
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
internal abstract class DataRefreshManagerBinding {

    @Singleton
    @Binds
    abstract fun bindDataRefreshManager(dataRefreshManagerIml: DataRefreshManagerImpl): DataRefreshManager
}

排查与解决方法

1. 补全Hilt核心配置

  • 必须为自定义Application添加@HiltAndroidApp注解,这是Hilt初始化的入口:
    @HiltAndroidApp
    class MovieApplication : Application() {}
    
  • 在AndroidManifest.xml中注册该Application:
    <application
        android:name=".MovieApplication"
        <!-- 其他配置项 -->
    >
    

2. 确保ViewModel获取方式正确

  • 在Compose页面中,必须使用hiltViewModel()获取Hilt注入的ViewModel,禁止使用普通viewModel():
    @Composable
    fun MovieListScreen(
        viewModel: MovieListViewModel = hiltViewModel()
    ) {
        // 页面逻辑
    }
    
  • 若在Activity/Fragment中获取,需使用Hilt兼容的ViewModelProvider:
    val viewModel = ViewModelProvider(this)[MovieListViewModel::class.java]
    

3. 验证依赖注入链路完整性

  • 检查DataRefreshManagerImpl是否添加了@Inject构造方法,否则DataRefreshManagerBinding无法完成绑定:
    class DataRefreshManagerImpl @Inject constructor() : DataRefreshManager {
        // 实现逻辑
    }
    
  • 补充MovieDataSource的Qualifier依赖提供模块,确保LocalDataSourceModule和NetworkDataSourceModule注解能正确对应实例:
    @Module
    @InstallIn(SingletonComponent::class)
    class DataSourceModule {
        @Provides
        @LocalDataSourceModule
        fun provideLocalMovieDataSource(movieDao: MovieDao): MovieDataSource {
            return LocalMovieDataSource(movieDao)
        }
    
        @Provides
        @NetworkDataSourceModule
        fun provideNetworkMovieDataSource(apiService: MoviesApiService): MovieDataSource {
            return NetworkMovieDataSource(apiService)
        }
    }
    

4. 清理编译缓存并重建项目

  • 执行Build > Clean Project和Build > Rebuild Project,清除Hilt生成代码的缓存。
  • 若问题仍存在,执行File > Invalidate Caches... > Invalidate and Restart重启Android Studio。

5. 检查依赖版本兼容性

确保Hilt、ViewModel、Compose相关依赖版本匹配,示例配置:

// Hilt核心依赖
implementation "com.google.dagger:hilt-android:2.44"
kapt "com.google.dagger:hilt-android-compiler:2.44"
implementation "androidx.hilt:hilt-navigation-compose:1.0.0"

// ViewModel依赖
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1"
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1"

内容的提问来源于stack exchange,提问作者Prido1024

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 14:32:09