[Dagger2](https://github.com/google/dagger)是一個Java和Android的依賴注入架構.
本文介紹Android中dagger2的基本使用.
其中包括@Inject, @Component, @Module和@Provides注解的使用.
Dagger2是一個Java和Android的依賴注入架構.
其中包括
@Inject
, @Component
@Module
和 @Provides
注解的使用. 使用依賴注入的好處
1.使用類和被依賴的對象構造分開,這樣如果我們需要改變被依賴類的構造方法,不必改動每一個使用類.
2.對各種被依賴類的執行個體,可以隻構造一次.
3.當我們需要更換一種實作時,隻需要保證接口一緻.
4.利于單元測試,我們可以友善地mock依賴類的對象.
優點總結: 建立對象和使用對象分離, 子產品化增強.
Dagger2的使用
Set Up
在項目的build.gradle裡加這個:
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
然後app的build.gradle:
apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
android {
compileSdkVersion 24
buildToolsVersion "24.0.0"
defaultConfig {
applicationId "com.ddmeng.dagger2sample"
minSdkVersion 16
targetSdkVersion 24
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.0.0'
compile 'javax.annotation:jsr250-api:1.0'
compile 'com.google.dagger:dagger:2.2'
apt 'com.google.dagger:dagger-compiler:2.2'
}
常用注解
最常使用的主要是以下這幾個注解:
@Component
Annotates an interface or abstract class for which a fully-formed, dependency-injected implementation is to be generated from a set of modules(). The generated class will have the name of the type annotated with @Component prepended with Dagger. For example, @Component interface MyComponent {...} will produce an implementation named DaggerMyComponent.
@Module
Annotates a class that contributes to the object graph.
@Inject
Dagger constructs instances of your application classes and satisfies their dependencies. It uses the javax.inject.Inject annotation to identify which constructors and fields it is interested in.
Use @Inject to annotate the constructor that Dagger should use to create instances of a class. When a new instance is requested, Dagger will obtain the required parameters values and invoke this constructor.
@Provides
Annotates methods of a module to create a provider method binding. The method's return type is bound to its returned value. The component implementation will pass dependencies to the method as parameters.
Dagger2基本使用
最簡單的一個執行個體
首先寫一個Component
@Component(modules = MyApplicationModule.class)
public interface MyApplicationComponent {
// this should be an interface or abstract class
// write like this, and Make Project, then a DaggerMyApplicationComponent class will be generated
}
此時裡面的Module内容可以暫時為空:
@Module
public class MyApplicationModule {
}
寫好後make一下,就生成了
package com.ddmeng.dagger2sample.component;
import com.ddmeng.dagger2sample.module.MyApplicationModule;
import dagger.internal.Preconditions;
import javax.annotation.Generated;
@Generated(
value = "dagger.internal.codegen.ComponentProcessor",
comments = "https://google.github.io/dagger"
)
public final class DaggerMyApplicationComponent implements MyApplicationComponent {
private DaggerMyApplicationComponent(Builder builder) {
assert builder != null;
}
public static Builder builder() {
return new Builder();
}
public static MyApplicationComponent create() {
return builder().build();
}
public static final class Builder {
private Builder() {}
public MyApplicationComponent build() {
return new DaggerMyApplicationComponent(this);
}
/**
* @deprecated This module is declared, but an instance is not used in the component. This method is a no-op. For more, see https://google.github.io/dagger/unused-modules.
*/
@Deprecated
public Builder myApplicationModule(MyApplicationModule myApplicationModule) {
Preconditions.checkNotNull(myApplicationModule);
return this;
}
}
}
需要切換到project視圖下才能看見.
生成的這個實作,名字是在我們自己的Component名前面加了Dagger.
如果我們的類名不是頂級的,即還有外部類,則會以下劃線分隔連接配接.
現在我們的生成的myApplicationModule()方法被标記為
@Deprecated
,這是因為我的module裡面什麼都還沒有呢,是以被認為是沒有必要的.
現在我們添加一個要用的LogUtils類. 想要在MainActivity裡面用.
寫好LogUtils類,在構造函數上标記
@Inject
. 這時候就将LogUtils加入了dependency graph中, 相當于作為預備隊員.
想要在MainActivity作為一個字段用,
在Component裡面寫一句:
void inject(MainActivity activity);
因為此時還是沒有用到Module,是以在application裡面可以直接build,儲存component:
public class SampleApplication extends Application {
private MyApplicationComponent component;
@Override
public void onCreate() {
super.onCreate();
component = DaggerMyApplicationComponent.builder().build();
}
public MyApplicationComponent getComponent() {
return component;
}
}
在MainActivity使用的時候, 先get到Component, 然後調用inject()方法, 字段就被注入了.
public class MainActivity extends AppCompatActivity {
@Inject
LogUtils logUtils;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((SampleApplication) getApplication()).getComponent().inject(this);
logUtils.i("tag", "hi, I'm an instance of LogUtils");
}
}
運作程式後可以看到打出log,證明注入成功.
此時我們看到生成的代碼有三個類:
public final class DaggerMyApplicationComponent implements MyApplicationComponent {
public enum LogUtils_Factory implements Factory<LogUtils> {
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
可以通過檢視調用棧來看調用關系.
單例 @Singleton
@Singleton
如果我們想讓工具類是單例,隻需要在上面的基礎上,在類名前加上
@Singleton
.
此時對應的Component也需要加上
@Singleton
.否則編譯會不通過.
加好之後,可以列印hashCode()看出, 标記了
@Singleton
的這個對象,不論被注入幾次,都是同一個對象.
在我們的例子中, 可以讓FileUtils作為一個單例被注入:
@Singleton
public class FileUtils {
@Inject
public FileUtils() {
Log.i(LogUtils.TAG, "new FileUtils: " + hashCode());
}
public void doSomething() {
Log.i(LogUtils.TAG, "do sth with FileUtils " + hashCode());
}
}
檢視生成的代碼,可以看見
DaggerMyApplicationComponent
為單例的類多儲存了一個字段:
private Provider<FileUtils> fileUtilsProvider;
它在init的時候被初始化為:
this.fileUtilsProvider = ScopedProvider.create(FileUtils_Factory.create());
包了一層之後,在ScopeProvider裡實作了單例:
@Override
public T get() {
// double-check idiom from EJ2: Item 71
Object result = instance;
if (result == UNINITIALIZED) {
synchronized (this) {
result = instance;
if (result == UNINITIALIZED) {
instance = result = factory.get();
}
}
}
return (T) result;
}
@Module
@Provides
的使用
@Module
@Provides
上面的注入都是用
@Inject
, 在構造函數和要使用的字段上标記.
有些情況下
@Inject
是不能滿足需求的.
But @Inject doesn’t work everywhere.
- Interfaces can’t be constructed. 接口類型不能直接被構造.
- Third-party classes can’t be annotated. 第三方的類不能改動它的代碼.
- Configurable objects must be configured! 需要配置的對象需要被配置.
這些情況下,
@Inject
不夠用啦, 這時候就要用
@Provides
标記的方法.
方法的傳回值傳回了它滿足的依賴, 它實際傳回的對象可以是傳回值接口的實作,或者是傳回值類型的子類.
@Provides
方法也可以有依賴, 即它的參數.
Dagger會注入它的參數值, 如果它的參數值不能被注入, 則編譯會失敗.
注意這個尋找參數注入的過程是在
@Component
級别的, 隻要這個Component裡面有這個參數類型的注入, 即便可能是在另一個Module, 就會自動采用.
所有的
@Provides
方法都需要放在
@Module
裡面.
按照命名習慣(By convention), 一般
@Provides
标記的方法都有一個provide字首, 而module類都有一個Module字尾.
例子:
@Module
public class MyApplicationModule {
private Context context;
public MyApplicationModule(Context context) {
this.context = context;
}
@Provides
@Singleton
public Context providesContext() {
return context;
}
// Inject interface, return implementation class instance
@Provides
public HttpUtil provideHttpUtil() {
Log.i(LogUtils.TAG, "provideHttpUtil");
return new MyHttpUtil();
}
// Inject class from third-party, or Android framework service
// This provide method need a parameter, Dagger will obtain the parameter value (injected it)
// If the parameter is not injectable, then compilation failed
@Provides
@Singleton
ConnectivityManager provideConnectivityManager(Context context) {
return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
}
}
Dagger2中幾種常用注解總結
@Inject
@Inject
@Inject
的用法分為三種
- 構造函數上的
:@Inject
如果構造函數是有參數的, 則它的所有參數都會自動從dependency graph中找到并注入.
同時構造的這個類也被作為dependency graph的一部分.
但是我們在一個類中最多隻能用
@Inject
标記一個構造方法.
- 字段上的
: 從dependency graph中找到并注入字段.@Inject
這裡需要手動調用
(SampleApplication) getApplication()).getComponent().inject(this);
類似的方法, 在這個方法被調用之前, 字段都是null.
注意這裡的字段不能是private的.
- public方法上的
@Inject
所有方法的參數都會由dependency graph提供.
方法注入在構造函數之後立即調用, 意味着我們可以用一個建構好的this對象.
@Module
@Provides
@Module
@Provides
@Module
标記了提供依賴的類, 其中包含了一些
@Provides
标注的方法, 傳回值即依賴.
@Component
@Component
用
@Component
标記的接口負責将所有的事情聯系起來, 可以看做是
@Module
@Inject
之間的橋梁.
我們可以定義我們用的依賴來自哪些Module或者Component.
在Component裡可以定義哪些依賴是公有的 (提供傳回值為某種依賴的無參數方法) , 也可以定義我們的component可以去哪裡inject對象 (void inject()方法, 參數是去注入的地方) .
@Component
可以有自己的子Component, 也可以有lifecycle.
先就這麼多吧, 更多更進階的使用可以期待下文, 也可以參見後面的參考資料.
本文位址: Using Dagger2 in Android
本文Demo: dagger2-sample
參考資料:
dagger2 repo
dagger2 website
User Guide
Dagger 2.0文檔
dagger 2的sample
這裡有些guides:
Code Path Guides: DI with dagger2
Dagger2
這裡有一系列關于Dagger2的文章還挺好的:
Froger_mcs dev blog
dagger 1 to dagger 2 migration
Introduction to DI
Dagger2 API
Inject everything - ViewHolder and Dagger2 (with Multibinding and AutoFactory example)
Custom Scope
作者的例子:
Github Client
作者: 聖騎士Wind
出處: 部落格園: 聖騎士Wind
Github: https://github.com/mengdd
微信公衆号: 聖騎士Wind
