Я новичок в создании приложений для 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
HiltViewModel: NoSuchMethodException в классе, аннотированном @HiltViewModel ⇐ Android
-
- Похожие темы
- Ответы
- Просмотры
- Последнее сообщение
-
-
Исправление NoSuchMethodException с помощью Kotlin Reflection в частных функциях
Anonymous » » в форуме JAVA - 0 Ответы
- 26 Просмотры
-
Последнее сообщение 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
-