原文:MVVM architecture, ViewModel and LiveData (Part 2)
在Google I / O期间,Google推出了包含LiveData 和ViewModel 的architecture components ,这有助于使用MVVM模式开发Android应用程序。 本文讨论这些组件如何为遵循MVVM的Android应用程序提供服务。
在本系列的第一篇文章中,我们讨论了这些组件如何为遵循MVVM的Android应用程序提供服务。 在第二篇文章中,我们将回答在依赖注入的第一篇文章结尾处提出的其中一个问题。
本文假定您具有Dagger的基本知识,因为我们将专注于在MVVM示例中设置最新的Dagger版本(版本2.11)以实现依赖注入。
如果您需要关于Dagger 2.11的基本信息,请查看Dagger用户指南。
配置Dagger 2.11 首先,让我们将Dagger 2.11依赖添加到我们的MVVM Sample。
指定Dagger版本2.11 1 2 3 4 project .ext { dagger_version = "2.11" }
1 2 annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version" compile "com.google.dagger:dagger:$project.dagger_version"
1 2 3 compile "com.google.dagger:dagger-android:$project.dagger_version" compile "com.google.dagger:dagger-android-support:$project.dagger_version" annotationProcessor "com.google.dagger:dagger-android-processor:$dagger_version"
Dagger 2.11项目设置 下图显示了本示例中的主Dagger 2.11设置。
我们主要有以下Dagger App类/接口:
AppModule 是一个Dagger模块,负责在应用程序级别提供单例服务,例如GitHubService 和ProjectViewModelFactory 。
AppComponent 负责注入AppModule 。
ViewModelSubComponent 是创建View Model实例的子组件。
MainActivityModule 和FragmentBuildersModule 是Activity和Fragment实例提供程序。
Injectable 只是可注射Fragment的标记接口。
AppInjector 是一个辅助类,用于在实现Injectable 接口时自动注入Fragments。
现在,让我们进入这个设置中每个Dagger项目的细节。
创建 View Model SubComponent 以下代码片断显示了ViewModelSubComponent 接口,该接口负责创建ViewModel实例:
1 2 3 4 5 6 7 8 9 10 @Subcomponent public interface ViewModelSubComponent { @Subcomponent .Builder interface Builder { ViewModelSubComponent build () ; } ProjectListViewModel projectListViewModel () ; ProjectViewModel projectViewModel () ; }
请注意,ViewModelSubComponent 将被ProjectViewModelFactory 调用以获取ViewModel实例。
但什么是_ProjectViewModelFactory_?
下一节回答这个问题。
创建自定义View Model Factory ProjectViewModelFactory 是一个扩展ViewModelProvider.Factory 以便将ViewModel实例提供给使用者Fragment类的工厂。
以下代码片段显示了ProjectViewModelFactory ,它是一个扩展ViewModelProvider.Factory 的自解释Factory实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @Singleton public class ProjectViewModelFactory implements ViewModelProvider .Factory { private final ArrayMap<Class, Callable<? extends ViewModel >> creators; @Inject public ProjectViewModelFactory (ViewModelSubComponent viewModelSubComponent) { creators = new ArrayMap <>(); creators.put(ProjectViewModel.class, () -> viewModelSubComponent.projectViewModel()); creators.put(ProjectListViewModel.class, () -> viewModelSubComponent.projectListViewModel()); } @Override public <T extends ViewModel > T create (Class<T> modelClass) { Callable<? extends ViewModel > creator = creators.get(modelClass); if (creator == null ) { for (Map.Entry<Class, Callable<? extends 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.call(); } catch (Exception e) { throw new RuntimeException (e); } } }
现在,让我们看下一节中的主要应用程序模块。
创建App模块 AppModule 是一个Dagger模块,负责在应用程序级别为消费者提供单例服务,例如GitHubService 和ProjectViewModelFactory 。 以下代码片段显示了AppModule 类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Module(subcomponents = ViewModelSubComponent.class) class AppModule { @Singleton @Provides GitHubService provideGithubService () { return new Retrofit .Builder() .baseUrl(GitHubService.HTTPS_API_GITHUB_URL) .addConverterFactory(GsonConverterFactory.create()) .build() .create(GitHubService.class); } @Singleton @Provides ViewModelProvider.Factory provideViewModelFactory ( ViewModelSubComponent.Builder viewModelSubComponent) { return new ProjectViewModelFactory (viewModelSubComponent.build()); } }
这里需要注意的一点是,不要忘记通过在**@Module注解的 subcomponents参数中指定 ViewModelSubComponent到 AppModule**。
创建 Injectable和 AppInjector 可注入接口只是一个普通的空白标记接口,如下所示:
1 2 public interface Injectable {}
Injectable 将由可注射的Fragment实施。
为了在实现Injectable 接口时自动注入片段,将创建以下AppInjector 助手类,以在**onFragmentCreated()**上注入片段实例,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public class AppInjector { private AppInjector () {} public static void init (MVVMApplication mvvmApplication) { DaggerAppComponent.builder().application(mvvmApplication) .build().inject(mvvmApplication); mvvmApplication .registerActivityLifecycleCallbacks(new Application .ActivityLifecycleCallbacks() { @Override public void onActivityCreated (Activity activity, Bundle savedInstanceState) { handleActivity(activity); } }); } private static void handleActivity (Activity activity) { if (activity instanceof HasSupportFragmentInjector) { AndroidInjection.inject(activity); } if (activity instanceof FragmentActivity) { ((FragmentActivity) activity).getSupportFragmentManager() .registerFragmentLifecycleCallbacks( new FragmentManager .FragmentLifecycleCallbacks() { @Override public void onFragmentCreated (FragmentManager fm, Fragment fragment, Bundle savedInstanceState) { if (fragment instanceof Injectable) { AndroidSupportInjection.inject(fragment); } } }, true ); } } }
有一点需要注意,**AppInjector.init()**将在应用程序启动时调用(正如我们将在自定义应用程序类部分中展示的那样)。
创建 Activity 和 Fragment Modules 以下代码片段显示了Fragments Dagger模块:
1 2 3 4 5 6 7 8 @Module public abstract class FragmentBuildersModule { @ContributesAndroidInjector abstract ProjectFragment contributeProjectFragment () ; @ContributesAndroidInjector abstract ProjectListFragment contributeProjectListFragment () ; }
从Dagger 2.10开始,@ContributesAndroidInjector 轻松将活动和片段附加到匕首图上。 以下代码片断显示了MainActivityModule :
1 2 3 4 5 @Module public abstract class MainActivityModule { @ContributesAndroidInjector(modules = FragmentBuildersModule.class) abstract MainActivity contributeMainActivity () ; }
现在,我们来看看Dagger 2.11设置中的最后一项,即AppComponent 。
创建AppComponent 下一个代码片段显示了AppComponent 接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Singleton @Component(modules = { AndroidInjectionModule.class, AppModule.class, MainActivityModule.class}) public interface AppComponent { @Component .Builder interface Builder { @BindsInstance Builder application (Application application) ; AppComponent build () ; } void inject (MVVMApplication mvvmApplication) ; }
有一件重要的事情要注意,除了包括AppModule 和MainActivityModule 之外,我们还根据官方文档向AppComponent 添加了AndroidSupportInjectionModule ,该文档声明有必要确保所有必要的绑定都可用。 AndroidSupportInjectionModule 是dagger-android中的一个内置模块:
https://github.com/google/dagger/blob/master/java/dagger/android/support/AndroidSupportInjectionModule.java
更新存储库层实现 现在,我们完成了设置Dagger 2.11,让我们更新我们现有的应用程序代码,以便利用Dagger依赖注入。
ProjectRepository 不再需要手动创建GitHubService 服务实例,它所需要做的就是在它的GitHubService 实例的构造函数中使用**@Inject**,如下所示:
1 2 3 4 5 6 7 8 9 10 11 @Singleton public class ProjectRepository { private GitHubService gitHubService; @Inject public ProjectRepository (GitHubService gitHubService) { this .gitHubService = gitHubService; } }
更新ViewModel层实现 更新ViewModel图层也是必要的,以避免在此图层内手动从ProjectRepository 创建实例。
以下代码片段显示了ProjectViewModel 的一个示例,该示例使用@inject注释来注入Application 和ProjectRepository 实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class ProjectViewModel extends AndroidViewModel { private static final String TAG = ProjectViewModel.class.getName(); private static final MutableLiveData ABSENT = new MutableLiveData (); { ABSENT.setValue(null ); } private final LiveData<Project> projectObservable; private final MutableLiveData<String> projectID; public ObservableField<Project> project = new ObservableField <>(); @Inject public ProjectViewModel (@NonNull ProjectRepository projectRepository, @NonNull Application application) { super (application); this .projectID = new MutableLiveData <>(); projectObservable = Transformations.switchMap(projectID, input -> { if (input.isEmpty()) { return ABSENT; } return projectRepository.getProjectDetails("Google" , projectID.getValue()); }); } }
更新视图实现(Fragments和主要的Activity) 更新视图层也是必要的,以避免在该图层内手动创建ViewModel类的实例。
以下代码片段显示了ProjectFragment 的一个示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class ProjectFragment extends LifecycleFragment implements Injectable { @Inject ViewModelProvider.Factory viewModelFactory; @Override public void onActivityCreated (@Nullable Bundle savedInstanceState) { super .onActivityCreated(savedInstanceState); final ProjectViewModel viewModel = ViewModelProviders.of(this , viewModelFactory) .get(ProjectViewModel.class); } }
这里需要注意的一些重点:
现在每个Fragment都必须实现可注入接口。
Fragment应该引用ViewModelProvider.Factory 以获取ViewModel实例。
创建定制 Application类 最后,我们的自定义application类代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class MVVMApplication extends Application implements HasActivityInjector { @Inject DispatchingAndroidInjector<Activity> dispatchingAndroidInjector; @Override public void onCreate () { super .onCreate(); AppInjector.init(this ); } @Override public DispatchingAndroidInjector<Activity> activityInjector () { return dispatchingAndroidInjector; } }
这里需要注意两点:
Application必须实现HasActivityInjector ,并**@Inject DispatchingAndroidInjector** 才能从**activityInjector()**方法返回。
在Application类的onCreate()中,我们初始化 AppInjector 以便在实现Injectable 接口时自动注入Fragments。
源代码 在GitHub中检查更新的应用程序的源代码,随意随意分叉和更新,如你所愿:
https://github.com/hazems/mvvm-sample-app/tree/part2
下一步是什么 在本文后,您现在有足够的信息来使用Google Architectural components创建自己的MVVM应用程序。 希望我能为本系列的下一篇文章留有余地,内容包括以下主题:
错误处理。
在此演示中添加更多功能,以了解Room如何促进SQLite数据操作,以及如何实现有效的缓存。
单元测试。
我们在哪里可以将Rx用于此架构?
我们如何使用Kotlin简化这个实现? (旅程真的有很多有趣的东西 : ))。
如果你喜欢这篇文章,我感谢你的支持与他人分享这篇文章,让伟大的Android社区知道。