Dagger2使用简介

Dagger是最早由Square公司开发的一款开源的依赖注入工具(Dagger1, 版本1.x),现在由Google接手进行开发和维护(Dagger2, 版本2.x)。Dagger2相比于Dagger1有不少改动的地方,效率更高,本文将通过Demo讲解Dagger2的用法。

本文Github源码下载

在Demo的两个分支(without_dagger_branch、with_dagger_branch)上分别通过自定义依赖,以及使用Dagger2进行依赖注入的方式实现了相同的功能,通过两种方式的比较可以加深理解。

Demo利用RecyclerView实现了一个简单的电影列表,效果如下:


Demo效果

首先理解什么是依赖,看without_dagger_branch的代码如下:

public class MainActivity extends AppCompatActivity {    
    ...
    private RecyclerViewAdapter viewAdapter;   

    @Override    
    protected void onCreate(Bundle savedInstanceState) {  
        ...            
        viewAdapter = new RecyclerViewAdapter(this);        

        mRecyclerView.setAdapter(viewAdapter);      
        ...  
  }
    ...
}

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {  
    ...  
    private ThirdPartyUtility utility;    
    public RecyclerViewAdapter(Context context) {        
        this.context = context;        
        this.utility = new ThirdPartyUtility(context);    
    }
    ...
}

在MainActivity里用到了RecyclerViewAdapter类型的变量viewAdapter,其中RecyclerViewAdapter是我们自定义的RecyclerView的Adapter,而RecyclerViewAdapter里又用到了ThirdPartyUtility类型的变量utility,ThirdPartyUtility是第三方开发的工具包。viewAdapter就称为类MainActivity的依赖,而utility称为类RecyclerViewAdapter的依赖。

像上面代码所示的这种方式,在类里面进行用到的变量的初始化,就是传统的初始化依赖的方式。通过依赖注入框架,我们可以将依赖初始化的过程放在Dagger辅助类里面完成,然后注入到MainActivity中,这样一来方便测试,同时可以使得MainActivity里面的代码聚焦于功能逻辑的实现,而不是各种变量繁杂的初始化。

切换到with_dagger_branch分支,我们通过Dagger进行依赖注入实现同样的功能。Dagger依赖注入主要是通过几个注解实现的,包括@Inject, @Module,@Provides,@Component等。

使用Dagger前先配置一下gradle:
根目录gradle:

buildscript {    
    ...
    dependencies {   
        ...     
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'        
    }
    ...
}

App目录下gradle:

dependencies {    
    ...
    compile 'com.google.dagger:dagger:2.0.2'    
    apt 'com.google.dagger:dagger-compiler:2.0.2'    
    provided 'org.glassfish:javax.annotation:10.0-b28'
    ...
}

配置完Gradle,sync一下,开始开发。首先使用@Inject指定要注入的变量:

public class MainActivity extends AppCompatActivity {      
    ...
    @Inject    
    RecyclerViewAdapter viewAdapter; 
   
    @Override    
    protected void onCreate(Bundle savedInstanceState) {        
        ...
        mRecyclerView.setAdapter(viewAdapter);
        ...        
}

通过用@Inject指定要注入的依赖变量viewAdapter,我们无需再通过new的方式进行变量初始化。

接下来修改RecyclerViewAdapter类,给其构造函数添加@Inject注解,告知Dagger利用哪个函数进行依赖viewAdapter的初始化:

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {    
    ...   
    @Inject    
    ThirdPartyUtility thirdPartyUtility;    
    @Inject    
    Context context;  
  
    @Inject    
    public RecyclerViewAdapter() {}
    ...
}

在RecyclerViewAdapter中,我们用到两个变量thirdPartyUtility和context,同理,我们通过添加@Inject注解实现这两个变量的依赖注入。这里有个问题,跟我们自定义的RecyclerViewAdapter类不同,Context类和ThirdPartyUtility不是我们自定义的类,不受我们的控制,所以我们无法也在它们的构造函数中添加@Inject注解来告知Dagger怎样进行初始化。这里就需要用到@Module注解和@Provides注解来应对这种情况。

新定义一个类ModuleClass,如下:

@Module
public class ModuleClass {    
    private final Context context;    
    public ModuleClass(Context context) {        
        this.context = context;    
    }    

    @Provides    
    Context provideContext() {        
        return context;    
    }    

    @Provides    
    ThirdPartyUtility provideThirdPartyUtility() {        
        return new ThirdPartyUtility(context);    
    }
}

给ModuleClass添加@Module注解,并添加两个以provide作为前缀的函数,分别提供第三方类Context和ThirdPartyUtility的初始化。并在两个函数上面添加@Provides注解。这样就可以实现不受我们控制的第三方类的初始化。

最后利用@Component注解添加一个用于连接ModuleClass和MainActivity类的桥梁接口,如下:

@Component(modules = {ModuleClass.class})
public interface ComponentClass {
    void inject(MainActivity mainActivity); 
}

通过@Component注解指定这是个Component类,并通过modules指定包含的Module类。添加一个inject函数,函数的参数就是要注入的目的类,在这里是MainActivity。
最后就是真正注入依赖的地方,在MainActivity中:

public class MainActivity extends AppCompatActivity {    
    ...
    @Inject    
    RecyclerViewAdapter viewAdapter;  
  
    @Override    
    protected void onCreate(Bundle savedInstanceState) {     
        ...   
        DaggerComponentClass.builder().moduleClass(new ModuleClass(this)).build().inject(this);        
        mRecyclerView.setAdapter(viewAdapter);      
        ...  
    }
    ...
}

这里Dagger框架根据Component类自动生成的一个辅助类DaggerComponentClass,通过这个类进行依赖注入,需要make一下工程生成这个辅助类。这样就完成了依赖viewAdapter的注入。运行代码,得到跟传统的new方式初始化依赖同样的效果。

更多的功能可以通过其他注解实现,如通过@Singleton实现单例,通过@Scope等注解实现作用域的指定等,这里不再介绍,可以参考Google文档进行了解和实践。

参考:
https://github.com/google/dagger

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

推荐阅读更多精彩内容