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

上一篇Dagger2入门学习之MVP项目整合(上)的最后我们列举了Dagger的关键注释,其中@Singleton和@Scope还没有接触,接下来我们来先看看这两个标签的使用。

@Singleton

类似于SharedPreferences对象,我们可能在整个App的生命周期中只需要一个单例,而不需要每次都通过application.getSharedPreferences(“spfile”, Context.MODE_PRIVATE)获得新的对象,那么怎么办呢,我们只需要在你需要单例的对象提供方法上加一个注解@Singleton ,类似这样:

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

private MyApplication application;

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

//全局单例SharedPreferences
@Provides
@Singleton
SharedPreferences provideSharedPreferences() {
return application.getSharedPreferences("spfile", Context.MODE_PRIVATE);
}

@Provides
MyApplication provideApplication() {
return application;
}
}

只需要一个@Singleton,无论我们在多少个Activity容器中通过@Inject获取这个SharedPreferences对象的实例,该对象都是同一个对象,从而实现了全局单例的效果。

你需要什么对象全局单例,就在提供该对象方法的@Provides注解旁加上一个@Singleton,并且在该module关联的Component中加上同样的注解

1
2
3
4
5
6
7
8
9
@Singleton      //不要忘了还要在关联的Component接口中声明,否则会编译报错
@Component(modules = AppModule.class)
public interface AppComponent {

SharedPreferences sharedPreferences();

MyApplication myApplication();

}

@Scope

除了全局单例,我们可能在开发中更多的是需要局部单例,比如,我在ActivityA中需要实例化两个Student对象,在ActivityB中也需要一个Student对象,但我希望ActivityA中的两个Student都叫小明,但ActivityB中的Student对象叫小刚。这个时候我们可以使用@Scope来自定义注解限定注解作用域。

1
2
3
4
@Scope  //声明这是一个自定义@Scope注解
@Retention(RUNTIME)
public @interface ActivityScope {
}

使用自定义@Scope注解

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

private A03Activity activity;

public A03Module(A03Activity activity) {
this.activity = activity;
}

@Provides
@ActivityScope//添加注解实现局部单例
Student provideStudent() {
return new Student();
}

}

在Component中同样添加@ActivityScope注解,否则会编译报错

1
2
3
4
5
6
7
@ActivityScope//添加注解实现局部单例
@Component(modules = A03Module.class, dependencies = AppComponent.class)
public interface A03Component {

void inject(A03Activity activity);

}

事实上,Android开发中使用Dagger,开发人员仍然需要面对一些问题。Google工程师们尝试弥补Dagger的问题,于是Dagger2-Android基于Dagger2应用于Android开发,由google开发的的拓展库应运而生。

Dagger2-Android

Demo Github地址:https://github.com/lxqxsyu/TestDagger
_参考文档:https://google.github.io/dagger/android

在Android项目中使用Dagger的主要区别在于Android框架本身具有一些类(例如Activity、Fragment)无法实例化它们。这样就需要我们在这些系统框架类的生命周期方法中实例化和注入,就像下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class FrombulationActivity extends Activity {
@Inject Frombulator frombulator;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// DO THIS FIRST. Otherwise frombulator might be null!
((SomeApplicationBaseType) getContext().getApplicationContext())
.getApplicationComponent()
.newActivityComponentBuilder()
.activity(this)
.build()
.inject(this);
// ... now you can write the exciting code
}
}

但是这样做有一些问题:

  1. 复制黏贴,到时后期很难维护和重构。
  2. 打破了依赖注入的核心原则:类不应该知道它是如何注入的。

接下来我们看如何更好的在Android中使用Dagger

Dagger2-Android引入

项目地址:https://github.com/google/dagger

  1. 环境&依赖
1
2
3
4
annotationProcessor 'com.google.dagger:dagger-compiler:2.x' //一定要添加dagger2的annotationProcessor!
compile 'com.google.dagger:dagger-android:2.x'
compile 'com.google.dagger:dagger-android-support:2.x' // if you use the support libraries
annotationProcessor 'com.google.dagger:dagger-android-processor:2.x
  1. Demo结构

Demo结构

这个就是整个demo的结构,图中MVP三层的接口和实现很容易理解,最右边的AppComponent实现的是全局的依赖注入,左边的MainComponent实现的是简单业务逻辑的注入关系,具体我们看代码分析。

Demo实现(被弃用)

说明:这种Dagger实现方式已经在Dagger2.10版本之后被废弃,请往下翻参考最新的

AppComponent

1
2
3
4
5
@Singleton
@Component(modules = {AppModule.class, OkHttpModule.class})
public interface AppComponent {
MainComponent addSub(MainModule mainModule);
}

这是一个全局的Component,这里例举了两个modules(根据实际需要可以添加多个)

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
@Module
public class AppModule {

private Context context;

public AppModule(CustomeApplication application) {
this.context = application;
}

@Singleton
@Provides
public Context ProviderApplicationContext(){
return context;
}

@Singleton
@Provides
@Named("default")
public SharedPreferences providerDefaultSharedPreferences(){
return PreferenceManager.getDefaultSharedPreferences(context);
}

@Singleton
@Provides
@Named("encode")
public SharedPreferences providerEncodeSharedPreferences(){
return context.getSharedPreferences("encode",Context.MODE_PRIVATE);
}
}

后面使用SharePreference和ApplicationContext就可以直接使用了而且是使用@Singleton修饰的。

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
public class CustomeApplication extends Application{

private static AppComponent appComponent;

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

public static CustomeApplication get(Context context) {
return (CustomeApplication) context.getApplicationContext();
}

private void setupApplicationComponent() {
appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
}

public AppComponent getAppComponent() {
if(appComponent == null){
this.setupApplicationComponent();
}
return appComponent;
}
}

MainComponent

1
2
3
4
5
@ActivityScope
@Subcomponent(modules = MainModule.class)
public interface MainComponent {
void inject(MainMvpActivity mainMvpActivity);
}
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 MainModule {

private MainMvpView mView;
private MainMvpModel mModel;

public MainModule(MainMvpView view, MainMvpModel model){
mView = view;
mModel = model;
}

@ActivityScope
@Provides
MainMvpView provideMainMvpView() {
return mView;
}

@ActivityScope
@Provides
MainMvpModel provideMainMvpModel(){
return mModel;
}
}
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
public class MainMvpActivity extends AppCompatActivity implements MainMvpView{

@Inject
MainMvpPresenterImpl mPresenter;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CustomeApplication.get(this)
.getAppComponent()
.addSub(new MainModule(this, new MainMvpModelImpl()))
.inject(this);

findViewById(R.id.click_me).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPresenter.loadData();
}
});
}

@Override
public void updateUI(String showMsg) {
Toast.makeText(MainMvpActivity.this, showMsg, Toast.LENGTH_LONG).show();
}
}

Dagger新注入方式

上面这种方式破坏了依赖注入的核心准则:一个类不应该知道它是如何被注入的,最新引入的dagger-android模块就是为了解决这个问题——将注入类与注入器分离开来。

在我们的项目中,新模块的配置需要不止一个component和module。每一个Activity都需要自己的subComponent并将其与Application的Component连接起来。以下是我建议的类结构:

1
2
3
4
5
6
7
8
9
/
| App (extending Application)
| AppComponent
| AppModule
| ContributeAndroidInjectorsModule // 3
+ ----- feature/
| FeatureModule // 2
| FeatureActivityModule // 1
| FeatureActivity

每一个feature拥有它自己的component以及module。

FeatureActivityModule

每一个Activity现在需要它自己的subcomponent,为此,Dagger引入了一个非常方便的注解来供我们使用以提醒Dagger生成subcomponent的代码。就像这样:

1
2
3
4
5
6
@Module
public abstract class FeatureActivityModule {
@ActivityScope
@ContributesAndroidInjector(modules = { FeatureModule.class })
abstract FeatureActivity contributeFeatureActivityInjector();
}

FeatureModule

我们将Activity自身需要的绑配置在这个module中。此module中的绑定仅仅在此Activity和它的subcomponent中有效,除非该module在其他component中使用。假设我们正在使用MVP的设计模式,以下是你通常用来绑定View的方式:

1
2
3
4
5
6
7
@Module
abstract class FeatureModule {

@ActivityScope
@Binds
abstract FeatureView provideFeatureView(FeatureActivity featureActivity);
}

ContributeActivityModule

到目前为止,我们告诉了Dagger为我们的Activity生成subcomponent,但是我们并没有将subcomponent与Applicaiton的Component连接起来。为了实现这个,我们需要另一个module来持有所有Activity的module。这个module将会被包含在Application的Component中。

1
2
3
4
5
6
7
@Module(includes = {
FeatureActivityModule.class
// Other Activity modules will be added here
})
abstract class ContributeActivityModule {

}

AppComponent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Singleton
@Component(modules = [
AndroidSupportInjectionModule::class, // 1
ContributeActivityModule::class, // 2
AppModule::class
])
public interface AppComponent extends AndroidInjector<App> { // 3

@Component.Builder
abstract class Builder extends AndroidInjector.Builder<App> {

}

}

注意:AppComponent中的一些修改:

  • 为了使Dagger Android能够正常运行,我们在Application的Component中包含了AndroidSupportInjectionModule
  • 包含ContributeActivityModule是为了将Activity的subcomponent与AppComponent连接起来
  • AppComponent必须继承自AndroidInjector 并将其泛型设定为Application类
  • Appcomponent的Builder可以选择性的继承AndroidInjector.Builder并提供Application类。因为这个base类已经实现了公共的component builder,这个builder中包含了一个设置Application实例的方法以及另一个构建component的方法,这可以很轻松的为我们介绍一些代码行数。

自定义的Application

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class App extends DaggerApplication {

private AppComponent appCompoent;

@Override
public void onCreate() {
appCompoent = DaggerAppComponent.builder()
.application(this)
.build();

super.onCreate();
}

@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return appCompoent;
}
}

最后在Activity中使用

在FeatureActivity中,我们将之前写的注入Activity的代码删除,同时使其继承自DaggerAppCompatActivity而不是AppCompatActivity

1
2
3
4
5
6
7
8
9
10
public class FeatureActivity extends DaggerAppCompatActivity implements FeatureView {
public static final String EXTRA_SOME_ID = "some_id";
@Inject FeaturePresenter presenter;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.feature_activity);
}
}

相关链接

Demo Github地址
轻松学,听说你还没有搞懂 Dagger2
告别Dagger2模板代码:Dagger Android使用详解

评论

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

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

×