注:本文是小生自學(xué)時(shí)做(fanyi)的筆記,可能含有少兒不宜的誤導(dǎo)性內(nèi)容,學(xué)習(xí)Dagger請(qǐng)移步原博。
原博地址:http://frogermcs.github.io/
Dagger2使用:
需求:主頁(yè)上有一個(gè)TextView,在TextView中顯示人名和年齡
使用Dagger2做依賴注入,需要?jiǎng)?chuàng)建兩樣?xùn)|西:
1.Component接口: 想象成是注射器的針管,單詞的含義貌似跟作用完全沒(méi)關(guān)系啊,待我查查看。Component類使用 @Component(modules = {xxx.class}) 注解。
UserComponent:
@Component(modules = {UserModule.class})
public interface UserComponent {
void inject(MainActivity mainActivity);
}
2.Module: 想象是注射器的針筒。注意,Module不是提供的依賴(注射液),而是提供依賴的組件(針筒)。Module類使用 @Module 注解,提供依賴的方法用 @Provides 標(biāo)識(shí),方法名以 provide 開(kāi)頭。
@Module
public class UserModule {
UserModule() {}
@Provides
UserModel provideUsers() {
UserModel user = new UserModel();
user.setName("lala");
user.setAge(18);
return user;
}
}
以上就是我們?yōu)榱耸褂肈agger需要額外添加的東西,針筒加針管,還缺少:1.注射器的活塞。2.需要注射的液體。3.接受注射的病人。
1.注射器的活塞
也就是用來(lái)真正實(shí)施注入的東西。Dagger會(huì)根據(jù)注解生成一個(gè)實(shí)現(xiàn)了MembersInjector接口的類。MembersInjector直譯過(guò)來(lái)就是成員注射器,我們把它看成是注射器的活塞。此接口只有一個(gè)方法,就是 void injectMembers(T instance)
.調(diào)用生成類的 injectMembers 方法即相當(dāng)于推動(dòng)活塞的動(dòng)作,真正實(shí)施注射。對(duì)于這個(gè)東西我們不需要添加額外的代碼,它由Dagger在預(yù)編譯時(shí)期自動(dòng)生成。
2.需要注射的液體
這就是我們需要注入的依賴了。它可以是任何可以實(shí)例化的東西。
UserModel:
//UserModel沒(méi)有添加任何額外的東西
public class UserModel {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
3.接受注射的病人
也就是接收依賴的需求者。我們用 @inject 注解來(lái)標(biāo)識(shí)。
MainActivity:
//需要注入的依賴使用 @Inject 標(biāo)記
public class MainActivity extends AppCompatActivity {
private TextView textView;
@Inject
UserModel user;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.text_view);
//進(jìn)行真正的注入操作
DaggerUserComponent.builder()
.userModule(new UserModule())
.build()
.inject(this);
textView.setText("Name:" + user.getName() + "::Age:" + user.getAge());
}
}
效果:
咦,這么看起來(lái)整個(gè)實(shí)現(xiàn)還是比較麻煩的。直接在MainActivity new一個(gè)user出來(lái)不就完了,為什么要用Dagger繞這么大一圈。
先看看什么是依賴。
在MainActivity需要一個(gè)UserModel的實(shí)例,此時(shí),我們稱MainActivity對(duì)UserModel有依賴,如果我們要改UserModel,比如在構(gòu)造函數(shù)里就傳進(jìn)去姓名和年齡,而不通過(guò)set方法。此時(shí),我們就要更改MainActivity中的代碼,這里的MainActivity和UserModel是高耦合的。
而使用Dagger后,即使UserModel有了改變,我們只要改一下Module里的provide方法即可,而不用動(dòng)MainActivity的代碼,此時(shí),MainActivity和UserModel是低耦合的。真真是在平坦的路面上曲折前行啊...
上面是使用Dagger做一個(gè)最基本的依賴注入。下面來(lái)詳解一下各個(gè)部分。
- @Inject 注解
構(gòu)造函數(shù)注入
public class LoginActivityPresenter {
private LoginActivity loginActivity;
private UserDataStore userDataStore;
private UserManager userManager;
@Inject
public LoginActivityPresenter(LoginActivity loginActivity,
UserDataStore userDataStore,
UserManager userManager) {
this.loginActivity = loginActivity;
this.userDataStore = userDataStore;
this.userManager = userManager;
}
}
直接在構(gòu)造函數(shù)上加 @Inject 注解,所有的參數(shù)都從Module里獲取。同時(shí),這個(gè)類也可以被用于注入。如下:
public class LoginActivity extends BaseActivity {
@Inject
LoginActivityPresenter presenter;
//...
}
限制是一個(gè)類中只能有一個(gè)構(gòu)造函數(shù)有 @Inject 注解。
成員注入
public class SplashActivity extends AppCompatActivity {
@Inject
LoginActivityPresenter presenter;
@Inject
AnalyticsManager analyticsManager;
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
getAppComponent().inject(this);
}
}
也就是上面我們使用的注入方式,這種方式比較好理解,不過(guò)需要手動(dòng)調(diào)用注入才能完成注入過(guò)程。注入前該成員一直都是null。
限制是被注入成員不能是private的,因?yàn)镈agger生成的代碼是這么注入的:
splashActivity.analyticsManager = analyticsManagerProvider.get();
這個(gè)后面分析生成代碼的時(shí)候會(huì)談到。
方法注入
public class LoginActivityPresenter {
private LoginActivity loginActivity;
@Inject
public LoginActivityPresenter(LoginActivity loginActivity) {
this.loginActivity = loginActivity;
}
@Inject
public void enableWatches(Watches watches) {
watches.register(this); //Watches instance required fully constructed LoginActivityPresenter
}
}
被注解的方法的所有參數(shù)都由Module提供。當(dāng)被注入方需要當(dāng)前類的實(shí)例(this)時(shí)可以用這種方式把自己傳給被注入方。注入方法會(huì)在構(gòu)造函數(shù)調(diào)用完畢后立馬被調(diào)用。
@Module 注解
用來(lái)標(biāo)注那些提供依賴的類,Dagger通過(guò)這個(gè)注解來(lái)找到提供依賴的類。
@Module
public class GithubApiModule {
@Provides
@Singleton
OkHttpClient provideOkHttpClient() {
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setConnectTimeout(60 * 1000, TimeUnit.MILLISECONDS);
okHttpClient.setReadTimeout(60 * 1000, TimeUnit.MILLISECONDS);
return okHttpClient;
}
@Provides
@Singleton
RestAdapter provideRestAdapter(Application application, OkHttpClient okHttpClient) {
RestAdapter.Builder builder = new RestAdapter.Builder();
builder.setClient(new OkClient(okHttpClient))
.setEndpoint(application.getString(R.string.endpoint));
return builder.build();
}
}
@Provides 注解
標(biāo)注返回依賴的方法。
@Module
public class GithubApiModule {
//...
@Provides //This annotation means that method below provides dependency
@Singleton
RestAdapter provideRestAdapter(Application application, OkHttpClient okHttpClient) {
RestAdapter.Builder builder = new RestAdapter.Builder();
builder.setClient(new OkClient(okHttpClient))
.setEndpoint(application.getString(R.string.endpoint));
return builder.build();
}
}
@Component 注解
標(biāo)注Module與Inject之間的橋梁接口。在這個(gè)接口中定義從哪個(gè)module獲取依賴,也用于定義那些Module可以用于注入,以及可以注入對(duì)象到哪里。
這個(gè)例子表示:當(dāng)前Component使用兩個(gè)Modules,可以注入依賴到GithubClientApplication,可以使三個(gè)依賴公開(kāi)可見(jiàn):
@Singleton
@Component(
modules = {
AppModule.class,
GithubApiModule.class
}
)
public interface AppComponent {
void inject(GithubClientApplication githubClientApplication);
Application getApplication();
AnalyticsManager getAnalyticsManager();
UserManager getUserManager();
}
Component也可以依賴別的Component,也可以有自己的生命周期(詳見(jiàn)下面的Scope)
@ActivityScope
@Component(
modules = SplashActivityModule.class,
dependencies = AppComponent.class
)
public interface SplashActivityComponent {
SplashActivity inject(SplashActivity splashActivity);
SplashActivityPresenter presenter();
}
@Scope 注解
@Scope
public @interface ActivityScope {
}
用于定義自定義作用域注解。有點(diǎn)類似于單例,不同的是,單例的作用域是整個(gè)application,而自定義作用域可以自定義(特么廢話么)。下面還會(huì)詳解Scope,這里按下不表。先說(shuō)好,Scope是拿來(lái)干這個(gè)的:保持對(duì)象的單一實(shí)例。
然后附贈(zèng)一些不咋用,不太重要的東西:
@MapKey 注解
用于定義依賴的集合。
定義:
@MapKey(unwrapValue = true)
@interface TestKey {
String value();
}
提供依賴:
@Provides(type = Type.MAP)
@TestKey("foo")
String provideFooKey() {
return "foo value";
}
@Provides(type = Type.MAP)
@TestKey("bar")
String provideBarKey() {
return "bar value";
}
使用:
@Inject
Map<String, String> map;
map.toString() // => ?{foo=foo value, bar=bar value}”
@Qualifier 注解
給高階程序猿(強(qiáng)迫癥)準(zhǔn)備的,用于為繼承自同一個(gè)接口的依賴打 TAG 來(lái)區(qū)分他們。比如有兩個(gè)Adapter繼承自同一個(gè)Adapter接口,你還想讓代碼看上去整齊有型,就這么干。
命名依賴:
@Provides
@Singleton
@GithubRestAdapter //Qualifier
RestAdapter provideRestAdapter() {
return new RestAdapter.Builder()
.setEndpoint("https://api.github.com")
.build();
}
@Provides
@Singleton
@FacebookRestAdapter //Qualifier
RestAdapter provideRestAdapter() {
return new RestAdapter.Builder()
.setEndpoint("https://api.facebook.com")
.build();
}
注入依賴:
@Inject
@GithubRestAdapter
RestAdapter githubRestAdapter;
@Inject
@FacebookRestAdapter
RestAdapter facebookRestAdapter;
@Singleton 注解(Java自帶)
Dagger不單可以注入實(shí)例,還可以注入單例。當(dāng)年老大要咱們做單元測(cè)試,結(jié)果因?yàn)轫?xiàng)目里各種單例用得太亂導(dǎo)致大量代碼處于不可測(cè)的狀態(tài)。一開(kāi)始注意到Dagger也是因?yàn)橛辛薉agger,就不需要寫(xiě)那種很難Mock對(duì)象的單例了。
我們更改一下一開(kāi)始的示例的界面,加一個(gè)TextView,MainActivity如下:
public class MainActivity extends AppCompatActivity {
private TextView textView1;
private TextView textView2;
@Inject
UserModel user1;
@Inject
UserModel user2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView1 = (TextView) findViewById(R.id.text_view_1);
textView2 = (TextView) findViewById(R.id.text_view_2);
DaggerUserComponent.builder()
.userModule(new UserModule())
.build()
.inject(this);
user2.setName("Stark");
user2.setAge(10);
textView1.setText("Name:" + user1.getName() + "::Age:" + user1.getAge());
textView2.setText("Name:" + user2.getName() + "::Age:" + user2.getAge());
}
}
可以看到這里我注入了兩個(gè)UserModel的實(shí)例,在注入后重新設(shè)置user2的name和Age,然后顯示在兩個(gè)TextView上。結(jié)果如圖:
可以看到,上面的user1還是我在Module中創(chuàng)建的UserModel實(shí)例,user2是另一個(gè)獨(dú)立的對(duì)象,所以它們之間互不干擾。
好,下面我們改動(dòng)一丟丟Component和Module的代碼,如下:
@Component(modules = {UserModule.class})
@Singleton
public interface UserComponent {
void inject(MainActivity mainActivity);
}
@Module
public class UserModule {
UserModule() {}
@Provides
@Singleton
UserModel provideUsers() {
UserModel user = new UserModel();
user.setName("lala");
user.setAge(18);
return user;
}
}
為Component加了 @Singleton 的類注解,為Module創(chuàng)建相應(yīng)實(shí)例的方法加了同樣的 @Singleton 注解,再運(yùn)行一下看看。
恩,就是這么簡(jiǎn)單,我只更改了user2的數(shù)據(jù),結(jié)果user1也更改了,因?yàn)樗鼈冎赶蛲粋€(gè)對(duì)象。就這樣,成功使用Dagger注入了單例。且這個(gè)單例的代碼是可測(cè)的,因?yàn)樗膶?shí)例很容易Mock。
Scope 詳解
Scope,直譯過(guò)來(lái)是范圍、圈子的意思。就像前面提到的,在Dagger中,Scope用來(lái)保證在指定作用域中,拿到的依賴對(duì)象是唯一的(指定范圍內(nèi)是單例)。比如,我有一個(gè)登錄系統(tǒng),用戶登錄后,一直到登出前,拿到的UserModel就應(yīng)該是單例。此時(shí),我可以自定義一個(gè)UserScope的作用域,即實(shí)現(xiàn)一個(gè) @UserScope 注解,用此注解標(biāo)識(shí)的Component在指定的范圍內(nèi)(從用戶登錄到用戶登出)注入的一定是同一個(gè)實(shí)例。有沒(méi)有很爽的感覺(jué)。。。
Dagger是沒(méi)有默認(rèn)自帶的各種Scope的,什么 @ActivityScope 啊,@ApplicationScope 啊統(tǒng)統(tǒng)沒(méi)有,只有Java自帶的一個(gè)@Singleton,也就是上面講到的那個(gè),它與Apllication處于同一作用域,從App開(kāi)啟到結(jié)束只有一個(gè)實(shí)例。那么下面我們來(lái)看看如何自定義一個(gè) Scope 注解。
Scope的實(shí)現(xiàn)在Dagger2里頭就是對(duì)Component做正確的配置工作。有兩種方式實(shí)現(xiàn)自定義Scope,使用 @Subcomponent 注解 或 使用 Components 依賴。這兩種實(shí)現(xiàn)方式最終出來(lái)的結(jié)果是不同的,使用 @Subcomponent 注解的話,子域可以獲取到父域的所有依賴;而如果使用 Components 依賴,則子域只能獲取到父域通過(guò)Component接口暴露出來(lái)的依賴。
先來(lái)看看使用 @Subcomponent 注解的方式實(shí)現(xiàn):
@Singleton
@Component(
modules = {
AppModule.class,
GithubApiModule.class
}
)
public interface AppComponent {
UserComponent plus(UserModule userModule);
SplashActivityComponent plus(SplashActivityModule splashActivityModule);
}
可以看到,在AppComponent接口中有兩個(gè)plus方法,這兩個(gè)方法的意思是,我們可以從APPComponent中創(chuàng)建兩個(gè)子Component: UserComponent 和 SplashActivityComponent. 因?yàn)樗鼈兪茿ppComponent的子Component,所以都可以獲取到AppModule和GithubApiModule產(chǎn)生的實(shí)例。
規(guī)則:返回類型是子Component類型,方法名任性著來(lái),參數(shù)是子Component所需的就行。
可以看到,UserComponent需要另一個(gè)Module,該Module被當(dāng)做plus方法的參數(shù)傳入。這樣,我們我們用新Module產(chǎn)生的額外對(duì)象拓展了AppComponent可注入的依賴表。UserComponent如下:
@UserScope
@Subcomponent(
modules = {
UserModule.class
}
)
public interface UserComponent {
RepositoriesListActivityComponent plus(RepositoriesListActivityModule repositoriesListActivityModule);
RepositoryDetailsActivityComponent plus(RepositoryDetailsActivityModule repositoryDetailsActivityModule);
}
@UserScope 注解肯定是自定義的咯:
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface UserScope {
}
在UserComponent中,可以創(chuàng)建另外兩個(gè)子Component: RepositoriesListActivityComponent 和 RepositoryDetailsActivityComponent.
重要的東西在這兒呢。所有從UserComponent拿到的從AppComponent繼承下來(lái)的實(shí)例都是單例(范圍是Application)。而那些UserModule自己產(chǎn)生的實(shí)例都是“本地單例”,其周期就是UserComponent存在的周期。
所以,每次我們調(diào)用UserComponent userComponent = appComponent.plus(new UserComponent(user));
時(shí),從userComponent拿到的對(duì)象都是不同的實(shí)例。
對(duì)于這個(gè)我們自定義的UserScope,它的生命周期當(dāng)然也得咱們自己維護(hù),要維護(hù)它的初始化和銷毀。
public class GithubClientApplication extends Application {
private AppComponent appComponent;
private UserComponent userComponent;
//...
public UserComponent createUserComponent(User user) {
userComponent = appComponent.plus(new UserModule(user));
return userComponent;
}
public void releaseUserComponent() {
userComponent = null;
}
//...
}
當(dāng)我們從網(wǎng)絡(luò)獲取到User對(duì)象時(shí),調(diào)用createUserComponent方法,UserScope從此時(shí)開(kāi)始。當(dāng)RepositoriesListActivity finish時(shí),調(diào)用releaseUserComponent終結(jié)UserScope。
到此為止,對(duì)于Dagger的使用已經(jīng)學(xué)習(xí)地差不多了。但是只知道怎么用怎么能滿足咱們欲求不滿的內(nèi)心呢?所以下一篇要賞析一下Dagger自動(dòng)生成的代碼,來(lái)看一下整個(gè)注入流程是如何走通的。