架构进阶,Dagger2的原理及使用详解 [复制链接]

2019-8-2 09:59
rongchuang 阅读:713 评论:0 赞:0
Tag:  Dagger2

目录

  • 一:Dagger2是什么?
  • 二:为什么要有Dagger2
  • 三:Dagger2如何使用
  1. 基本的概念
  2. 如何使用Dagger2
  3. 高级用法
  • (1)构造方法需要其他参数时候
  • (2) 模块之间的依赖关系
  • (3) @Named注解使用
  • (4) @Singleton注解
  • (5)自定义Scoped
  • (6)Subcomponent
  • (7)lazy 和 Provider
  • ?#27169;?MVP + Dagger2

Ps?#20309;?#26411;有架构师进阶资料和面试题资料


一:Dagger2是什么?

是一个依赖注入框架,butterknife也是一个依赖注入框架。不过butterknife,最多叫奶油刀,Dagger2被叫做利器啊,他的主要作用,就是对象的管理,其目的是为了降低程序耦合。

二:为什么要有Dagger2


8年Android开发程序员:架构进阶,Dagger2的原理及使用详解


下面我就?#20013;?#20102;

public class A {
public void eat() {
System.out.print("吃饭了");
}
}

使用的时候我们就要

A a = new A();
a.eat();

如果现在改了,早A的构造方法中必须传入B对象

public class A {
private B b;
public A(B b) {
this.b = b;
}
public void eat() {
System.out.print("吃饭了");
}
}

那么使用的时候

A a = new A(new B());
a.eat();

可能就有人说了,不就加一个对象么,这里只是我举的一个很简单的例子,看的感觉很简单,但是在?#23548;士?#21457;中,如果现在改了一个这个构造方法。是不是意味着,整个项目中的都的?#27169;?#19968;不小?#27169;?就是BUG 啊

三:Dagger2如何使用

1. 基本的概念

上来给你说,怎么玩,肯定懵逼,这里我简单说一?#24405;?#20010;概念,想有个?#29616;?#22312;往下看,会好很多,Dagger 是通过@Inject使用具体的某个对象,这个对象呢,是由@Provides注解提供,但是呢,这个@Provides只能在固定的模块中,也就是@Module注解,我们查找的时候,不是直接去找模块,而是去找@Component


8年Android开发程序员:架构进阶,Dagger2的原理及使用详解


我们反向推导,当我们使用

@Inject
A a

想要获取a对象的示例的时候,Dagger2 会先去找,当前Activity或者Fragment所连接的桥梁,例如上图中,连接的只有一个桥梁,?#23548;?#19978;可以有多个,这个桥梁,会去寻找他所依赖的模块,如图中,依赖了模块A,和模块B,然后在模块中,会去寻找@Providers注解,去寻找A的实例化对象。

2. 如何使用Dagger2

(1) 引入依赖库

Dagger2官网

 compile 'com.google.dagger:dagger:2.11'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11'

(2) 创建Moudule

//第一步 添加@Module 注解
@Module
public class MainModule {
}

(3)创建具体的示例

//第一步 添加@Module 注解
@Module
public class MainModule {
//第二步 使用Provider 注解 实例化对象
@Provides
A providerA() {
return new A();
}
}

(4)创建一个Component

//第一步 添加@Component
//第二步 添加module
@Component(modules = {MainModule.class})
public interface MainComponent {
//第三步 写一个方法 绑定Activity /Fragment
void inject(MainActivity activity);
}

(5)Rebuild Project


8年Android开发程序员:架构进阶,Dagger2的原理及使用详解


然后AS 会自动帮我们生成一个


8年Android开发程序员:架构进阶,Dagger2的原理及使用详解


开头都是以Dagger开始的

(6)将Component与Activity/Fragment绑定关系

package com.allens.daggerdemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.allens.daggerdemo.Bean.A;
import com.allens.daggerdemo.component.DaggerMainConponent;
import javax.inject.Inject;

public class MainActivity extends AppCompatActivity {
/***
* 第二步 使用Inject 注解,获取到A 对象的实例
*/
@Inject
A a;

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

/***
* 第一步 添加依赖关系
*/
//第一种方式
DaggerMainConponent.create().inject(this);

//第二种方式
DaggerMainConponent.builder().build().inject(this);

/***
* 第三步 调用A 对象的方法
*/
a.eat();
}
}

肯定有小伙伴说了,为?#22235;?#21040;一个对象,这么大个弯,太麻烦了。别急慢慢看,路要一步一?#38454;?#22043;

3. 高级用法

(1)构造方法需要其他参数时候

怎么?#30340;兀?#23601;和最来时的意思一样,

A a = new A(new B());
a.eat();

这种情况,如何使用Dagger2呢

肯定有小伙伴这么想

 @Provides
A providerA() {
return new A(new B());
}

直接 new 一个B ,这样的使用方法,是不对的!!!!!!,不对的!!!!!!!,不对的!!!!!!!!!

正确的打开方式

这时候,我们什么都不用该,只需要在moudule中添加一个依赖就可以了

@Module
public class MainModule {

/***
* 构造方法需要其他参数时候
*
* @return
*/
@Provides
B providerB() {
return new B();
}

@Provides
A providerA(B b) {
return new A(b);
}
}

(2) 模块之间的依赖关系



模块与模块之间的联系,

@Module (includes = {BModule.class})// includes 引入)
public class AModule {
@Provides
A providerA() {
return new A();
}
}

这样的话,Dagger会现在A moudule 中寻找对象,如果没找到,会去找module B 中是否有被Inject注解的对象,如果还是没有,那么GG,抛出异常

一个Component 应用多个 module

@Component(modules = {AModule.class,BModule.class})
public interface MainComponent {
void inject(MainActivity activity);
}

dependencies 依赖其他Component

@Component(modules = {MainModule.class}, dependencies = AppConponent.class)
public interface MainConponent {
void inject(MainActivity activity);
}

注意 这里有坑。一下会讲解


(3) @Named注解使用

相当于有个表示,虽然大家都是同一个对象,但是实例化对象不同就不如

A a1 = new A();
A a2 = new A();

// a1 a2 能一样嘛

Module中 使用@Named注解

@Module
public class MainModule {

private MainActivity activity;

public MainModule(MainActivity activity) {
this.activity = activity;
}

@Named("dev")
@Provides
MainApi provideMainApiDev(MainChildApi mainChildApi, String url) {
return new MainApi(mainChildApi, activity,"dev");
}

@Named("release")
@Provides
MainApi provideMainApiRelease(MainChildApi mainChildApi, String url) {
return new MainApi(mainChildApi, activity,"release");
}

}

在Activity/Fragment中使用

public class MainActivity extends AppCompatActivity {

@Named("dev")
@Inject
MainApi apiDev;

@Named("release")
@Inject
MainApi apiRelease;

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

DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.mainChildModule(new MainChildModule())
.build()
.inject(this);
apiDev.eat();
apiRelease.eat();
Log.i("TAG","apiDev--->" + apiDev);
Log.i("TAG","apiRelease--->" + apiRelease);
}

}

打印Log

07-14 01:46:01.170 2006-2006/? I/TAG: apiDev--->[email protected]
07-14 01:46:01.170 2006-2006/? I/TAG: apiRelease--->[email protected]

(4) @Singleton注解

单利模式,是不是超级方便,你想然哪个对象单利化,直接在他的Provider上添加@Singleton 就行了

例如

 @Singleton
@Provides
A providerA(B b) {
return new A(b);
}

注意: 第一个坑!!!

如果 moudule所依赖的Comonent 中有被单利的对象,那么Conponnent也必须是单利的

@Singleton
@Component(modules = {MainModule.class})
public interface MainConponent {
}

然后 在Activity中使用,直接打印a1 a2 的地址,

 @Inject
A a2;
@Inject
A a1;

可以看到Log

12-30 01:32:58.420 3987-3987/com.allens.daggerdemo E/TAG: A1---->[email protected]
12-30 01:32:58.420 3987-3987/com.allens.daggerdemo E/TAG: A1---->[email protected]

不相信的小伙伴可以吧@Singleton去掉试试

现在我们完成了单利,然后做了一个事情,就是点击某个按钮,跳转到一个新的Activiry,两边都引用同样一个A 对象,打印A 的地址,

说一下,一个Conponent 可以被对个Activity/Fragment 引用,如

@Singleton
@Component(modules = {MainModule.class})
public interface MainConponent {
void inject(MainActivity activity);
void inject(TestAct activity);
}

上面与两个Activity, MainActivity 和 TestAct ,都引用相同的 对象,答应地址看看

12-30 00:48:17.477 2788-2788/com.allens.daggerdemo E/TAG: A1---->[email protected]
12-30 00:48:17.517 2788-2788/com.allens.daggerdemo E/TAG: A2---->[email protected]

竟然不同,说好的单利呢

注意: 第二个坑,单利对象只能在同一个Activity中有效。不同的Activity 持有的对象不同

那有人就要问了,没什么办法么,我就想全局只要一个实例化对象啊? 办法肯定是有的,


(5) 自定义Scoped

/**
* @作者 : Android架构
* @创建日期 :2017/7/14 下午3:04
* @方法作用:
* 参考Singleton 的写法
* Scope 标注是Scope
* Documented 标记在文档
* @Retention(RUNTIME) 运行时级别
*/
@Scope
@Documented
@Retention(RUNTIME)
public @interface ActivityScoped {
}

首先想一下,什么样的对象,能够做到全局单例,生命周期肯定和APP 绑定嘛,这里我做演示,一个AppAip 我们要对这个对象,全局单利,所以二?#23433;?#35828;,先给Application 来个全家桶,

Module

@Module
public class AppModule {

@Singleton
@Provides
AppApi providerAppApi() {
return new AppApi();
}
}

Component

@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
AppApi getAppApi();
}

Application

public class MyApp extends Application {

private AppConponent appComponent;

@Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppConpoment.create();
}

public AppConponent getAppComponent() {
return appConponent;
}
}

最后是如何使用

首先,这个是个桥梁,依赖方式,上文已经说过了

@ActivityScoped
@Component(modules = {MainModule.class}, dependencies = AppConponent.class)
public interface MainComponent {
void inject(MainActivity activity);
void inject(TestAct activity);
}

?#24863;?#30340;小伙伴可能已经发现了,只有在上面一个MainComponent添加了一个@ActivityScoped,这里说明一下,@Singleton是Application 的单利

注意,第三个坑,子类component 依赖父类的component ,子类component的Scoped 要小于父类的Scoped,Singleton的级别是Application

所以,我们这里的@Singleton 级别大于我们自定义的@ActivityScoped,同时,对应module 所依赖的component ,也要放上相应的Scope

好吧,上面的例子,打印Log.

12-30 02:16:30.899 4717-4717/? E/TAG: A1---->[email protected]
12-30 02:16:31.009 4717-4717/? E/TAG: A2---->[email protected]

一样啦

爬坑?#25913;希?#26497;度重要)

  1. Provide 如果是单例模式 对应的Compnent 也要是单例模式
  2. inject(Activity act) 不能放父类
  3. 即使使用了单利模式,在不同的Activity 对象还是不一样的
  4. 依赖component, component之间的Scoped 不能相同
  5. 子类component 依赖父类的component ,子类component的Scoped 要小于父类的Scoped,Singleton的级别是Application
  6. 多个Moudle 之间不能提供相同的对象实例
  7. Moudle 中使用了自定义的Scoped 那么对应的Compnent 使用同样的Scoped

(6)Subcomponent

这个是系?#31243;?#20379;的一个Component,当使用Subcomponent,那么默认会依赖Component

例如

@Subcomponent(modules = TestSubModule.class)
public interface TestSubComponent {
void inject(MainActivity activity);
}
@Component(modules = {MainModule.class})
public interface MainConponent {
TestSubComponent add(TestSubModule module);
}

在TestSubComponent中 我void inject(MainActivity activity);,便是这个桥梁,我是要注入到MainActivity,但是dagger 并不会给我生成一个Dagger开头的DaggerTestSubComponent 这个类,如果我想使用TestSubModule.class里面提供的对象,依然还是使用DaggerMainConponent例如

 DaggerMainConponent
.builder()
.mainModule(new MainModule())
.build()
.add(new TestSubModule())
.inject(this);

可以看到这里有一个add的方法,真是?#20197;贛ainConponent添加的TestSubComponent add(TestSubModule module);


(7)lazy 和 Provider

public class Main3Activity extends AppCompatActivity {

@PresentForContext
@Inject
Lazy<Present> lazy;
@PresentForName
@Inject
Provider<Present> provider;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);

AppComponent appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
ActivityComponent activityComponent = DaggerActivityComponent.builder()
.appComponent(appComponent)
.activityModule(new ActivityModule())
.build();

activityComponent.injectActivity(this);
Present present = lazy.get();
Present present1 = provider.get();
}
}

其中Lazy(懒加载)的作用好比component初始化了一个present对象,然后放到一个池子里,需要的时候就get它,所以你?#30475;蝕et的时候拿到的对象都是同一个;并且当你第一次去get时,它才会去初始化这个实例.

procider(强制加载)的作用:

1:同?#31995;?#20320;第一次去get时,它才会去初始化这个实例

2:后面当你去get这个实例时,是否为同一个,取决于他Module里实现的方式


?#27169;?MVP + Dagger2

这是现在主流的设计架构

MVP,这个?#19968;?#22312;后面的文?#38470;?#32461;,这里不做太多解释

当你了解MVP 的时候,你就知道,所有的业务逻辑全在Presenter,

换句话, presenter 持有的对象,控制着你程序的全部逻辑,这在dagger 中,讲白了 我们只要将所有的presetner 对象控制就可以了

下面附上目录结构,当然仅仅作为参考。dagger ?#30475;?#30340;用法还是需要各位自己去体会,下面的项目 是我刚刚学会dagger 时候 写的一个项目


8年Android开发程序员:架构进阶,Dagger2的原理及使用详解


8年Android开发程序员:架构进阶,Dagger2的原理及使用详解


可以看到 ,我是 将所有的activity 或者 fragment 全部添加在同一个Component中,当然现在的话 不推荐,比如Utils 你可以专门做一个Component,

首先放上我的Module,公司项目,很多东西没敢放上来,体谅,可以看到 我这里提供了一个SplashPresenter,也就是启动页的Presneter,业务逻辑

@Module
public class ApiModule {

public ApiModule() {

}

@Provides
@Singleton
Handler provideHandler() {
return new Handler();
}

@Provides
@Singleton
SQLiteDatabase provideSQLiteDatabase() {
return new DataBaseHelper(MyApp.context, Config.SqlName, null, Config.SqlVersion).getWritableDatabase();
}

/**
* @ User : Android架构
* @ 创建日期 : 2017/7/13 下午3:24
* @模块作用 :
* <p>
* ====================================================================================================================================
* ====================================================================================================================================
*/
private SplashPresenter splashPresenter;

public ApiModule(SplashAct splashAct) {
splashPresenter = new SplashPresenter(splashAct, new SplashModel());
}

@Provides
@Singleton
SplashPresenter provideSplashPresenter() {
return splashPresenter;
}

.....
}

当我使用的时候,只需要注入即可,如下代码

public class SplashAct extends BaseActivity implements SplashContract.View {

@Inject
SplashPresenter presenter;

@Inject
Handler handler;

@Inject
ApiService apiService;

@Override
protected void onCreate() {
setContentView(R.layout.activity_splash);
}

@Override
protected void initInject() {
DaggerApiComponent.builder()
.apiModule(new ApiModule(this))
.build()
.inject(this);
}

@Override
protected void initListener() {
presenter.getWordsInfo(true, apiService);
}

@Override
public void gotoLogInAct() {
handler.postDelayed(new Runnable() {
@Override
public void run() {
startActivity(new Intent(SplashAct.this, LogInAct.class));
finish();
}
}, 1500);
}
}


?#20381;?#35828;两句
您需要登录后才可以评论 登录 | 立即注册
facelist
所有评论(0)
领先的中文移动开发者社区
18620764416
7*24全天服务
意见反馈:[email protected]

扫一扫关注我们

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粤ICP备15117877号 )

阿拉斯加垂钓APP下载