Dagger2入门学习之MVP项目整合(上)

Dagger基础回顾

在前面一篇Dagger2基础传送门我们得出了一个结论。

  1. 若一个类(Xx)的构造函数被@inject标注,则该类编译时会产生Xx_Factory.
  2. 若一个类(Xx)的成员变量被@inject标注,则该类编译时会产生Xx_MembersInjector.
  3. @Component标注的接口(Xx)在编译时生成DaggerXx,负责将上面两个联系起来。

下面我们通过前面提到的这个例子再来回顾一下这个结论

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Created by 水寒 on 2017/11/16.
*/

public class Province {

@Inject
public City city;

@Inject
public Province(City city) {
this.city = city;
}

public String showAddress() {
return "四川省" + city.show();
}
}

Dagger2会在编译过程中生成对应的依赖项,这些依赖项在Android Studio该路径下,如图所示:

Dagger依赖生成文件目录

第一条结论:若一个类(Xx)的构造函数被@inject标注,则该类编译时会产生Xx_Factory.

1
2
3
4
@Inject
public Province(City city) {
this.city = city;
}

产生的对应的Province_Factory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public final class Province_Factory implements Factory<Province> {

private final MembersInjector<Province> provinceMembersInjector;

private final Provider<City> cityProvider;

public Province_Factory(
MembersInjector<Province> provinceMembersInjector, Provider<City> cityProvider) {
this.provinceMembersInjector = provinceMembersInjector;
this.cityProvider = cityProvider;
}

@Override
public Province get() {
return MembersInjectors.injectMembers(
provinceMembersInjector, new Province(cityProvider.get()));
}

public static Factory<Province> create(
MembersInjector<Province> provinceMembersInjector, Provider<City> cityProvider) {
return new Province_Factory(provinceMembersInjector, cityProvider);
}
}

上面代码看似比较多,其实做了一件很简单的事情,创建实例create方法(这里注入了cityProvider,通过get方法可以得到City实例)

我们接下来看看Province_Factory的构造函数,我们看到传进一个对应的City_MemberInjector和City的Provider.

1
2
3
public interface Provider<T> {
T get();
}

第二条结论:若一个类(Xx)的成员变量被@inject标注,则该类编译时会产生Xx_MembersInjector.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public final class City_MembersInjector implements MembersInjector<City> {

private final Provider<Street> streetProvider;

public City_MembersInjector(Provider<Street> streetProvider) {
this.streetProvider = streetProvider;
}

public static MembersInjector<City> create(Provider<Street> streetProvider) {
return new City_MembersInjector(streetProvider);
}

@Override
public void injectMembers(City instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.street = streetProvider.get();
}
}

生成的City_MembersInjector中我们注意其中重写的MembersInjector接口的injectMembers方法,这里进行了实例真正的赋值操作(关联)。

第三条结论:@Component标注的接口(Xx)在编译时生成DaggerXx,负责将上面两个联系起来。

1
2
3
4
5
@Component
public interface MainActivityComponent {

void inject(MainActivity activity);
}

在生成的DaggerMainActivityComponent里面我们看看MainActivityComponet的inject方法实现

1
2
3
4
@Override
public void inject(MainActivity activity) {
mainActivityMembersInjector.injectMembers(activity);
}

通过injectMembers()完成真正目标对象实例化以及依赖操作

1
2
3
4
5
6
7
//MainActivity_MembersInjector.java  
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.province = provinceProvider.get();
}

我们再来看看DaggerMainActivityComponent中的initialize方法

1
2
3
4
5
6
7
8
9
10
11
12
private void initialize(final Builder builder) {

this.cityMembersInjector = City_MembersInjector.create(Street_Factory.create());

this.cityProvider = City_Factory.create(cityMembersInjector, Street_Factory.create());

this.provinceMembersInjector = Province_MembersInjector.create(cityProvider);

this.provinceProvider = Province_Factory.create(provinceMembersInjector, cityProvider);

this.mainActivityMembersInjector = MainActivity_MembersInjector.create(provinceProvider);
}

可以看到inject()和initialize()刚好是相反的过程,直到找到依赖的源头完成源头对象的实例化,即这里的Street_Factory的get()方法的返回值;在Street实例化完成之后返回给City完成City实例化,City实例化完之后对自己的成员重新赋值了一遍,即产生依赖关系:

MVP模式

MVP 模式是 MVC 模式在 Android 上的一种变体,要介绍 MVP 就得先介绍 MVC。在 MVC 模式中,Activity 应该是属于 View 这一层。而实质上,它既承担了 View,同时也包含一些 Controller 的东西在里面。这对于开发与维护来说不太友好,耦合度大高了。把 Activity 的 View 和 Controller 抽离出来就变成了 View 和 Presenter,这就是 MVP 模式。

MVP模式的作用:

  • 分离了视图逻辑和业务逻辑,降低了耦合
  • Activity 只处理生命周期的任务,代码变得更加简洁
  • 视图逻辑和业务逻辑分别抽象到了 View 和 Presenter 的接口中去,提高代码的可阅读性
  • Presenter 被抽象成接口,可以有多种具体的实现,所以方便进行单元测试
  • 把业务逻辑抽到 Presenter 中去,避免后台线程引用着 Activity 导致 Activity 的资源无法被系统回收从而引起内存泄露和 OOM

MVP模式将原来一个Activity可以解决的问题,分成了M、V、P三部分,这在项目中会产品大量的类,如果利用Dagger2来进行这些类的有效管理,是接下来我们要思考的问题。

MVP实例

Model 接口

1
2
3
4
public interface MainMvpModel extends IModel{

void requestAPI(ApiCallback callback);
}

Model 实现

1
2
3
4
5
6
7
public class MainMvpModelImpl implements MainMvpModel {

@Override
public void requestAPI(ApiCallback callback) {
Log.d("TEST", "请求接口");
}
}

View 接口

1
2
3
4
public interface MainMvpView {

void updateUI();
}

View 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MainMvpActivity extends AppCompatActivity implements MainMvpView{

private MainMvpPresenter mPresenter = new MainMvpPresenterImpl();

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPresenter.attachView(this);
mPresenter.loadData();
}

@Override
public void updateUI() {

}

@Override
protected void onDestroy() {
super.onDestroy();
mPresenter.detachView(this);
}
}

Presenter 接口

1
2
3
4
public interface MainMvpPresenter extends IPresenter{

void loadData();
}

Presenter 实现

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 MainMvpPresenterImpl implements MainMvpPresenter {

private MainMvpModel mModel;
private MainMvpView mView;


public MainMvpPresenterImpl(){
mModel = new MainMvpModelImpl();
}

@Override
public void loadData() {
mModel.requestAPI(new ApiCallback() {
@Override
public void callback(int code) {

}
});
}

@Override
public void attachView(IView view) {
mView = (MainMvpView) view;
}

@Override
public void detachView(IView view) {
mView = null;
}
}

上面是典型的MVP开发安卓项目的结构,我们看到Presenter中强制注入了View和Model.如果我们要修改View和Model的构造方法的时候就需要修改Presenter, 同样我们修改Presenter的构造方法的时候就需要在对应的Activity中做相应的修改。接下来我们使用Dagger2的注释来实现这种依赖关系。

Component和Module

在使用Dagger实现MVP结构前我们得先知道关于Module的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Module
public class MainModule {

private MainMvpView mView;

public MainModule(MainMvpView view){
mView = view;
}

@Provides
MainMvpView provideMainView() {
return mView;
}
}
1
2
3
4
5
@Component(modules = {MainModule.class})
public interface MainMvpActivityComponent {

void inject(MainMvpActivity activity);
}

这个@Module相当于一个额外的拓展,通过@Provides提供给一种直接的依赖关系,假如我们不能通过给构造函数添加@Inject,则可以通过该方式进行依赖注入。

1
2
3
4
DaggerMainMvpActivityComponent.builder()
.mainModule(new MainModule(this))
.build()
.inject(this);

现在我们来理一理这三者的关系

  • @Inject : 注入,被注解的构造方法会自动编译生成一个Factory工厂类提供该类对象。

  • @Component: 注入器,类似快递员,作用是将产生的对象注入到需要对象的容器中,供容器使用。

  • @Module: 模块,类似快递箱子,在Component接口中通过@Component(modules =
    xxxx.class),将容器需要的商品封装起来,统一交给快递员(Component),让快递员统一送到目标容器中。

全局类 AppComponent & AppModule

开发中,局部变量尚还好说,我们在没有Dagger的时候,用的时候new一个就可以了,类似于SharedPerferences这种对象我们就比较头疼,也没有必要每次都去context.getSharedPerferences()吧,但是我们有了Dagger,我们可以通过依赖注入的方式,仅仅初始化一次,之后每次想使用,只需要@Inject注解,就能直接使用了。

1. 创建AppModule,提供你想要提供的全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Module
public class AppModule {

private MyApplication application;

public AppModule(MyApplication application) {
this.application = application;
}

//提供全局的sp对象
@Provides
SharedPreferences provideSharedPreferences(){
return application.getSharedPreferences("spfile", Context.MODE_PRIVATE);
}

//提供全局的Application对象
@Provides
MyApplication provideApplication(){
return application;
}

//你还可以提供更多.......
}

2. 创建AppComponent,然后ctrl+F9编译

1
2
3
4
5
6
7
@Component(modules = AppModule.class)
public interface AppComponent {

SharedPreferences sharedPreferences();

MyApplication myApplication();
}

3. 编译成功后,修改Application类,把Component注入器注入你的Application中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyApplication extends Application{

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

private void inject() {
AppComponent appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
ComponentHolder.setAppComponent(appComponent);
}
}

4. ComponentHolder类,方便获取appComponent对象

1
2
3
4
5
6
7
8
9
10
11
public class ComponentHolder {
private static AppComponent myAppComponent;

public static void setAppComponent(AppComponent component) {
myAppComponent = component;
}

public static AppComponent getAppComponent() {
return myAppComponent;
}
}

Activity的Component和Module

1
2
3
4
5
6
@Component(modules = MainModule.class, dependencies = AppComponent.class)
public interface MainMvpComponent {

void inject(MainMvpActivity activity);

}

请注意dependencies = AppComponent.class, 这句话意思是可以依赖全局的AppComponent,相当于一种继承关系。这样我们的MainMvpActivity中就可以使用SharePreference了(因为AppComponent注入器中已经有AppModule了)。

1
2
3
4
5
6
7
8
9
10
@Inject
SharedPreferences sp;

....

DaggerA02Component.builder()
.appComponent(ComponentHolder.getAppComponent())//添加appComponent注入器
.mainModule(new MainModule(this))
.build()
.inject(this);

关于Android中Dagger实现MVP下篇中再看。

关键的注释

Dagger中的关键注释,如果有点不清晰,没关系,后面会慢慢理解。

注解用法
@ModuleModules类里面的方法专门提供依赖,所以我们定义一个类,用@Module注解,这样Dagger在构造类的实例的时候,就知道从哪里去找到需要的 依赖。modules的一个重要特征是它们设计为分区并组合在一起(比如说,在我们的app中可以有多个组成在一起的modules)
@Provide在modules中,我们定义的方法是用这个注解,以此来告诉Dagger我们想要构造对象并提供这些依赖。
@Singleton当前提供的对象将是单例模式 ,一般配合@Provides一起出现
@Component用于接口,这个接口被Dagger2用于生成用于模块注入的代码
@Inject在需要依赖的地方使用这个注解。(你用它告诉Dagger这个 构造方法,成员变量或者函数方法需要依赖注入。这样,Dagger就会构造一个这个类的实例并满足他们的依赖。)
@ScopeScopes可是非常的有用,Dagger2可以通过自定义注解限定注解作用域。

参考:谷歌官方Demo https://google.github.io/dagger//android.html
参考:http://blog.csdn.net/mq2553299/article/details/73065745
参考:https://www.jianshu.com/p/39d1df6c877d

评论

Ajax Android AndroidStudio Animation Anroid Studio AppBarLayout Banner Buffer Bulma ByteBuffer C++ C11 C89 C99 CDN CMYK COM1 COM2 CSS Camera Raw, 直方图 Chrome ContentProvider CoordinatorLayout C语言 DML DOM Dagger Dagger2 Darktable Demo Document DownloadManage Element Error Exception Extensions File FileProvider Fresco GCC Git GitHub GitLab Gradle Groovy HTML5 Handler HandlerThread Hexo Hybrid I/O IDEA IO ImageMagick IntelliJ Intellij Interpolator JCenter JNI JS Java JavaScript JsBridge Kotlin Lab Lambda Lifecycle Lint Linux Looper MQTT MVC MVP Maven MessageQueue Modbus Momentum MySQL NDK NIO NexT Next Nodejs ObjectAnimator Oracle VM Permission PhotoShop Physics Python RGB RS-232 RTU Remote-SSH Retrofit Runnable RxAndroid RxJava SE0 SSH Spring SpringBoot Statubar Task Theme Thread Tkinter UI UIKit UML VM virtualBox VS Code ValueAnimator ViewPropertyAnimator Web Web前端 Workbench api apk bookmark by关键字 compileOnly css c语言 databases demo hexo hotfix html iOS icarus implementation init jQuery javascript launchModel logo merge mvp offset photos pug query rxjava2 scss servlet shell svg tkinter tomcat transition unicode utf-8 vector virtual box vscode 七牛 下载 中介者模式 串口 临潼石榴 主题 书签 事件 享元模式 仓库 代理模式 位运算 依赖注入 修改,tables 光和色 内存 内核 内部分享 函数 函数式编程 分支 分析 创建 删除 动画 单例模式 压缩图片 发布 可空性 合并 同向性 后期 启动模式 命令 命令模式 响应式 响应式编程 图层 图床 图片压缩 图片处理 图片轮播 地球 域名 基础 增加 备忘录模式 外观模式 多线程 大爆炸 天气APP 太白山 头文件 奇点 字符串 字符集 存储引擎 宇宙 宏定义 实践 属性 属性动画 岐山擀面皮 岐山肉臊子 岐山香醋 工具 工厂模式 年终总结 开发技巧 异常 弱引用 恒星 打包 技巧 指针 插件 摄影 操作系统 攻略 故事 数据库 数据类型 数组 文件 新功能 旅行 旋转木马 时序图 时空 时间简史 曲线 杂谈 权限 枚举 架构 查询 标准库 标签选择器 样式 核心 框架 案例 桥接模式 检测工具 模块化 模板引擎 模板方法模式 油泼辣子 泛型 洛川苹果 浅色状态栏 源码 瀑布流 热修复 版本 版本控制 状态栏 状态模式 生活 留言板 相册 相对论 眉县猕猴桃 知识点 码云 磁盘 科学 笔记 策略模式 类图 系统,发行版, GNU 索引 组件 组合模式 结构 结构体 编码 网易云信 网格布局 网站广播 网站通知 网络 美化 联合 膨胀的宇宙 自定义 自定义View 自定义插件 蒙版 虚拟 虚拟机 补码 补齐 表单 表达式 装饰模式 西安 观察者模式 规范 视图 视频 解耦器模式 设计 设计原则 设计模式 访问者模式 语法 责任链模式 贪吃蛇 转换 软件工程 软引用 运算符 迭代子模式 适配器模式 选择器 通信 通道 配置 链表 锐化 错误 键盘 闭包 降噪 陕西地方特产 面向对象 项目优化 项目构建 黑洞
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×