HiltViewModel: NoSuchMethodException в классе, аннотированном @HiltViewModelAndroid

Форум для тех, кто программирует под Android
Ответить Пред. темаСлед. тема
Anonymous
 HiltViewModel: NoSuchMethodException в классе, аннотированном @HiltViewModel

Сообщение Anonymous »

Я новичок в создании приложений для Android. Попробовал запустить простое приложение для просмотра списка фильмов. Когда я запустил его, я получил эту ошибку.

Caused by: java.lang.NoSuchMethodException: com.example.nameful.ui.main.MovieListViewModel. []
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.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
>
var favoriteMovieList: Flow

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

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

return movieListFromRepo
}

/**
* Gets favorite movie list
*/
private fun loadFavoriteMovies(): Flow {
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
}
}

Я немного поискал информацию об этой ошибке, и возможно, эта ошибка вызвана тем, что в MovieListViewModel.kt нет пустого конструктора. Я пытался сделать это, но решения были невозможны, потому что пустой конструктор запросит GetMovieListUseCase внутри this() или super(), что сделает то же самое с MoviesRepository или его реализацией MoviesRepositoryImpl, для которого пустой конструктор невозможен, потому что MovieRepository — это интерфейс, а MoviesRepositoryImpl не имеет статического метода.
Это 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
> {
return popularMoviesRepository.getMostPopularMovies()
}

suspend fun getFavoriteMovies(): Flow {
return popularMoviesRepository.getFavoriteMovies()
}

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

}

MoviesRepository.kt
package com.example.nameful.data.repository

import androidx.paging.PagingData
import com.example.nameful.model.basic.Movie
import com.example.nameful.model.basic.Provider
import com.example.nameful.model.basic.Role
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject

interface MoviesRepository {
suspend fun getMostPopularMovies(): Flow
>
suspend fun getFavoriteMovies(): Flow
suspend fun getMovieById(movieId: Int): Flow
suspend fun toggleFavorite(movieId: Int)
suspend fun reloadMovies()
suspend fun getMovieCast(movieId: Int): Flow
suspend fun getMovieProviders(movieId: Int): Flow
}


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
> {

// 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 {
return movieLocalDataSource.getFavoriteMovies()
}

override suspend fun getMovieById(movieId: Int): Flow {
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 = 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 {
// 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 = movieNetworkDataSource.getFreshMovieCast(movieId)
// insert into local data source
movieLocalDataSource.insertCast(castList)
}

override suspend fun getMovieProviders(movieId: Int): Flow {
// 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 = movieNetworkDataSource.getFreshProviders(movieId)
// insert into local data source
movieLocalDataSource.insertProviders(providerList)
}

}

Я также читал, что это как-то связано с классом, аннотированным @AndroidEntryPoint, в моем случае это был MainActivity.kt.
package com.example.nameful

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material.ExperimentalMaterialApi
import androidx.lifecycle.ViewModel
import com.example.nameful.ui.MovieApp
import com.example.nameful.ui.main.MovieListViewModel
import com.example.nameful.ui.theme.PopularMoviesTheme
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class MainActivity : ComponentActivity() {

@ExperimentalMaterialApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
PopularMoviesTheme {
MovieApp()
}
}
}

}

Я пытался изменить этот класс на основе инструкций о том, что init не вызывается при внедрении viewModel с использованием Hilt, но не удалось.
Я попробовал много предложений , но такие вещи, как изменение viewModel() на hiltViewModel(), ничего не изменили.
РЕДАКТИРОВАТЬ: вот модули 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
}
}

ApiModule.kt
package com.example.nameful.data.api

import com.google.gson.GsonBuilder
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import javax.inject.Singleton

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

@Provides
@Singleton
fun provideApi(builder: Retrofit.Builder): MoviesApiService {
return builder
.build()
.create(MoviesApiService::class.java)
}

@Provides
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit.Builder {
return Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
.baseUrl("https://api.themoviedb.org/3/")
.client(okHttpClient)
}

@Provides
fun provideOkHttpClient(
authenticationInterceptor: AuthenticationInterceptor
): OkHttpClient {
val loggingInterceptor: HttpLoggingInterceptor =
HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
return OkHttpClient.Builder()
.addInterceptor(authenticationInterceptor)
.addInterceptor(loggingInterceptor)
.build()
}
}

DatabaseModule.kt
package com.example.nameful.data.database

import android.content.Context
import androidx.room.Room
import com.example.nameful.data.dao.CastDao
import com.example.nameful.data.dao.MovieDao
import com.example.nameful.data.dao.ProviderDao
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

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

@Singleton
@Provides
fun provideDatabase(@ApplicationContext appContext: Context): MovieDatabase {
return Room.databaseBuilder(appContext, MovieDatabase::class.java, "movies").build()
}

@Singleton
@Provides
fun provideMovieDao(movieDatabase: MovieDatabase): MovieDao {
return movieDatabase.movieDao()
}

@Singleton
@Provides
fun provideCastDao(movieDatabase: MovieDatabase): CastDao {
return movieDatabase.castDao()
}

@Singleton
@Provides
fun provideMovieProviderDao(movieDatabase: MovieDatabase): ProviderDao {
return movieDatabase.providerDao()
}
}

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
}

MoviesRepositoryBinding.kt
package com.example.nameful.data.repository

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 DependenciesBindings {

@Singleton
@Binds
abstract fun bindRepository(moviesRepositoryImpl: MoviesRepositoryImpl): MoviesRepository
}


Подробнее здесь: https://stackoverflow.com/questions/761 ... tviewmodel
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение
  • Исправление NoSuchMethodException с помощью Kotlin Reflection в частных функциях
    Anonymous » » в форуме JAVA
    0 Ответы
    26 Просмотры
    Последнее сообщение Anonymous
  • Ошибка сборки собственного образа GrallVM «java.lang.NoSuchMethodException: sun.invoke.util.ValueConversions.booleanToİn
    Anonymous » » в форуме JAVA
    0 Ответы
    74 Просмотры
    Последнее сообщение Anonymous
  • Вызвано: java.lang.NoSuchMethodException: [класс android.app.Application] - viewmodel
    Anonymous » » в форуме Android
    0 Ответы
    23 Просмотры
    Последнее сообщение Anonymous
  • Java NoSuchMethodException с MessageDigest: java.security.MessageDigest.()
    Anonymous » » в форуме JAVA
    0 Ответы
    14 Просмотры
    Последнее сообщение Anonymous
  • NoSuchMethodException, создаваемое HiltWorker с дополнительными параметрами
    Anonymous » » в форуме Android
    0 Ответы
    9 Просмотры
    Последнее сообщение Anonymous

Вернуться в «Android»