Java Dagger 2 Android-将()依赖项注入具有依赖项引用的ViewModels vs应用程序

Java Dagger 2 Android-将()依赖项注入具有依赖项引用的ViewModels vs应用程序,java,android,dependency-injection,dagger-2,Java,Android,Dependency Injection,Dagger 2,我正在用Dagger 2创建一个基本的Android应用程序。我一直很难理解如何正确地使用它,直到我发现。在这篇文章中,他演示了如何将匕首2与“推特”应用程序结合使用。在,他展示了应用程序的@Inject字段可以通过Inject方法得到满足。随后,他展示了一个简单的Android实现 我的应用程序的ViewModels依赖于存储库类。我使用Dagger 2通过应用程序类将此存储库注入ViewModels,如下所示: //In my Dagger 2 component @Singleton @C

我正在用Dagger 2创建一个基本的Android应用程序。我一直很难理解如何正确地使用它,直到我发现。在这篇文章中,他演示了如何将匕首2与“推特”应用程序结合使用。在,他展示了应用程序的
@Inject
字段可以通过Inject方法得到满足。随后,他展示了一个简单的Android实现

我的应用程序的ViewModels依赖于存储库类。我使用Dagger 2通过应用程序类将此存储库注入ViewModels,如下所示:

//In my Dagger 2 component
@Singleton
@Component(module = {MyRepositoryModule.class})
public interface MyRepositoryComponent{
    void inject(MyViewModel viewModel);
}

//In MyApplication
public class MyApplication extends Application{
    private MyRepositoryComponent repoComponent;

    //Instantiate the component in onCreate...

    public MyRepositoryComponent getMyRepositoryComponent(){
        return repoComponent;
    }
}

//Finally, in my ViewModel
public MyViewModel extends AndroidViewModel{
    @Inject
    public MyRepository repo;

    public MyViewModel(@NonNull MyApplication app){
        repo = app.getMyRepositoryComponent().inject(this);
    }
}
我之所以采用这种方法,是因为我可以重写MyApplication类并使用假组件进行测试(这是我的主要目标之一)。以前,我能够注入依赖项的唯一方法是在ViewModels中构建组件,这使得不可能用赝品替代

对于这样一个简单的应用程序,我知道我可以去掉inject方法,在MyApplication类中保留对存储库的引用。然而,假设有更多的依赖关系需要担心,这是否是在Android中为活动和视图模型注入依赖关系的常见/良好/测试友好的方法?

经过灵感和一些研究(请参阅),我找到了一个我满意的解决方案

我决定把匕首2从我的项目中删除,因为我设计得太过火了。我的应用程序依赖于一个存储库类,现在是一个
ViewModelProvider.Factory
实现,这两者都是应用程序运行时需要的。为了满足自己的需要,我对Dagger了解得够多了,所以我觉得把它从这个特定的项目中排除出来,在一个
应用程序
类中创建两个依赖项是很舒服的。这些类如下所示:

//In my Dagger 2 component
@Singleton
@Component(module = {MyRepositoryModule.class})
public interface MyRepositoryComponent{
    void inject(MyViewModel viewModel);
}

//In MyApplication
public class MyApplication extends Application{
    private MyRepositoryComponent repoComponent;

    //Instantiate the component in onCreate...

    public MyRepositoryComponent getMyRepositoryComponent(){
        return repoComponent;
    }
}

//Finally, in my ViewModel
public MyViewModel extends AndroidViewModel{
    @Inject
    public MyRepository repo;

    public MyViewModel(@NonNull MyApplication app){
        repo = app.getMyRepositoryComponent().inject(this);
    }
}
我的应用程序类创建了我的
ViewModel
工厂,为其提供了存储库,并向我的活动公开了
getViewModelFactory()
方法:

public class JourneyStoreApplication extends Application {

    private final JourneyStoreViewModelFactory journeyStoreViewModelFactory;

    {
        // Instantiate my viewmodel factory with my repo here
        final JourneyRepository journeyRepository = new JourneyRepositoryImpl();
        journeyStoreViewModelFactory = new JourneyStoreViewModelFactory(journeyRepository);
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    public JourneyStoreViewModelFactory getViewModelFactory(){
        return journeyStoreViewModelFactory;
    }
}
My
ViewModel
Factory,它使用存储库引用创建新的
ViewModel
s。随着我添加更多的
活动
类和
视图模型
s,我将对此进行扩展:

public class JourneyStoreViewModelFactory implements ViewModelProvider.Factory {

    private final JourneyRepository journeyRepository;

    JourneyStoreViewModelFactory(JourneyRepository journeyRepository){
        this.journeyRepository = journeyRepository;
    }

    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        if(modelClass == AddJourneyViewModel.class){
            // Instantiates the ViewModels with their repository reference.
            return (T) new AddJourneyViewModelImpl(journeyRepository);
        }
        throw new IllegalArgumentException(String.format("Requested class %s did not match expected class %s.", modelClass, AddJourneyViewModel.class));
    }
}
public class FakeJourneyStoreApplication extends JourneyStoreApplication {

    private final JourneyStoreViewModelFactory fakeJourneyStoreViewModelFactory;

    {   // Create my fake instances here for my tests
        final JourneyRepository fakeJourneyRepository = new FakeJourneyRepositoryImpl();
        fakeJourneyStoreViewModelFactory = new FakeJourneyStoreViewModelFactory(fakeJourneyRepository);
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    public JourneyStoreViewModelFactory getViewModelFactory(){
        return fakeJourneyStoreViewModelFactory;
    }
}
但这仍然留下了测试的问题,这是我的主要问题之一。 旁注:我将所有的
ViewModel
类抽象化(仅使用方法),然后为我的真实应用程序和测试代码实现它们。这是因为我发现它比直接调用我的
ViewModel
s,然后试图覆盖它们的方法并隐藏它们的状态来创建一个伪版本更容易

无论如何,我扩展了我的
JourneyStoreApplication
类(我知道这与我自己有矛盾,但它是一个很小的类,所以很容易管理),并使用它创建了一个地方来提供我的假
ViewModel
s:

public class JourneyStoreViewModelFactory implements ViewModelProvider.Factory {

    private final JourneyRepository journeyRepository;

    JourneyStoreViewModelFactory(JourneyRepository journeyRepository){
        this.journeyRepository = journeyRepository;
    }

    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        if(modelClass == AddJourneyViewModel.class){
            // Instantiates the ViewModels with their repository reference.
            return (T) new AddJourneyViewModelImpl(journeyRepository);
        }
        throw new IllegalArgumentException(String.format("Requested class %s did not match expected class %s.", modelClass, AddJourneyViewModel.class));
    }
}
public class FakeJourneyStoreApplication extends JourneyStoreApplication {

    private final JourneyStoreViewModelFactory fakeJourneyStoreViewModelFactory;

    {   // Create my fake instances here for my tests
        final JourneyRepository fakeJourneyRepository = new FakeJourneyRepositoryImpl();
        fakeJourneyStoreViewModelFactory = new FakeJourneyStoreViewModelFactory(fakeJourneyRepository);
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    public JourneyStoreViewModelFactory getViewModelFactory(){
        return fakeJourneyStoreViewModelFactory;
    }
}
我对我的
ViewModel
s进行了伪实现,并从
FakeJourneyStoreViewModelFactory
返回了它们的实例。稍后我可能会简化这个过程,因为可能有更多的“假”样板文件

开始(第4.9节),我扩展了
AndroidJUnitRunner
,为我的测试提供了假的
应用程序

public class CustomTestRunner extends AndroidJUnitRunner {
    @Override
    public Application newApplication(ClassLoader cl, String className, Context context)
    throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        return super.newApplication(cl, FakeJourneyStoreApplication.class.getName(), context);
    }
}
最后,我将自定义测试运行程序添加到我的
build.gradle
文件中:

android {
    defaultConfig {
        // Espresso
        testInstrumentationRunner "com.<my_package>.journeystore.CustomTestRunner"
    }
}
android{
默认配置{
//浓缩咖啡
TestInstrumentRunner“com..journeystore.CustomTestRunner”
}
}
如果有人有有用的东西要补充,我会把这个问题再留24小时,然后我会选择这个作为答案。

经过灵感和一些研究(见),我找到了一个我满意的解决方案

我决定把匕首2从我的项目中删除,因为我设计得太过火了。我的应用程序依赖于一个存储库类,现在是一个
ViewModelProvider.Factory
实现,这两者都是应用程序运行时需要的。为了满足自己的需要,我对Dagger了解得够多了,所以我觉得把它从这个特定的项目中排除出来,在一个
应用程序
类中创建两个依赖项是很舒服的。这些类如下所示:

//In my Dagger 2 component
@Singleton
@Component(module = {MyRepositoryModule.class})
public interface MyRepositoryComponent{
    void inject(MyViewModel viewModel);
}

//In MyApplication
public class MyApplication extends Application{
    private MyRepositoryComponent repoComponent;

    //Instantiate the component in onCreate...

    public MyRepositoryComponent getMyRepositoryComponent(){
        return repoComponent;
    }
}

//Finally, in my ViewModel
public MyViewModel extends AndroidViewModel{
    @Inject
    public MyRepository repo;

    public MyViewModel(@NonNull MyApplication app){
        repo = app.getMyRepositoryComponent().inject(this);
    }
}
我的应用程序类创建了我的
ViewModel
工厂,为其提供了存储库,并向我的活动公开了
getViewModelFactory()
方法:

public class JourneyStoreApplication extends Application {

    private final JourneyStoreViewModelFactory journeyStoreViewModelFactory;

    {
        // Instantiate my viewmodel factory with my repo here
        final JourneyRepository journeyRepository = new JourneyRepositoryImpl();
        journeyStoreViewModelFactory = new JourneyStoreViewModelFactory(journeyRepository);
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    public JourneyStoreViewModelFactory getViewModelFactory(){
        return journeyStoreViewModelFactory;
    }
}
My
ViewModel
Factory,它使用存储库引用创建新的
ViewModel
s。随着我添加更多的
活动
类和
视图模型
s,我将对此进行扩展:

public class JourneyStoreViewModelFactory implements ViewModelProvider.Factory {

    private final JourneyRepository journeyRepository;

    JourneyStoreViewModelFactory(JourneyRepository journeyRepository){
        this.journeyRepository = journeyRepository;
    }

    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        if(modelClass == AddJourneyViewModel.class){
            // Instantiates the ViewModels with their repository reference.
            return (T) new AddJourneyViewModelImpl(journeyRepository);
        }
        throw new IllegalArgumentException(String.format("Requested class %s did not match expected class %s.", modelClass, AddJourneyViewModel.class));
    }
}
public class FakeJourneyStoreApplication extends JourneyStoreApplication {

    private final JourneyStoreViewModelFactory fakeJourneyStoreViewModelFactory;

    {   // Create my fake instances here for my tests
        final JourneyRepository fakeJourneyRepository = new FakeJourneyRepositoryImpl();
        fakeJourneyStoreViewModelFactory = new FakeJourneyStoreViewModelFactory(fakeJourneyRepository);
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    public JourneyStoreViewModelFactory getViewModelFactory(){
        return fakeJourneyStoreViewModelFactory;
    }
}
但这仍然留下了测试的问题,这是我的主要问题之一。 旁注:我将所有的
ViewModel
类抽象化(仅使用方法),然后为我的真实应用程序和测试代码实现它们。这是因为我发现它比直接调用我的
ViewModel
s,然后试图覆盖它们的方法并隐藏它们的状态来创建一个伪版本更容易

无论如何,我扩展了我的
JourneyStoreApplication
类(我知道这与我自己有矛盾,但它是一个很小的类,所以很容易管理),并使用它创建了一个地方来提供我的假
ViewModel
s:

public class JourneyStoreViewModelFactory implements ViewModelProvider.Factory {

    private final JourneyRepository journeyRepository;

    JourneyStoreViewModelFactory(JourneyRepository journeyRepository){
        this.journeyRepository = journeyRepository;
    }

    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        if(modelClass == AddJourneyViewModel.class){
            // Instantiates the ViewModels with their repository reference.
            return (T) new AddJourneyViewModelImpl(journeyRepository);
        }
        throw new IllegalArgumentException(String.format("Requested class %s did not match expected class %s.", modelClass, AddJourneyViewModel.class));
    }
}
public class FakeJourneyStoreApplication extends JourneyStoreApplication {

    private final JourneyStoreViewModelFactory fakeJourneyStoreViewModelFactory;

    {   // Create my fake instances here for my tests
        final JourneyRepository fakeJourneyRepository = new FakeJourneyRepositoryImpl();
        fakeJourneyStoreViewModelFactory = new FakeJourneyStoreViewModelFactory(fakeJourneyRepository);
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    public JourneyStoreViewModelFactory getViewModelFactory(){
        return fakeJourneyStoreViewModelFactory;
    }
}
我对我的
ViewModel
s进行了伪实现,并从
FakeJourneyStoreViewModelFactory
返回了它们的实例。稍后我可能会简化这个过程,因为可能有更多的“假”样板文件

开始(第4.9节),我扩展了
AndroidJUnitRunner
,为我的测试提供了假的
应用程序

public class CustomTestRunner extends AndroidJUnitRunner {
    @Override
    public Application newApplication(ClassLoader cl, String className, Context context)
    throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        return super.newApplication(cl, FakeJourneyStoreApplication.class.getName(), context);
    }
}
最后,我将自定义测试运行程序添加到我的
build.gradle
文件中:

android {
    defaultConfig {
        // Espresso
        testInstrumentationRunner "com.<my_package>.journeystore.CustomTestRunner"
    }
}
android{
默认配置{
//浓缩咖啡
TestInstrumentRunner“com..journeystore.CustomTestRunner”
}
}

如果有人有什么有用的东西要补充,我会把这个问题再留24小时,然后我会选择这个作为答案。

你考虑过我概述的方法吗