Android Dagger2

Dagger2
概述:
官方地址:https://github.com/google/dagger
它是解决Android或java中依赖注入的一个类库。

Dagger2是Dagger1的分支,由谷歌公司接手开发,目前的版本是2.8。Dagger2是受到AutoValue项目的启发。 刚开始,Dagger2解决问题的基本思想是:利用生成和写的代码混合达到看似所有的产生和提供依赖的代码都是手写的样子。

基本注解:
@Inject
@Module
@Provide
@Component(注入器)
@Scope
@Singleton
@Qualifier(限定符)

基本用法:
@Inject + @Component

public class UserInfo {
    @Inject
​
    public UserInfo() {

    }
​
    public String getName() {
        return "Future";
    }
​
}
public class MainActivity extends AppCompatActivity {
   @Inject
    public UserInfo userInfo;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
​
        super.onCreate(savedInstanceState);
​
        setContentView(R.layout.activity_main);
​
        DaggerUserComponent.builder().build().inject(this);
​
           ((TextView)findViewById(R.id.name)).setText(userInfo.getName());
​
    }
​
}
@Component
public interface UserComponent {
    void inject(MainActivity mainActivity);
}

@Inject + @Component+ @Module + @Provides (Demo2)

@Component(modules = UserModule.class)
public interface UserComponent2 {
    void inject(MainActivity mainActivity);
}
@Module
public class UserModule {
​    @Provides
​    public ThirdUserInfo getUserInfo() {
        return new ThirdUserInfo();
    }
}
public class ThirdUserInfo {
    public String getName() {
        return "Future";
    }
}
public class SecondActivity extends Activity{​
    @Inject
    ThirdUserInfo thirdUserInfo;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DaggerUserComponent2.builder().build().inject(this);
        Log.d("lc", "name="+thirdUserInfo.getName());
​
    }
​
}

@Qualifier(限定符 TAG)当我们对于同一个类型,返回不同的对象时,Dagger会无法区分你到底要用哪个方法来提供依赖对象,这时就需要@Qualifier出现了。

@Module
public class UserModule {
    @Provides
    public ThirdUserInfo getUserInfo() {
        return new ThirdUserInfo().setName("tom");
    }
​
    @Provides
    public ThirdUserInfo getUserInfo2() {
        return new ThirdUserInfo().setName("lc");
    }
​
    @Provides
    public LoginInfo getLoginInfo() {
        return new LoginInfo();
    }
}

自定义注解Type 需要加@Qualifier注解来标识。

@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Type {
    String value() default "";
}

用自己定义的Type的注解来区分不同的依赖。

@Module
public class UserModule {
    @Provides
    @Type(value = "tom")
    public ThirdUserInfo getUserInfo() {
        return new ThirdUserInfo().setName("tom");
    }
​
​
​
    @Provides
    @Type(value = "lc")
    public ThirdUserInfo getUserInfo2() {
        return new ThirdUserInfo().setName("lc");
    }
​
    @Provides
    public LoginInfo getLoginInfo() {
        return new LoginInfo();
    }
}

然后再Activity中标识对应的注解。

public class SecondActivity extends AppCompatActivity{
    @Type("tom")
    @Inject
    ThirdUserInfo thirdUserInfo;
    @Type("lc")
    @Inject
    ThirdUserInfo thirdUserInfo2;
    @Inject
    LoginInfo loginInfo;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
​
        super.onCreate(savedInstanceState);
​
        DaggerUserComponent2.builder().build().inject(this);
​
        Log.d("lc", "name="+thirdUserInfo.getName());
​
        Log.d("lc", "name2="+thirdUserInfo2.getName());
​
    }
​
}

@Scope(作用域) & @Singleton(单例)
首先说下@Singleton(作用域-单例)
没有使用@Singleton的代码

public class UserInfo {
    @Inject
    public UserInfo() {
    }
}
@Component
public interface UserComponent3 {
    void inject(ThirdActivity thirdActivity);
}
public class ThirdActivity extends AppCompatActivity{
    @Inject
    UserInfo userInfo;
    @Inject
    UserInfo userInfo2;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
​
        super.onCreate(savedInstanceState);
​
        DaggerUserComponent3.builder().build().inject(this);
​
        Log.d("lc", "userInfo=" + userInfo.toString());
​
        Log.d("lc", "userInfo2=" + userInfo2.toString());
​
    }
​
}

打印结果:
D/lc (30180):userInfo=com.example.luchao.testdagger.demo3.UserInfo@43131e98
D/lc (30180): userInfo2=com.example.luchao.testdagger.demo3.UserInfo@43131ea8
这里可以看出是两个不同的对象
然后使用@ Singleton注解

@Singleton
public class UserInfo {
    @Inject
​
    public UserInfo() {
    }
​
}
@Singleton
@Component
public interface UserComponent3 {
    void inject(ThirdActivity thirdActivity);
}

打印结果:
D/lc ( 2834):userInfo=com.example.luchao.testdagger.demo3.UserInfo@43132bf0
D/lc ( 2834):userInfo2=com.example.luchao.testdagger.demo3.UserInfo@43132bf0

现在我们知道@Singleton注解实际上实现的是一个全局单例模式,在实际开发中我们可能还需要一种局部单例的控件,我们可以通过自定义@Scope来实现局部单例。
注意标红的这段话,是从网络上一篇博客看到的,因为这句话,我也被误导了好久。其实@Singleton并没有全局单例的功能。
当我从ThirdActivity跳转到ThirdActivity2中的时候。

public class ThirdActivity2 extends AppCompatActivity{
    @Inject
    UserInfo userInfo;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        ((MyApplication) //getApplication()).getApplicationCompenent().inject(this);
​
        DaggerUserComponent3.builder().build().inject(this);
​
        Log.d("lc", "userInfo3="+userInfo);
​
    }
​
}

发现UserInfo打印的完全不一样了。从此就产生了疑问了。后来阅读了一篇博客后恍然大悟。
地址:http://www.jianshu.com/p/1d42d2e6f4a5
其实@Singleton的作用并不是创建全局单例功能。仔细发现@Singleton的代码

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

其实也用到了@Scope注解。
在Dagger2中唯一创建全局单例模式的方式:
首先Module
提供了创建实例的方法,接着AppComponent
中对Module
进行管理,最后AppComponent****在自定义Applicaiton中被实例化了一次
最关键的就是被实例化一次,然后通过scope注解,通过代理实现单例。
如下:
自定义注解,用来表明Component与Module之间的关系。

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationScope {
}

module:

@Module
public class ApplicationModule {
    @ApplicationScope
    @Provides
    public UserInfo getUserInfo() {
        return new UserInfo();
    }
}

component:

@Component(modules = ApplicationModule.class)
@ApplicationScope
public interface ApplicationCompenent {
​
    void inject(ThirdActivity thirdActivity);
​
    void inject(ThirdActivity2 thirdActivity);
​
}

application:

public class MyApplication extends Application{
    private ApplicationCompenent applicationCompenent;
    @Override
    public void onCreate() {
        super.onCreate();
        applicationCompenent = DaggerApplicationCompenent.builder().build();
​
    }
​
    public ApplicationCompenent getApplicationCompenent() {
        return applicationCompenent;
    }
​
}

activity:

public class ThirdActivity extends AppCompatActivity{
    @Inject
    UserInfo userInfo;
    @Inject
    UserInfo userInfo2;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
​
        super.onCreate(savedInstanceState);
​
        setContentView(R.layout.activity_four);
​
        findViewById(R.id.next).setOnClickListener(new View.OnClickListener() {
​
            @Override
​
            public void onClick(View v) {
​
                Intent intent = new Intent(ThirdActivity.this, ThirdActivity2.class);
​
                startActivity(intent);
​
            }
​
        });
​
        ((MyApplication) getApplication()).getApplicationCompenent().inject(this);
​
//        DaggerUserComponent3.builder().build().inject(this);
​
        Log.d("lc", "userInfo=" + userInfo.toString());
​
        Log.d("lc", "userInfo2=" + userInfo2.toString());
​
    }
​
}

按照上面的情况来说,是不是@Singleton就是多此一举?其实也不是!
其实@
Singleton还有有一些作用的,首先一方面能让你直面的了解到这是一个单例,其次这个@
Singleton能够更好的管理Modlue
和Component
之间的关系。
Dagger2注入的基本实现原理:拿Demo2作为例子。
编译时会根据注解,生成实现类。路径:generated-source-apt-debug
生成类的对应关系: DaggerUserComponent2
UserComponent2 -> DaggerUserComponent2
SecondActivity -> SecondActivity_MembersInjector
UserModule:
getUserInfo -> UserModule_GetUserInfoFactory
getuserInfo2 -> UserModule_GetUserInfo2Factory
getLoginInfo -> UserModule_getLoginfoFactory
大概注入思路:Builder->Factory
见代码:
DaggerUserComponent2.builder().build().inject(this);注入的代码。

public static final class Builder {
  private UserModule userModule;
  private Builder() {}
  public UserComponent2 build() {
    if (userModule == null) {
      this.userModule = new UserModule();
    }
    return new DaggerUserComponent2(this);
  }
​
​
​
  public Builder userModule(UserModule userModule) {
​
    this.userModule = Preconditions.checkNotNull(userModule);
​
    return this;
​
  }
​
}

初始化 initialize:

private void initialize(final Builder builder) {
  this.getUserInfoProvider = UserModule_GetUserInfoFactory.create(builder.userModule);
​
  this.getUserInfo2Provider = UserModule_GetUserInfo2Factory.create(builder.userModule);
​
  this.getLoginInfoProvider = UserModule_GetLoginInfoFactory.create(builder.userModule);
​
  this.secondActivityMembersInjector =
​
      SecondActivity_MembersInjector.create(
​
          getUserInfoProvider, getUserInfo2Provider, getLoginInfoProvider);
​
}

为SecondActivity_MembersInjector类中的Provider<T> 对象赋值。

public static MembersInjector<SecondActivity> create(

​
    Provider<ThirdUserInfo> thirdUserInfoProvider,
​
    Provider<ThirdUserInfo> thirdUserInfo2Provider,
​
    Provider<LoginInfo> loginInfoProvider) {
​
  return new SecondActivity_MembersInjector(
​
      thirdUserInfoProvider, thirdUserInfo2Provider, loginInfoProvider);
​
}
public SecondActivity_MembersInjector(

​
    Provider<ThirdUserInfo> thirdUserInfoProvider,
​
    Provider<ThirdUserInfo> thirdUserInfo2Provider,
​
    Provider<LoginInfo> loginInfoProvider) {
​
  assert thirdUserInfoProvider != null;
​
  this.thirdUserInfoProvider = thirdUserInfoProvider;
​
  assert thirdUserInfo2Provider != null;
​
  this.thirdUserInfo2Provider = thirdUserInfo2Provider;
​
  assert loginInfoProvider != null;
​
  this.loginInfoProvider = loginInfoProvider;
​
}

此时对象的注入准备工作已经完成准备inject开始注入

@Override

​
public void inject(SecondActivity secondActivity) {
​
  secondActivityMembersInjector.injectMembers(secondActivity);
​
}

这时传进来要注入对象的类,我们这里是SecondActivity,这时猜想后面我们应该会对SecondActivity里面的
thirdUserInfo, thirdUserInfo2,loginInfo做初始化操作,往下看。
secondActivityMembersInjector是接口,哪里实现这个接口的呢?见初始化的initialize方法,里面SecondActivity_MembersInjector实现了secondActivityMembersInjector接口。
之后看SecondActivity_MembersInjector:

@Override

​
public void injectMembers(SecondActivity instance) {
​
  if (instance == null) {
​
    throw new NullPointerException("Cannot inject members into a null reference");
​
  }
​
  instance.thirdUserInfo = thirdUserInfoProvider.get();
​
  instance.thirdUserInfo2 = thirdUserInfo2Provider.get();
​
  instance.loginInfo = loginInfoProvider.get();
​
}

这里对SecondActivity中的对象完成了赋值,此时完成了注入的操作。
经过上述过程,完成了对SecondActivity依赖对象的注入。

简介:APT(AnnotationProcessing Tool)
现在很多主流库都使用了APT,比如Dagger2,ButterKnife,EventBus3等
代码样例参考:http://www.tuicool.com/articles/aIfaum6

dagger2****到底有哪些好****处****?(因地制宜)


增加开发效率、省去重复的简单体力劳动首先new一个实例的过程是一个重复的简单体力劳动,dagger2完全可以把new一个实例的工作做了,因此我们把主要精力集中在关键业务上、同时也能增加开发效率上。省去写单例的方法,并且也不需要担心自己写的单例方法是否线程安全,自己写的单例是懒汉模式还是饿汉模式。因为dagger2都可以把这些工作做了。

更好的管理类实例每个app中的ApplicationComponent管理整个app的全局类实例,所有的全局类实例都统一交给ApplicationComponent管理,并且它们的生命周期与app的生命周期一样。每个页面对应自己的Component,页面Component管理着自己页面所依赖的所有类实例。因为Component,Module,整个app的类实例结构变的很清晰。

解耦假如不用dagger2的话,一个类的new代码是非常可能充斥在app的多个类中的,假如该类的构造函数发生变化,那这些涉及到的类都得进行修改。设计模式中提倡把容易变化的部分封装起来。假如是通过用Inject注解标注的构造函数创建类实例,则即使构造函数变的天花乱坠,我们基本上都不需要修改任何代码。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容