为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

江户川乱折腾 2022-07-27 20:34:49
我正在使用 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 MyApplicationpublic class MyApplication extends Application{    private MyRepositoryComponent repoComponent;    //Instantiate the component in onCreate...    public MyRepositoryComponent getMyRepositoryComponent(){        return repoComponent;    }}//Finally, in my ViewModelpublic MyViewModel extends AndroidViewModel{    @Inject    public MyRepository repo;    public MyViewModel(@NonNull MyApplication app){        repo = app.getMyRepositoryComponent().inject(this);    }}我采用这种方法是因为我可以覆盖 MyApplication 类并使用假组件进行测试(这是我的主要目标之一)。以前,我能够注入依赖项的唯一方法是在 ViewModels 中构建我的组件,这使得无法用假货替代。对于这样一个简单的应用程序,我知道我可以取消注入方法并在 MyApplication 类中保存对存储库的引用。但是,假设有更多依赖项需要担心,这是否是一种常见/良好/测试友好的方法来为 Android 中的活动和 ViewModels 注入依赖项?
查看完整描述

1 回答

?
拉莫斯之舞

TA贡献1820条经验 获得超10个赞

从EpicPandaForce 的回答和一些研究(见这篇文章)中得到启发后,我找到了一个我很满意的解决方案。


我决定从我的项目中删除 Dagger 2,因为我对它进行了过度设计。我的应用程序依赖于存储库类和现在的ViewModelProvider.Factory实现,一旦应用程序运行,这两者都是必需的。我对 Dagger 的了解已经足够让我自己满意了,所以我很乐意将它从这个特定的项目中移除并在一个Application类中创建两个依赖项。这些类如下所示:


我的应用程序类创建了我的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;

    }

}

我的ViewModel工厂,它使用存储库引用创建新ViewModel的 s。随着我添加更多Activity类和ViewModels,我将对此进行扩展:


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));

    }

}

我的AddJourneyActivity班级,它使用AddJourneyViewModel:


public class AddJourneyActivity extends AppCompatActivity {


    private static final String TAG = AddJourneyActivity.class.getSimpleName();


    private AddJourneyViewModel addJourneyViewModel;

    private EditText departureTextField;


    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_add_journey);


        JourneyStoreApplication app = (JourneyStoreApplication) getApplication();

        addJourneyViewModel = ViewModelProviders

                // Gets the ViewModelFactory instance and creates the ViewModel.

                .of(this, app.getViewModelFactory())

                .get(AddJourneyViewModel.class);


        departureTextField = findViewById(R.id.addjourney_departure_addr_txt);

    }


    //...

}

但这仍然留下了测试的问题,这是我的主要问题之一。旁注:我将所有ViewModel类都抽象化(仅使用方法),然后为我的真实应用程序和测试代码实现它们。这是因为我发现它比直接extending my ViewModels 更容易,然后尝试覆盖他们的方法并隐藏他们的状态以创建一个假版本。


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


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提供我的假货: Application


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"

    }

}

我将把这个问题再开放 24 小时,以防有人要添加有用的东西,然后我会选择这个作为答案。


查看完整回答
反对 回复 2022-07-27
  • 1 回答
  • 0 关注
  • 147 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号