ViewModel не может быть предоставлена ​​без конструктора @Inject или метода с аннотацией @ Provides - PullRequest
0 голосов
/ 04 февраля 2020

Я пытаюсь разделить одну ViewModel между двумя действиями. Я получаю следующую ошибку ViewModel cannot be provided without an @Inject constructor or an @Provides-annotated method. Основная цель, которую я пытаюсь достичь sh, - использовать общую ViewModel между различными действиями, в моем случае LoginActivity и SampleActivity. Лучший способ сделать это - использовать ViewModelFactory. Поэтому я использую кинжал для внедрения ViewModelFactory в обе активности, в настоящее время я застрял на вышеуказанной ошибке.

Вот файл 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]

Любая помощь приветствуется!

...