《安卓-深入浅出MVVM教程》应用篇-09 Dagger2

1,155 阅读9分钟

简介

背景

这几年 MVP 架构在安卓届非常流行,几乎已经成为主流框架,它让业务逻辑 和 UI操作相对独立,使得代码结构更清晰。

MVVM 在前端火得一塌糊涂,而在安卓这边却基本没见到几个人在用,看到介绍 MVVM 也最多是讲 DataBinding 或 介绍思想的。偶尔看到几篇提到应用的,还是对谷歌官网的Architecture Components 文章的翻译。

相信大家看别人博客或官方文档的时候,总会碰到一些坑。要么入门教程写得太复杂(无力吐槽,前面写一堆原理,各种高大上的图,然并卵,到实践部分一笔带过,你确定真的是入门教程吗)。要么就是简单得就是一个 hello world,然后就没有下文了(看了想骂人)。

实在看不下去的我,决定插手你的人生。

目录

《安卓-深入浅出MVVM教程》大致分两部分:应用篇、原理篇。采用循序渐进方式,内容深入浅出,符合人类学习规律,希望大家用最少时间掌握 MVVM。

  • 应用篇:01 Hello MVVM (快速入门)02 Repository (数据仓库)03 Cache (本地缓存)04 State Lcee (加载/空/错误/内容视图)05 Simple Data Source (简单的数据源)06 Load More (加载更多)07 DataBinding (数据与视图绑定)08 RxJava209 Dagger210 Abstract (抽象)11 Demo (例子)12-n 待定(欢迎 github 提建议)

  • 原理篇01 MyLiveData(最简单的LiveData)02-n 待定(并不是解读源码,那样太无聊了,打算带你从0撸一个 Architecture)

关于提问

本人水平和精力有限,如果有大佬发现哪里写错了或有好的建议,欢迎在本教程附带的 github仓库 提issue。What?为什么不在博客留言?考虑到国内转载基本无视版权的情况,一般来说你都不是在源出处看到这篇文章,所以留言我也一般是看不到的。

教程附带代码

https://github.com/ittianyu/MVVM

应用篇放在 app 模块下,原理篇放在 implementation 模块下。每一节代码采用不同包名,相互独立。微信文章中不能带超链接,如果想收藏一整套,请关注 github 仓库。

前言

这一节也是在 04 节的基础上开始的,可能会比较难以理解,虽然我也很想深入浅出的将,但毕竟不能在这里讲 dagger 的用法,没用过的还是不建议看。个人表示 dagger 和 spring 比起来,使用太麻烦了,但在移动设备上也是没办法,只能牺牲简洁性,提高性能。引入 dagger 带来的不只是解耦和和便利性,同时也加大了项目上手的难度,用不用还是取决于团队的情况吧。

正式版发布说明

现在正式版已经发布了,请及时更新库版本号

 lifecycle = '1.0.0' lifecycleRuntime = '1.0.3' room = '1.0.0'

先修要求

  • 会使用 dagger 和 dagger-android

环境配置

加入 dagger 和 dagger-android 的库

 // dagger annotationProcessor "com.google.dagger:dagger-compiler:$rootProject.dagger" compile "com.google.dagger:dagger-android:$rootProject.dagger" compile "com.google.dagger:dagger-android-support:$rootProject.dagger" // if you use the support libraries annotationProcessor "com.google.dagger:dagger-android-processor:$rootProject.dagger"

重构

UserActivity

依赖注入主要是减少重复的创建代码。在 UserActivity 中, userViewModel 竟然是这样创建的。

 userViewModel = ViewModelProviders.of(this).get(UserViewModelModule.class);

这不能忍。果断干掉(删掉这段代码,让 dagger 自动设置一个值)。相应的,需要在 userViewModel 上加注入注解。并调用代码进行注入。(后面讲)

 @Inject UserViewModel userViewModel;

UserViewModel

然而事情没有这么简单,会发现 UserViewModel 里面还有  UserRepository 也需要类似的处理。

这里就不得不提 dagger 的不便利之处。Dagger 需要指定注入的类,而 Spring 可以直接设置扫描的包(设置好就不用管给哪个类注入了,美滋滋)。

因为如上原因,想要给 UserViewModel 注入对象,有两种选择:

  1. 调用注入代码对 UserViewModel 注入

  2. 在构建 UserViewModel 时,把里面需要构建的对象作为参数传进去。

事实上,dagger 的注入代码写起来让人很不爽。并不是说代码复杂或者很多,而是违背了依赖注入的原则:一个类不应该知道如何实现依赖注入。

对于在 Activity 中注入一次,还有办法通过基类来隐藏注入的代码。而多次注入这种事自然不能忍。毫无疑问,这是一道送命题。。不,送分题。

所以,我们修改一下。去掉 UserRepository 的单例构建方式,并添加一个带参数的构造函数。

 @Singleton public class UserViewModel extends ViewModel {     UserRepository userRepository; ...     @Inject     public UserViewModel(UserRepository userRepository) {         this.userRepository = userRepository;     } ... }

在构造方法上加上 Inject 表示构建的时候,使用这个构造方法。

UserRepository

走到这里,你会发现我们需要给 UserViewModel 提供  UserRepository, 这真是个艰难的任务啊。

因为打开 UserRepository 里面竟然还有三个对象需要注入。一鼓作气,全给他改了。

 @Singleton public class UserRepository {     private Context context;     private UserDataSource remoteUserDataSource;     private UserDataSource localUserDataSource;     @Inject     public UserRepository(Context context, @Remote UserDataSource remoteUserDataSource,                           @Remote UserDataSource localUserDataSource) {         this.context = context;         this.remoteUserDataSource = remoteUserDataSource;         this.localUserDataSource = localUserDataSource;     } }

这里面出现了自定义的注解 Remote 和 Local,主要是因为注入的两个类型一样,用于表明注入到哪个里面。会 Dagger 的都应该知道,不再解释。

因此我们需要给 UserRepository 注入两个数据源,然而这两个数据源也没那么简单

RemoteUserDataSource

 @Singleton public class RemoteUserDataSource implements UserDataSource {     private UserApi userApi;     private LocalUserDataSource localUserDataSource;     @Inject     public RemoteUserDataSource(UserApi userApi, LocalUserDataSource localUserDataSource) {         this.userApi = userApi;         this.localUserDataSource = localUserDataSource;     } ... }

LocalUserDataSource

 @Singleton public class LocalUserDataSource implements UserDataSource {     private UserService userService;     @Inject     public LocalUserDataSource(UserService userService) {         this.userService = userService;     } ... }

Provider

终于,我们把之前的代码改好了,那么就需要提供这样要注入的对象了。

UserActivityModule

最不好写的部分是这里,因为我们之前创建 ViewModel 都是无参的,这里确带了参数,所以我们不得不创建一个匿名工厂类来做这件事。实际上也很简单,就是 modelClass.getConstructor(UserRepository.class).newInstance(userRepository);

 @Module public class UserActivityModule {     @Provides     UserViewModel provideUserViewModel(UserActivity activity, final UserRepository userRepository) {         return ViewModelProviders.of(activity, new ViewModelProvider.Factory() {             @NonNull             @Override             public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {                 try {                     return modelClass.getConstructor(UserRepository.class).newInstance(userRepository);                 } catch (NoSuchMethodException e) {                     throw new RuntimeException("Cannot create an instance of " + modelClass, e);                 } catch (IllegalAccessException e) {                     throw new RuntimeException("Cannot create an instance of " + modelClass, e);                 } catch (InstantiationException e) {                     throw new RuntimeException("Cannot create an instance of " + modelClass, e);                 } catch (InvocationTargetException e) {                     throw new RuntimeException("Cannot create an instance of " + modelClass, e);                 }             }         }).get(UserViewModel.class);     } }

UserViewModelModule

这里就是提供 UserViewModel 生成需要的对象。

 @Module public class UserViewModelModule {     @Singleton     @Provides     UserRepository provideUserRepository(Context context, @Remote UserDataSource remoteUserDataSource,                                          @Local UserDataSource localUserDataSource) {         return new UserRepository(context, remoteUserDataSource, localUserDataSource);     }     @Remote     @Singleton     @Provides     UserDataSource provideRemoteUserDataSource(UserApi userApi, LocalUserDataSource localUserDataSource) {         return new RemoteUserDataSource(userApi, localUserDataSource);     }     @Singleton     @Provides     UserApi provideUserApi() {         return RetrofitFactory.getInstance().create(UserApi.class);     }     @Local     @Singleton     @Provides     UserDataSource provideLocalUserDataSource(UserService userService) {         return new LocalUserDataSource(userService);     }     @Singleton     @Provides     UserService provideUserService(UserDao userDao) {         return new UserServiceImpl(userDao);     }     @Singleton     @Provides     UserDao provideUserDao() {         return DBHelper.getInstance().getDb().getUserDao();     } }

BaseModule

上面可以注意到,是需要 Context 的,为了便于复用,放到基础 Module 中。

什么?哪里来的 BaseApplication ?不要急,马上就要说了。

 @Module public class BaseModule {     @Provides     Context provideContext() {         return BaseApplication.getAppContext();     } }

初始化

实际上 dagger 还有初始化的工作没有完成。

ActivitiesContributeModule

如下是 dagger-android 库提供的,用于生成重复性代码。

 @Module public abstract class ActivitiesContributeModule {     @ContributesAndroidInjector(modules = {UserActivityModule.class})     abstract UserActivity userActivityInjector(); }

Component

 @Singleton @Component(modules = {         UserViewModelModule.class,         BaseModule.class,         ActivitiesContributeModule_UserActivityInjector.class,         AndroidInjectionModule.class,         AndroidSupportInjectionModule.class, }) public interface BaseApplicationComponent {     void inject(BaseApplication application); }

BaseApplication

别忘了在 Manifests 中注册。

 public class BaseApplication extends Application implements HasActivityInjector {     @Inject     DispatchingAndroidInjector<Activity> dispatchingActivityInjector;     private static Context appContext;     public static Context getAppContext() {         return appContext;     }     @Override     public void onCreate() {         super.onCreate();         appContext = getApplicationContext();         DaggerBaseApplicationComponent.create().inject(this);     }     @Override     public AndroidInjector<Activity> activityInjector() {         return dispatchingActivityInjector;     } }

以上这些说实话真心不好解释,也可能是我太累了,不想解释了,又到快到1点了,明天还得继续上班,唉。

其他

初始化、注入注解 和 提供对象 这些都做好了,最后好像是差了 注入 这一个操作。

就像之前说的,对象不应该知道具体是怎么注入的,所以,我们最好是放到基类中去做。而 dagger-android 中也提供了这么一个类。

public class UserActivity extends DaggerAppCompatActivity

还有 DBHelper.getInstance().init(this); 请放到 Activity 的super.onCreate 之前,当然推荐是在 Application 中初始化,为什么我不这么做?因为我一个项目里面有 n 个 demo 例子,我不想混淆了。

 @Override protected void onCreate(@Nullable Bundle savedInstanceState) {     DBHelper.getInstance().init(this);     super.onCreate(savedInstanceState); }

好吧,应该没有其他的了,要是漏了,请查看我的 demo,文章开头有 github 链接。

总结

无力总结。这篇写得很失败,可能是太累了。本来打算周更,但发现月更都太累了。而且我的重心现在也不在安卓了,写这一系列文章主要是源于公司的技术分享会,说实话已经没什么动力,加上文章阅读的人好像不是很多,可能大家对安卓渐渐失去热情了吧,明明这么好的架构。

打算停更一段时间,可能几个月,可能几年。当然还是会写其他系列的教程,有感兴趣的欢迎关注我的订阅号 IT天宇

都说 8 小时谋生存,剩下时间谋发展,实际上可能是我业余做的事太多了,然而还是毫(穷)无(困)进(潦)展(倒),太累了,催更的,让我休息一会。