如何使用Dagger2注入AndroidViewModel?

5

我目前正在研究在我的Android应用中使用Dagger2

implementation 'com.google.dagger:dagger:2.21'
annotationProcessor 'com.google.dagger:dagger-compiler:2.21'

implementation 'com.google.dagger:dagger-android:2.21'
implementation 'com.google.dagger:dagger-android-support:2.21'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.21'

我已经成功地使用以下代码@Inject ViewModels及其相关的repositories:

import java.util.Map;

import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;

import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;

@Singleton
public class DaggerViewModelFactory implements ViewModelProvider.Factory {

    private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;

    @Inject
    public DaggerViewModelFactory(final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
        this.creators = creators;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends ViewModel> T create(final Class<T> modelClass) {
        Provider<? extends ViewModel> creator = creators.get(modelClass);

        if (creator == null) {
            for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : creators.entrySet()) {
                if (modelClass.isAssignableFrom(entry.getKey())) {
                    creator = entry.getValue();
                    break;
                }
            }
        }

        if (creator == null) {
            throw new IllegalArgumentException("unknown model class " + modelClass);
        }

        try {
            return (T) creator.get();
        } catch (final Exception e) {
            throw new RuntimeException(e);
        }
    }
}

import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoMap;

@Module
public abstract class ViewModelModule {

    @Binds
    abstract ViewModelProvider.Factory bindViewModelFactory(final DaggerViewModelFactory factory);

    @Binds
    @IntoMap
    @ViewModelKey(StilettoViewModel.class)
    abstract ViewModel provideStilettoViewModel(final StilettoViewModel stilettoViewModel);
}

然而,我的部分ViewModel是androidx.lifecycle.AndroidViewModel

我创建了这个ViewModelFactory:

import android.app.Application;

import java.util.Map;

import javax.inject.Provider;
import javax.inject.Singleton;

import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;

@Singleton
public class DaggerAndroidViewModelFactory extends ViewModelProvider.AndroidViewModelFactory {

    private final Application application;

    private Map<Class<? extends AndroidViewModel>, Provider<AndroidViewModel>> creators;


    /**
     * Creates a {@code AndroidViewModelFactory}
     *
     * @param application an application to pass in {@link AndroidViewModel}
     */
    public DaggerAndroidViewModelFactory(@NonNull final Application application) {
        super(application);
        this.application = application;
    }


    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull final Class<T> modelClass) {
        return super.create(modelClass);
    }
}

还有这个 Dagger 模块

import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.ViewModelProvider;
import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoMap;

@Module
public abstract class ViewModelModule {

    @Binds
    abstract ViewModelProvider.AndroidViewModelFactory bindAndroidViewModelFactory(final DaggerAndroidViewModelFactory factory);

    @Binds
    @IntoMap
    @ViewModelKey(MyAndroidViewModel.class)
    abstract AndroidViewModel provideArticlesViewModel(final ArticlesViewModel articlesViewModel);
}

这段代码无法编译,因为出现了以下错误

error: [Dagger/MissingBinding] MyAndroidViewModel cannot be provided without an @Inject constructor or an @Provides-annotated method.

是否可以在Dagger2中注入androidx.lifecycle.AndroidViewModel

我的实现有什么问题吗?

3个回答

7
如果你有一个 @Module 提供了 Application(或者你使用 @BindsInstance + @Component.Builder,两者都可以):
@Module
public class AppModule {
    private final Application app;

    public AppModule(Application app) {
        this.app = app;
    }

    @Provides
    Application app() { return app; } 
}

并且
@Component(modules={AppModule.class, ...})
@Singleton
public interface AppComponent {
}

现在不再使用AndroidViewModel,而是:

public class MyViewModel extends AndroidViewModel {
    public MyViewModel(Application app) {
        ...
    }
}

您可以使用带有@Inject构造函数的常规ViewModel:
public class MyViewModel extends ViewModel {
    @Inject
    MyViewModel(Application app) {
        ...
    }
}

您将能够通过您的DaggerViewModelFactory像任何其他常规的ViewModel类一样使用它。


1

对于我的代码,它对我有效:

应用程序组件的代码:

@Singleton
@Component(modules = [AppModule::class])
interface ApplicationComponent {
@Component.Factory
interface Factory {
    // With @BindsInstance, the Context passed in will be available in the 
graph
    fun create(@BindsInstance context: IGIApplication):ApplicationComponent
}

fun inject(igiApplication: IGIApplication)

fun inject(languageActivity: LanguageActivity)
fun inject(splashActivity: SplashActivity)

fun cleanComponent() : CleanComponent.Factory
fun mainComponent() : MainComponent.Factory
}

接下来是应用程序的代码:

@Singleton
class IGIApplication @Inject constructor(): Application() {
val appComponent: ApplicationComponent by lazy{
    DaggerApplicationComponent.factory().create(applicationContext as 
IGIApplication)
}

init {
    app = this
}

override fun onCreate() {
    super.onCreate()
    Tracker.getInstance().startWithAppGuid(applicationContext, 
provideSettings().appGuid)
    app = this
    appComponent.inject(this)

}}

在ViewModel中,使用ViewModel()而不是AndroidViewModel():

@Singleton
class LanguageViewModel @Inject constructor(private val application: 
IGIApplication) :
ViewModel() {

val currentLanguage = MutableLiveData<String?>()

init {
    val currentLang =
        SharePreference.getStringPref(
            application.applicationContext,
            SharePreference.CURRENT_LANGUAGE
        )
    if (currentLang.isNullOrEmpty()) {
        currentLanguage.value = "en"
    } else {
        currentLanguage.value = currentLang
    }

}
}

-1

使用 Hilt 注入 ViewModel 对象。 了解更多这里

将构造函数注释为ViewModelInject,它提供了 View Model 工厂。

@AndroidEntryPoint
class HomeFragment : Fragment() {

    private val homeViewModel: HomeViewModel by viewModels()
}

视图模型类是:

class HomeViewModel @ViewModelInject constructor(
    authorizationRepository: AuthorizationRepository
) : ViewModel()

请在您的Gradle文件中添加以下额外依赖项。
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02'
kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02'

3
这个问题是关于AndroidViewModel的。如果你扩展了AndroidViewModel,你需要在ViewModel中注入Application。 - Pitos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接