Dagger 2 Android - 将()依赖项注入 ViewModel 与具有依赖项引

我正在使用 Dagger 2 创建一个基本的 Android 应用程序。在我遇到Jake Wharton 的这篇精彩演讲之前,我很难理解如何正确使用它。在其中,他演示了使用 Dagger 2 和“Tweeter”应用程序。在~22:44,他展示了应用程序的@Inject字段可以满足注入方法。他后来展示了一个简单的 Android 实现。


我的应用的 ViewModel 依赖于存储库类。我正在使用 Dagger 2 通过 Application 类将此存储库注入 ViewModel,如下所示:


//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 中构建我的组件,这使得无法用假货替代。


对于这样一个简单的应用程序,我知道我可以取消注入方法并在 MyApplication 类中保存对存储库的引用。但是,假设有更多依赖项需要担心,这是否是一种常见/良好/测试友好的方法来为 Android 中的活动和 ViewModels 注入依赖项?


江户川乱折腾
浏览 148回答 1
1回答

拉莫斯之舞

从EpicPandaForce 的回答和一些研究(见这篇文章)中得到启发后,我找到了一个我很满意的解决方案。我决定从我的项目中删除 Dagger 2,因为我对它进行了过度设计。我的应用程序依赖于存储库类和现在的ViewModelProvider.Factory实现,一旦应用程序运行,这两者都是必需的。我对 Dagger 的了解已经足够让我自己满意了,所以我很乐意将它从这个特定的项目中移除并在一个Application类中创建两个依赖项。这些类如下所示:我的应用程序类创建了我的ViewModel工厂,为它提供了存储库,getViewModelFactory()并向我的活动公开了一个方法:public class JourneyStoreApplication extends Application {&nbsp; &nbsp; private final JourneyStoreViewModelFactory journeyStoreViewModelFactory;&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; // Instantiate my viewmodel factory with my repo here&nbsp; &nbsp; &nbsp; &nbsp; final JourneyRepository journeyRepository = new JourneyRepositoryImpl();&nbsp; &nbsp; &nbsp; &nbsp; journeyStoreViewModelFactory = new JourneyStoreViewModelFactory(journeyRepository);&nbsp; &nbsp; }&nbsp; &nbsp; @Override&nbsp; &nbsp; public void onCreate() {&nbsp; &nbsp; &nbsp; &nbsp; super.onCreate();&nbsp; &nbsp; }&nbsp; &nbsp; public JourneyStoreViewModelFactory getViewModelFactory(){&nbsp; &nbsp; &nbsp; &nbsp; return journeyStoreViewModelFactory;&nbsp; &nbsp; }}我的ViewModel工厂,它使用存储库引用创建新ViewModel的 s。随着我添加更多Activity类和ViewModels,我将对此进行扩展:public class JourneyStoreViewModelFactory implements ViewModelProvider.Factory {&nbsp; &nbsp; private final JourneyRepository journeyRepository;&nbsp; &nbsp; JourneyStoreViewModelFactory(JourneyRepository journeyRepository){&nbsp; &nbsp; &nbsp; &nbsp; this.journeyRepository = journeyRepository;&nbsp; &nbsp; }&nbsp; &nbsp; @NonNull&nbsp; &nbsp; @Override&nbsp; &nbsp; public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {&nbsp; &nbsp; &nbsp; &nbsp; if(modelClass == AddJourneyViewModel.class){&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Instantiates the ViewModels with their repository reference.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return (T) new AddJourneyViewModelImpl(journeyRepository);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; throw new IllegalArgumentException(String.format("Requested class %s did not match expected class %s.", modelClass, AddJourneyViewModel.class));&nbsp; &nbsp; }}我的AddJourneyActivity班级,它使用AddJourneyViewModel:public class AddJourneyActivity extends AppCompatActivity {&nbsp; &nbsp; private static final String TAG = AddJourneyActivity.class.getSimpleName();&nbsp; &nbsp; private AddJourneyViewModel addJourneyViewModel;&nbsp; &nbsp; private EditText departureTextField;&nbsp; &nbsp; @Override&nbsp; &nbsp; protected void onCreate(Bundle savedInstanceState) {&nbsp; &nbsp; &nbsp; &nbsp; super.onCreate(savedInstanceState);&nbsp; &nbsp; &nbsp; &nbsp; setContentView(R.layout.activity_add_journey);&nbsp; &nbsp; &nbsp; &nbsp; JourneyStoreApplication app = (JourneyStoreApplication) getApplication();&nbsp; &nbsp; &nbsp; &nbsp; addJourneyViewModel = ViewModelProviders&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Gets the ViewModelFactory instance and creates the ViewModel.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .of(this, app.getViewModelFactory())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .get(AddJourneyViewModel.class);&nbsp; &nbsp; &nbsp; &nbsp; departureTextField = findViewById(R.id.addjourney_departure_addr_txt);&nbsp; &nbsp; }&nbsp; &nbsp; //...}但这仍然留下了测试的问题,这是我的主要问题之一。旁注:我将所有ViewModel类都抽象化(仅使用方法),然后为我的真实应用程序和测试代码实现它们。这是因为我发现它比直接extending my ViewModels 更容易,然后尝试覆盖他们的方法并隐藏他们的状态以创建一个假版本。无论如何,我扩展了我的JourneyStoreApplication课程(与我知道的自己相矛盾,但它是一个小课程,所以很容易管理)并用它来创建一个提供我的假ViewModels 的地方:public class FakeJourneyStoreApplication extends JourneyStoreApplication {&nbsp; &nbsp; private final JourneyStoreViewModelFactory fakeJourneyStoreViewModelFactory;&nbsp; &nbsp; {&nbsp; &nbsp;// Create my fake instances here for my tests&nbsp; &nbsp; &nbsp; &nbsp; final JourneyRepository fakeJourneyRepository = new FakeJourneyRepositoryImpl();&nbsp; &nbsp; &nbsp; &nbsp; fakeJourneyStoreViewModelFactory = new FakeJourneyStoreViewModelFactory(fakeJourneyRepository);&nbsp; &nbsp; }&nbsp; &nbsp; @Override&nbsp; &nbsp; public void onCreate() {&nbsp; &nbsp; &nbsp; &nbsp; super.onCreate();&nbsp; &nbsp; }&nbsp; &nbsp; public JourneyStoreViewModelFactory getViewModelFactory(){&nbsp; &nbsp; &nbsp; &nbsp; return fakeJourneyStoreViewModelFactory;&nbsp; &nbsp; }}我制作了我ViewModel的 s 的假实现,并从FakeJourneyStoreViewModelFactory. 稍后我可能会简化这一点,因为“假”样板可能比需要的要多。离开本指南(第 4.9 节),我扩展为我的测试AndroidJUnitRunner提供我的假货: Applicationpublic class CustomTestRunner extends AndroidJUnitRunner {&nbsp; &nbsp; @Override&nbsp; &nbsp; public Application newApplication(ClassLoader cl, String className, Context context)&nbsp; &nbsp; throws ClassNotFoundException, IllegalAccessException, InstantiationException {&nbsp; &nbsp; &nbsp; &nbsp; return super.newApplication(cl, FakeJourneyStoreApplication.class.getName(), context);&nbsp; &nbsp; }}最后,我将自定义测试运行器添加到我的build.gradle文件中:android {&nbsp; &nbsp; defaultConfig {&nbsp; &nbsp; &nbsp; &nbsp; // Espresso&nbsp; &nbsp; &nbsp; &nbsp; testInstrumentationRunner "com.<my_package>.journeystore.CustomTestRunner"&nbsp; &nbsp; }}我将把这个问题再开放 24 小时,以防有人要添加有用的东西,然后我会选择这个作为答案。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java