没有@Inject构造函数或@Provides注释的方法,将无法提供ViewModel

问题描述 投票:0回答:1

我正在尝试在两个活动之间共享一个ViewModel。我收到以下错误ViewModel cannot be provided without an @Inject constructor or an @Provides-annotated method。我要实现的主要目标是在不同的活动之间使用共享的ViewModel,在我的情况下为LoginActivitySampleActivity。最好的方法是使用ViewModelFactory。因此,我使用匕首将ViewModelFactory注入到两个Activity中,当前我陷入了上述错误。

这里是build.gadle文件:

implementation("com.google.dagger:dagger:2.26")
implementation("com.google.dagger:dagger-android-support:2.26")
implementation("com.google.dagger:dagger-android:2.26")
kapt "com.google.dagger:dagger-android-processor:2.26"
kapt "com.google.dagger:dagger-compiler:2.26"

这里是ActivityModule.kt

@Module
abstract class ActivityModule {

    @ContributesAndroidInjector
    abstract fun contributeLoginActivity(): LoginActivity

    @ContributesAndroidInjector
    abstract fun contributeSampleActivity(): SampleActivity
}

这里是AppComponent.kt

@Singleton
@Component(
    modules = [
        AppModule::class,
        AndroidInjectionModule::class,
        ViewModelModule::class,
        ActivityModule::class
    ]
)
interface AppComponent {
    fun inject(app: MainApplication)

    @Component.Factory
    interface Factory {
        fun create(@BindsInstance context: Context): AppComponent
    }
}

这里是AppModule.kt

@Module(includes = [ViewModelModule::class])
class AppModule {

}

ViewModelKey.kt

@Suppress("DEPRECATED_JAVA_ANNOTATION")
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@MapKey
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)

ViewModelModule.kt

@Module
abstract class ViewModelModule {

    @Binds
    @IntoMap
    @ViewModelKey(MainViewModel::class) // PROVIDE YOUR OWN MODELS HERE
    internal abstract fun bindMainViewModel(mainViewModel: MainViewModel): ViewModel


    @Binds
    internal abstract fun bindViewModelProviderFactory(factory: ViewModelProviderFactory): ViewModelProvider.Factory
}

这里是MainApplication.kt

class MainApplication: Application(), HasAndroidInjector {

    @Inject
    lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>

    override fun onCreate() {
        super.onCreate()

        DaggerAppComponent.factory().create(this).inject(this)


    }


    override fun androidInjector() = dispatchingAndroidInjector

}

LoginActivity.kt

class LoginActivity : DaggerAppCompatActivity() {

    @Inject lateinit var modelFactory: ViewModelProvider.Factory
    lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        AndroidInjection.inject(this)
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)
        val button: Button = findViewById(R.id.button)
        button.setOnClickListener { onButtonClick() }
        val label: TextView = findViewById(R.id.counter)

        viewModel =  ViewModelProvider(this, modelFactory).get(MainViewModel::class.java)
        println("stack: ${viewModel.hashCode()}")

        viewModel.userLogin.observe(this, Observer{ user ->
            print("Debug: ${user}")
            if(user != null){
                label.text = user.user.name
                redirectToLogin()
            }
        })
    }


    fun onButtonClick(){
        var username: EditText = findViewById(R.id.username)
        var password: EditText = findViewById(R.id.password)
        println(username.text.toString())
        println(password.text.toString())
        viewModel.setUser(username.text.toString(), password.text.toString())
    }

    fun redirectToLogin(){
        println("Login done!")
        val intent = Intent(this, SampleActivity::class.java)
        intent.putExtra("extra", viewModel.userLogin.value)
        startActivity(intent)
    }

}

SampleActivity.kt

class SampleActivity : DaggerAppCompatActivity() {

    lateinit var viewModel: MainViewModel
    @Inject lateinit var modelFactory: ViewModelProvider.Factory

    override fun onCreate(savedInstanceState: Bundle?) {
        AndroidInjection.inject(this)
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sample)
        val loggedInUser: LoggedInUser? = intent.getParcelableExtra("extra")
//        println("extra: $loggedInUser")
        viewModel = ViewModelProvider(this, modelFactory).get(MainViewModel::class.java)
        println("stack: ${viewModel.hashCode()}")
        println(" $ WORKS: ${viewModel.userLogin.value?.user?.name}")


    }
}

我似乎找不到问题,这是整个错误堆栈:

public abstract interface AppComponent {
                ^
      company.MyApp.ViewModels.MainViewModel is injected at
          company.MyApp.di.ViewModelModule.bindMainViewModel$app_debug(mainViewModel)
      java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
          company.MyApp.ViewModels.ViewModelProviderFactory(creators)
      company.MyApp.ViewModels.ViewModelProviderFactory is injected at
          company.MyApp.di.ViewModelModule.bindViewModelProviderFactory$app_debug(factory)
      androidx.lifecycle.ViewModelProvider.Factory is injected at
          company.MyApp.LoginActivity.modelFactory
      company.MyApp.LoginActivity is injected at
          dagger.android.AndroidInjector.inject(T) [company.MyApp.di.AppComponent → company.MyApp.di.ActivityModule_ContributeLoginActivity.LoginActivitySubcomponent]
  The following other entry points also depend on it:
      dagger.android.AndroidInjector.inject(T) [company.MyApp.di.AppComponent → company.MyApp.di.ActivityModule_ContributeSampleActivity.SampleActivitySubcomponent]

感谢您的任何帮助!

android kotlin dagger-2
1个回答
0
投票

将ViewModel的@Provides注释方法添加到AppModule:

@Module(includes = [ViewModelModule::class])
class AppModule {

    @Provides
    @Named("mainViewModel")
    fun provideMainViewModel(): MainViewModel =
            MainViewModel()
}

如果您的ViewModel使用UseCase,则也需要在此处注入它们,例如:

@Module(includes = [ViewModelModule::class])
class AppModule {

    @Provides
    @Named("mainViewModel")
    fun provideMainViewModel(): MainViewModel =
            MainViewModel(
                    provideExampleUseCase()
            )

    @Provides
    @Named("exampleUseCase")
    fun provideExampleUseCase(): ExampleUseCase =
            ExampleUseCase()

}

这是ViewModel的外观:

class MainViewModel
@Inject constructor(
        private val exampleUseCase: ExampleUseCase,
) : ViewModel() {

    //........

}

请注意,如果您的UseCase需要注入存储库,则需要将其注入到UseCase中,就像将UseCase注入到ViewModel中一样。

© www.soinside.com 2019 - 2024. All rights reserved.