概述
Dagger2是一款使用在Java和Android上的靜態的,運行時依賴注入框架.官方地址:http://google.github.io/dagger/
記得當初剛學習Dagger2的時候看了許多博客,但是感覺上手依然困難,所謂光學不練就是這個意思吧
時至今日,用上此框架的同仁越來越多.分析文章也很多,上手相對要簡單了許多.
學習Dagger2最先要明白的是其各個注解的含義及工作原理,這樣才可以快速的上手和使用.
在這里簡要記錄一下在使用Dagger2過程中的感受和心得體會.
本文示例代碼地址:Dagger2Sample
配置信息
首先貼出此篇博客的所有依賴配置信息,因為dagger2需要依賴 apt,所以也需要引入apt插件,
- project 下面的build.gradle文件配置
buildscript {
repositories {
jcenter()
//依賴maven庫
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
classpath 'me.tatarka:gradle-retrolambda:3.2.5'
}
}
allprojects {
repositories {
jcenter()
mavenCentral()
}
}
- app目錄下的build.gradle文件配置
apply plugin: 'me.tatarka.retrolambda' //使用lanbda表達式
apply plugin: 'com.neenbedankt.android-apt' //apt插件
android {
//...
// 注釋沖突
packagingOptions {
exclude 'META-INF/services/javax.annotation.processing.Processor'
}
// 使用Java1.8
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
apt 'com.google.dagger:dagger-compiler:2.0.2'
compile 'com.google.dagger:dagger:2.0.2'
provided 'javax.annotation:jsr250-api:1.0'
compile 'io.reactivex:rxandroid:1.1.0' // RxAndroid
compile 'io.reactivex:rxjava:1.1.0' // RxJava
compile 'com.squareup.retrofit2:retrofit:2.0.2' // Retrofit網絡處理
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2' // Retrofit的rx解析庫
compile 'com.squareup.retrofit2:converter-gson:2.0.2' // Retrofit的gson庫
compile 'com.jakewharton:butterknife:8.0.1' // 標注
apt 'com.jakewharton:butterknife-compiler:8.0.1' //視圖注入
}
Dagger2基礎注解
Inject,Component,Module,Provides是Dagger中最基礎的幾個注解,是整個依賴注入的核心,下面我們來看一下各個注解的作用.
Inject and Component
@Inject
:
用來標注需要依賴的成員
和 被依賴類的構造函數
(如果依賴類同時依賴了其他類,其他類的構造函數也要有@Inject
標注),
注意: 使用@Inject
標注構造函數,不能標注一個類的多個構造函數
@Inject public UserModel() {
this.name = "Hello Dagger2,I`m from Inject";
}
@Inject public UserModel(String name) {
this.name = name;
}
上面的寫法會報錯:Error:(29, 18) 錯誤: Types may only contain one @Inject constructor.
@Component
:
光有@Inject
可不行,還需要一個東西將他兩聯系起來才能讓依賴類
找到被依賴對象
,其中Component
就起到了這個作用,在Dagger2
中是以接口形式存在,
是用來連接 需要依賴類
和 被依賴類
,Component
使用injectXX(XX xx)
將依賴注入到需要依賴的地方,
基本使用
接下來我們來看一下Dagger2最基本的用法
第一步:編寫JavaBean
/**
* Created on 16/6/8.下午8:57.
*
* @author bobomee
*/
public class UserModel {
private String name;
@Inject public UserModel() {
this.name = "Hello Dagger2,I`m from Inject";
}
public String getName() {
return name;
}
}
第二步:創建Component
/**
* Created on 16/6/8.下午8:59.
*
* @author bobomee
*/
@Component public interface UserComponent {
void inject(MainActivity mainActivity);
final class Initializer {
private Initializer() {
}
public static UserComponent init() {
return DaggerUserComponent.create();
}
}
}
第三步:構建依賴,先build一下生成dagger圖譜
public class MainActivity extends AppCompatActivity {
@BindView(R.id.text) TextView text;
@Inject UserModel userModel;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
//inject dependencies
UserComponent.Initializer.init().inject(this);
text.setText(userModel.getName());
}
}
注意: @Inject
成員不能是private
的,否則會報:Error:(35, 29) 錯誤: Dagger does not support injection into private fields
Provides and Module
@Provides
:
自定義依賴,Dagger2中不僅提供了@Inject
標注,他比@Inject
更加強大,不僅可以提供本地依賴,還可以提供第三方依賴(第三方庫和Android系統類
不可以用@Inject
注解構造函數).
注意: @Provides
的優先級高于@Inject
@Module
:
所有的@Provides
都必須包含在@Module
內部,相當于簡單工廠,提供了各種依賴@Provides
方法.之后再將Module加入到Component管理即可完成依賴注入(Component不僅可以從@Inject
找到被依賴類,還可以從@Module
找到被依賴類)
注意: 通過modules列出一個Component所有依賴的Module,如果缺失任何一個編譯會報錯
自定義Module使用
第一步:編寫AppModule,提供依賴@Provide
方法,其中@Singleton
是自定義注解
/**
* Created on 16/6/8.下午9:30.
*
* @author bobomee
*/
@Module public class AppModule {
private App app;//App為我們自定義的Application
public AppModule(App app) {
this.app = app;
}
@Provides @Singleton public App provideApp() {
return app;
}
}
第二步:創建Component,這里使用了@Singleton
,必須和Module中@Provides
方法修飾相同,否則編譯報錯
/**
* Created on 16/6/8.下午9:31.
*
* @author bobomee
*/
@Singleton @Component(modules = {
AppModule.class
}) public interface AppComponent {
//將依賴注入到自定義的Application
void inject(App app);
final class Initializer {
private Initializer() {
}
public static AppComponent init(App app) {
return DaggerAppComponent.builder().appModule(new AppModule(app)).build();
}
}
}
第三步:自定義Application中完成依賴
/**
* Created on 16/6/8.下午9:29.
*
* @author bobomee
*/
public class App extends Application {
private AppComponent appComponent;
@Inject static App app;
public static App get() {
return app;
}
@Override public void onCreate() {
super.onCreate();
//當inject完成后app就不為空了,且和Application生命周期相同,因此//Singleton可以起到全局單例的作用
buildComponent();
}
private void buildComponent() {
appComponent = AppComponent.Initializer.init(this);
appComponent.inject(this);
}
//向外提供appComponent,方便其他依賴appComponent的component構建
public AppComponent component() {
return appComponent;
}
}
第四步:使用依賴
//MainActivity中
@BindView(R.id.text1) TextView text1;
text1.setText(App.get().toString());
Component組織方式
一個應用中,必須包含一個全局的Component(類似于上面的AppComponent),管理整個App的實例.
一般AppComponent和Application生命周期相同,所以注入到Application中即可.
因為Application是全局單例的,所以AppModule中創建的實例也是單例的(
@Singleton
注解就是依照此原理).根據具體功能或者單獨的一個頁面定義一個Component.
某個單獨的Component要用到全局實例的時候,可以通過繼承AppComponent來實現
Component之間的關系有
依賴(dependencies)
,包含(SubComponent)
,繼承方式(extends)
Component依賴寫法
接下來來看一下一個典型的dependencies寫法
第一步: 定義注入到MainActivity的Product
/**
* Created on 16/6/8.下午9:48.
*
* @author bobomee
*/
public class Product {
private String productQualifier;
public Product(String productQualifier) {
this.productQualifier = productQualifier;
}
public String getProductQualifier() {
return productQualifier;
}
}
第二步:定義Component
/**
* Created on 16/6/8.下午8:59.
*
* @author bobomee
*/
@Component public interface UserComponent {
// unused
//void inject(MainActivity mainActivity);
final class Initializer {
private Initializer() {
}
public static UserComponent init() {
return DaggerUserComponent.create();
}
}
}
/**
* Created on 16/6/8.下午10:00.
*
* @author bobomee
*/
@ActivityScope @Component(dependencies = UserComponent.class,
modules = ProductModule.class) public interface ProductComponent extends UserComponent {
void inject(MainActivity mainActivity);
final class Initializer {
private Initializer() {
}
public static ProductComponent init(UserComponent userComponent) {
//dependency usercomponent
return DaggerProductComponent.builder().productModule(new ProductModule()).userComponent(userComponent).build();
}
}
}
注意: 此處依賴UserComponent,需要將UserComponent中的注入刪除.
//MainActivity
//inject dependencies
ProductComponent.Initializer.init(UserComponent.Initializer.init()).inject(this);
@Scope
:
注解作用域.用于更好的組織Component,和定義Component的粒度.
@Singleton
是Dagger2默認實現的,用于管理全局單例(AppComponent中),
@Scope
用在Component 和 Module 頭上,
如果要定義一個實例的生命周期在Activity內,則可以定義@ActivityScope
(當然名稱隨便起,只要對應即可)
/**
* Created on 16/6/8.下午9:55.
*
* @author bobomee
*/
@Scope @Retention(RetentionPolicy.RUNTIME) public @interface ActivityScope {
}
/**
* Created on 16/6/8.下午10:00.
*
* @author bobomee
*/
@ActivityScope @Component(dependencies = UserComponent.class,
modules = ProductModule.class) public interface ProductComponent extends UserComponent {
//...
}
/**
* Created on 16/6/8.下午9:49.
*
* @author bobomee
*/
@Module public class ProductModule {
@ActivityScope @Provides Product provideProduct() {
return new Product("this is a product");
}
//...
}
依賴迷失之Qualifier
@Qualifier
:
限定符,當依賴類中需要被依賴類的兩個不同對象的時候,幫助我們去為相同接口的依賴創建“tags”.
如我們需要兩個不同的level的product,Qualifier會幫助你區分對應的一個,
/**
* Created on 16/6/8.下午9:57.
*
* @author bobomee
*/
@Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface ProductLevel {
String value() default "";
}
- 聲明Module
/**
* Created on 16/6/8.下午9:49.
*
* @author bobomee
*/
@Module public class ProductModule {
//....
@ActivityScope @ProductLevel("good") @Provides Product provideGoodProduct() {
return new Product("good product");
}
@ActivityScope @ProductLevel("bad") @Provides Product provideBadProduct() {
return new Product("bad product");
}
}
- 注入Inject
@Inject @ProductLevel("good") Product product1;
@Inject @ProductLevel("bad") Product product2;
總結
- Inject用來標注
依賴
和被依賴的構造函數
- Provides提供依賴的方法上添加的注解,provide方法需要包含在Module中
- Module專門提供依賴,類似工廠模式,包含Provides方法
- Component
依賴
和被依賴
的橋梁,(先從Module中找依賴,再從Inject構造函數找) - Scope自定義注解,用于標示作用域,命名隨意,對應即可,其中@Singleton是一個系統的模式實現.(管理Module與Component的匹配)
- Qualifier自定義注解,用于解決一個實例可以被多種方式構建的依賴迷失問題
- Component有三種組織關系,分為依賴,包含和繼承,用于解決依賴復用與共享問題
依賴注入的過程:
步驟1:查找Module中是否存在創建該類的方法。
步驟2:若存在創建類方法,查看該方法是否存在參數
...........步驟2.1:若存在參數,則按從步驟1開始依次初始化每個參數
...........步驟2.2:若不存在參數,則直接初始化該類實例,一次依賴注入到此結束
步驟3:若不存在創建類方法,則查找Inject注解的構造函數,看構造函數是否存在參數
...........步驟3.1:若存在參數,則從步驟1開始依次初始化每個參數
...........步驟3.2:若不存在參數,則直接初始化該類實例,一次依賴注入到此結束
參考:
這里推薦牛曉偉的三篇博客:
Android:dagger2讓你愛不釋手-基礎依賴注入框架篇