如果没有@Provides注释的方法,则无法提供Dagger / MissingBinding androidx.lifecycle.ViewModelProvider.Factory

时间:2019-03-26 13:48:49

标签: android kotlin viewmodel dagger-2

我正在尝试在我的项目中实现ViewModel Factory,但是在使用Dagger 2实现它时遇到了一些问题。我提供了必要的代码,您可以看一下让我知道如何提出建议。

我有所有需要提供的方法。我还要提到的是,我试图通过ViewModelFactory将参数(存储库)传递给ViewModel类,这是我得到错误的地方。

这是我正在使用的所有课程:

HomeViewModel.kt

class HomeViewModel @Inject constructor(
        private val  categoriesRepository: CategoriesRepository) : ViewModel() {

    val categories by lazyDefferd {
        categoriesRepository.getCategories()
        Log.d("All_Categories", "")
    }
}

HomeViewModelFactory.kt

@Suppress("UNCHECKED_CAST")
@Singleton
class HomeViewModelFactory @Inject constructor
    (private val viewModelsMAp: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>)
        : ViewModelProvider.Factory {

    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        val creator = viewModelsMAp[modelClass] ?: viewModelsMAp.entries.firstOrNull {
            modelClass.isAssignableFrom(it.key)
        }?.value ?: throw ViewModelNotFound()
        try {
            return creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }
}

HomeFragment.kt

class HomeFragment : Fragment() {

    @Inject
    lateinit var factory: ViewModelProvider.Factory

    private lateinit var viewModel: HomeViewModel

    companion object {
        fun newInstance() = HomeFragment()
    }


    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {

        val view = inflater.inflate(R.layout.home_fragment, container, false)

        return view
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Injector.appComponent.inject(this)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        viewModel = ViewModelProviders.of(this, factory).get(HomeViewModel::class.java)

        GlobalScope.launch {
            val categories = viewModel.categories.await()
            Log.d("Responses", "$categories")
        }

    }

匕首: AppComponent.kt

@Singleton
@Component(
        modules = [AppModule::class, NetworkModule::class]
)
interface AppComponent {
    fun inject(homeFragment: HomeFragment)
}

AppModule.kt

@Module
class AppModule(
        val application : Context) {

    @Provides
    fun providesApplication(): Context = application

    @Provides
    @Singleton
    fun provideDb(app: Application): AppDatabase {
        return Room.databaseBuilder(app, AppDatabase::class.java, "my.db")
                .build()
    }

    @Provides
    @Singleton
    fun providesCategoryDao(db: AppDatabase) : CategoryDao {
        return db.categoryDao()
    }

    @Provides
    @Singleton
    fun providesSubCategoryDao(db: AppDatabase): SubCategoryDao {
        return db.subcategoryDao()
    }

    @Provides
    fun provideCategoryRepository(db: AppDatabase, apiService: ApiService) : CategoriesRepository {
        return CategoriesRepository(db.categoryDao(), apiService)
    }
}

NetworkMOdule.kt

@Module
class NetworkModule {


    /**
     * Provide the Api Service implementation
     * @param retrofit the Retrofit object used to instantiate the service
     * @return the api service implementation
     */
    @Provides
    @Singleton
    fun providesApiService(retrofit: Retrofit): ApiService {
        return retrofit.create(ApiService::class.java)
    }


    /**
     * Provides Retrofit object
     * @return the Retrofit object
     */
    @Provides
    @Singleton
    fun providesRetrofit(): Retrofit {
        return Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addCallAdapterFactory(CoroutineCallAdapterFactory())
                .addConverterFactory(GsonConverterFactory.create())
                .build()
    }
}

ViewModelKey.kt

@MustBeDocumented
@Target(
        AnnotationTarget.FUNCTION,
        AnnotationTarget.PROPERTY_GETTER,
        AnnotationTarget.PROPERTY_SETTER
)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)

ViewModelModule.kt

@Suppress("unused")
@Module
abstract class ViewModelModule {

    @Binds
    @IntoMap
    @ViewModelKey(HomeViewModel::class)
    abstract fun bindHomeViewModel(homeViewModel: HomeViewModel): ViewModel

    @Binds
    abstract fun bindHomeViewModelFactory(homeViewModelFactory: HomeViewModelFactory): ViewModelProvider.Factory
}

Injector.kt

class Injector {

    companion object {

        lateinit var appComponent: AppComponent

        fun  initAppComponent(appModule: AppModule, networkModule: NetworkModule) {
            appComponent = DaggerAppComponent.builder()
                    .appModule(appModule)
                    .networkModule(networkModule)
                    .build()
        }
    }
}

编译后,我得到:

C:\Users\vvasilev\Documents\Projects\myproject-android\app\build\tmp\kapt3\stubs\debug\com\myproject\vascovasilev\myproject\dagger\components\AppComponent.java:10: error: [Dagger/MissingBinding] androidx.lifecycle.ViewModelProvider.Factory cannot be provided without an @Provides-annotated method.
    public abstract void inject(@org.jetbrains.annotations.NotNull()
                         ^
      androidx.lifecycle.ViewModelProvider.Factory is injected at
          com.myproject.vascovasilev.myproject.ui.fragments.home.HomeFragment.factory
      com.myproject.vascovasilev.myproject.ui.fragments.home.HomeFragment is injected at
          com.myproject.vascovasilev.myproject.dagger.components.AppComponent.inject(com.myproject.vascovasilev.myproject.ui.fragments.home.HomeFragment)

1 个答案:

答案 0 :(得分:0)

  

在AppComponent中,我认为您忘记添加ViewModelModule

  @Singleton
  @Component(modules = [
    AndroidSupportInjectionModule::class,
    AppModule::class,
    NetworkModule::class
    ViewModelModule::class
])
interface AppComponent : AndroidInjector<DaggerApplication> {

    /**
     * component builder
     */
    @Component.Builder
    interface Builder {
        /**
         * get builder
         * @param application
         */
        @BindsInstance
        fun application(application: Application): Builder

        /**
         * build appcomponent
         */
        fun build(): AppComponent
    }
}