前言
繼上篇文章,這篇我們來學(xué)習(xí)Dagger2的高級(jí)用法。如果沒看上篇文章的話最好先看下上篇文章再來學(xué)習(xí)本章,因?yàn)楸菊率墙永m(xù)上篇文章來寫的,直接來看的話可能效果不是很好,Dagger2入門-基本使用(上)。
注解
- @Qualifier: 要作用是用來區(qū)分不同對(duì)象實(shí)例
@Named 其實(shí)是@Qualifier的一種實(shí)現(xiàn) - Scope
- Subcomponent
- Lazy與Provider
@Qualifier
上面也提到了,作用是用來區(qū)分不同對(duì)象的實(shí)例的。平時(shí)我們可能會(huì)對(duì)一個(gè)類創(chuàng)建不同的構(gòu)造方法以滿足不同的需求,假設(shè)現(xiàn)在現(xiàn)在ApiSevice有2個(gè)構(gòu)造方法,根據(jù)不同情況調(diào)用不同方法。這時(shí)就要用到@Named標(biāo)簽(@Named是@Qualifier的一種實(shí)現(xiàn)).
public class ApiService {
public ApiService(Context context) {
}
public ApiService(String url) {
Log.d("TAG", "ApiService: " + url);
}
public void register() {
Log.d("TAG", "ApiService: ");
}
}
可以看到ApiService有2個(gè)不同的構(gòu)造方法,并且參數(shù)不同。下面我們就要在UserModule分別創(chuàng)建這兩個(gè)對(duì)象的實(shí)例。
@Module
public class UserModule {
Context context;
public UserModule(Context context) {
this.context = context;
}
@Provides
@Named("dev")
public ApiService provideApiServiceDev(String url) {
ApiService apiService = new ApiService(url);
Log.d("TAG", "provideApiServiceDev: " + apiService);
return apiService;
}
@Provides
@Named("release")
public ApiService provideApiServiceRelease() {
ApiService apiService = new ApiService(context);
Log.d("TAG", "provideApiServiceRelease: " + apiService);
return apiService;
}
@Provides
public Context provideContext() {
return context;
}
@Provides
public String providerUrl() {
return "www.baidu.com";
}
@Provides
public UserManager provideUserManager(ApiService apiService, UserStroe userStroe) {
return new UserManager(userStroe, apiService);
}
}
可以看到我們?yōu)锳piService分別提供了2個(gè)構(gòu)造方法但參數(shù)不同。然后分別用@Named("dev")和@Named("release")注解,表明這是2個(gè)不同的構(gòu)造方法。
(提問:這里為什么我們可以直接引用參數(shù)參數(shù)中的context和url呢?因?yàn)槲覀兲峁┝藀roviderUrl()和provideContext()所以可以直接使用)
那么我們看下MainActivity中如何調(diào)用的:
@Named("dev")
@Inject
ApiService mApiService;
@Named("release")
@Inject
ApiService mApiService1;
private boolean is_Dev = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerUserComponet.builder().userModule(new UserModule(this)).build().inject(this);
Log.d("TAG", "mApiService= " + mApiService);
Log.d("TAG", "mApiService1= " + mApiService1);
if (is_Dev) {
mApiService.register();
} else {
mApiService1.register();
}
}
}
可以看到我們?cè)趍odle中用@Named區(qū)分,在使用是只需在 @Inject時(shí)添加上@Named注解就會(huì)創(chuàng)建對(duì)應(yīng)注解的實(shí)例,然后我們用一個(gè)is_Dev標(biāo)簽,表明不同情況使用不同的對(duì)象。我們來看下打印結(jié)果:
D/TAG: ApiService: www.baidu.com
D/TAG: provideApiServiceDev: .ApiService@4a7c44c8
D/TAG: provideApiServiceRelease: .ApiService@4a7c477c
D/TAG: mApiService= .ApiService@4a7c44c8
D/TAG: mApiService1= .ApiService@4a7c477c
注意:我們?cè)贛oudle用了@Named標(biāo)簽,在調(diào)用時(shí)也需要加上@Named標(biāo)簽,如果在調(diào)用處不使用@Named注解就需要在Moudle中創(chuàng)建對(duì)應(yīng)沒有用@Named注解的實(shí)例方法
通過字符串標(biāo)記一個(gè)對(duì)象,容易導(dǎo)致前后不匹配,所以除了使用這種方法,我們還可以通過自定義注解的方式。
那么如何實(shí)現(xiàn)自定義注解@Qualifier呢?很簡單,@Named就是@Qualifier的一種實(shí)現(xiàn),我看他是怎么實(shí)現(xiàn)的,我們就照葫蘆畫瓢被:
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
/** The name. */
String value() default "";
}
@Qualifier :注明是Qualifier(關(guān)鍵詞)
@Documented :標(biāo)記在文檔
@Retention(RUNTIME) :運(yùn)行時(shí)級(jí)別
可以看到@Qualifier決定它關(guān)鍵性的作用是用來區(qū)分不同對(duì)象的。里面的String類型就是我們之前填寫的("dev")和("release")用來區(qū)分的,那么我也照著寫我們的自定義的標(biāo)簽:
@Qualifier
@Retention(RUNTIME)
public @interface Dev {
}
@Qualifier
@Retention(RUNTIME)
public @interface Release {
}
我們創(chuàng)建了2個(gè)自定義注解,當(dāng)然你全賦值過來也是沒有問題的,這里我去掉了不必要的部分。因?yàn)楸旧砦覀兙筒幌胗米址畢^(qū)分了,所以我把字符串參數(shù)去掉了。
@Provides
@Release
public ApiService provideApiServiceDev(String url) {
ApiService apiService = new ApiService(url);
Log.d("TAG", "provideApiServiceDev: " + apiService);
return apiService;
}
@Provides
@Dev
public ApiService provideApiServiceRelease() {
ApiService apiService = new ApiService(context);
Log.d("TAG", "provideApiServiceRelease: " + apiService);
return apiService;
}
@Dev
@Inject
ApiService mApiService;
@Release
@Inject
ApiService mApiService1;
用法和上面是結(jié)果是一樣的。
@Singleton
我們先看下他的里面是什么樣子的:
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
友情提示:我剛學(xué)習(xí)的時(shí)候就總搞不懂總以為@Scope,@Singleton,@Qualifier,@Named是4個(gè)不同作用的操作符,其實(shí)他就是兩兩一對(duì)的,@Named是@Qualifier具體實(shí)現(xiàn),@Singleton是@Scope的具體實(shí)現(xiàn);@Scope和@Qualifier相當(dāng)于不同作用注解的關(guān)鍵字
他就是用@Scope注解修飾的注解。這樣我們就知道了。那么首先我們來看下,如果不用單例什么樣的:
@Inject
ApiService mApiService1;
@Inject
ApiService mApiService2;
private boolean is_Dev = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerUserComponet.builder().userModule(new UserModule(this)).build().inject(this);
Log.d("TAG", "mApiService1= " + mApiService1);
Log.d("TAG", "mApiService2= " + mApiService2);
}
}
結(jié)果:
D/TAG: mApiService1= .ApiService@4a7c7904
D/TAG: mApiService2= .ApiService@4a7c7aa8
可以看到確實(shí)創(chuàng)建了2個(gè)實(shí)例,那么如何使用單例注解:
首先在Module中將創(chuàng)建實(shí)例的方法加上@Singleton
@Singleton
@Provides
public ApiService provideApiService() {
ApiService apiService = new ApiService(context);
Log.d("TAG", "provideApiService: " + apiService);
return apiService;
}
然后在@Component中也加上@Singleton:
@Singleton
@Component(modules = {UserModule.class})
public interface UserComponet {
void inject(MainActivity activity);
}
@Singleton在使用時(shí)調(diào)用處正常書寫:
@Inject
ApiService mApiService1;
@Inject
ApiService mApiService2;
打印結(jié)果:
provideApiService: com.example.ggxiaozhi.dagger2.ApiService@4a7c5200
mApiService1= com.example.ggxiaozhi.dagger2.ApiService@4a7c5200
mApiService2= com.example.ggxiaozhi.dagger2.ApiService@4a7c5200
可以看到只調(diào)用了一次創(chuàng)建對(duì)象的方法。2個(gè)對(duì)象其實(shí)是一個(gè)實(shí)例。
注意:
- module 的 provide 方法使用了 scope ,那么 component 就必須使用同一個(gè)注解
- @Singleton 的生命周期依附于component,同一個(gè)module被不同的@Component依賴結(jié)果也不一樣
- @Singleton分為Activity級(jí)別單例生命周期和全局的生命周期單例
這里第一點(diǎn)注意我們通過上面的事例比較容易理解,那么第二點(diǎn)是什么意思呢?這句話的意思在于@Singleton 的生命周期依附于component。那么實(shí)際測試下。我們?cè)趧?chuàng)建一個(gè)LoginAcyivity,然后MainActivity創(chuàng)建對(duì)象后直接跳轉(zhuǎn)LoginAcyivity。并創(chuàng)建LogingConponent如下:
@Singleton
@Component(modules = UserModule.class)
public interface LoginComponent {
void inject(LoginActivity activity);
}
LogingConponent也依賴UserModule.class。然后在LoginAcyivity中創(chuàng)建ApiService如下:
public class LoginActivity extends AppCompatActivity {
@Inject
ApiService mApiService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
DaggerLoginComponent.builder().userModule(new UserModule(this)).build().inject(this);
Log.d("TAG", "LoginActivity-->mApiService : "+mApiService);
}
}
運(yùn)行結(jié)果:
D/TAG: provideApiService: com.example.ggxiaozhi.dagger2.ApiService@4a7c5994
D/TAG: MainActivity-->mApiService1= com.example.ggxiaozhi.dagger2.ApiService@4a7c5994
D/TAG: MainActivity-->mApiService2= com.example.ggxiaozhi.dagger2.ApiService@4a7c5994
D/TAG: provideApiService: com.example.ggxiaozhi.dagger2.ApiService@4a7d54f8
D/TAG: LoginActivity-->mApiService : com.example.ggxiaozhi.dagger2.ApiService@4a7d54f8
可以看你到LoginComponent和UserComponent都依賴UserMoudle,并且創(chuàng)建ApiService已經(jīng)加了@Singleton注解但是在MainActivity中是單例但是在LoginActivity又創(chuàng)建了不同的ApiService的實(shí)例,這就是上面提到的因?yàn)長oginComponent和UserComponent為兩個(gè)不同的@Component,@Singleton的生命周期依附于component,同一個(gè)module provide singleton ,不同component 也是不一樣。所以會(huì)看到這樣的結(jié)果。如果我們修改下代碼呢?如下:
@Singleton
@Component(modules = {UserModule.class})
public interface UserComponet {
void inject(MainActivity activity);
void inject(LoginActivity activity);
}
然后在LoginActivity中也引用UserComponent而不去引用LogingComponent呢?
public class LoginActivity extends AppCompatActivity {
@Inject
ApiService mApiService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
UserComponet userComponet = DaggerUserComponet.builder().userModule(new UserModule(this)).build();
userComponet.inject(this);
Log.d("TAG", "LoginActivity-->mApiService : "+mApiService);
Log.d("TAG", "LoginActivity-->UserComponet : "+userComponet);
}
}
這也也是不行的。為什么?看打印結(jié)果:
D/TAG: provideApiService: com.example.ggxiaozhi.dagger2.ApiService@4a7c454c
D/TAG: MainActivity-->mApiService1= com.example.ggxiaozhi.dagger2.ApiService@4a7c454c
D/TAG: MainActivity-->mApiService2= com.example.ggxiaozhi.dagger2.ApiService@4a7c454c
D/TAG: MainActivity-->UserComponet= com.example.ggxiaozhi.dagger2.DaggerUserComponet@4a7c382c
D/TAG: provideApiService: com.example.ggxiaozhi.dagger2.ApiService@4a7d3ccc
D/TAG: LoginActivity-->mApiService : com.example.ggxiaozhi.dagger2.ApiService@4a7d3ccc
D/TAG: LoginActivity-->UserComponet : com.example.ggxiaozhi.dagger2.DaggerUserComponet@4a7d3c9c
可以看到,在UserComponet在LoginActivity和MainActivity中會(huì)創(chuàng)建2個(gè)不同的實(shí)例,當(dāng)然會(huì)創(chuàng)建2個(gè)不同的mApiService了。如果想實(shí)現(xiàn)全局單例就要用到自定義@Scope注解。
自定義@Scope注解
上面是屬于Activity生命周期單例。下面我們就創(chuàng)建全局生命周期單例。
1. 創(chuàng)建全局AppModule:
@Module
public class AppMoudle {
private MyApplication context;
public AppMoudle(MyApplication context) {
this.context = context;
}
@Singleton
@Provides
public ApiService provideApiService() {
ApiService apiService = new ApiService(context);
Log.d("TAG", "provideApiService: " + apiService);
return apiService;
}
}
2. 創(chuàng)建全局AppComponent:
@Singleton
@Component(modules = AppMoudle.class)
public interface AppComponent {
/**
* 全局單例。所以不用Inject Activity
*
* @return 向下返回ApiService實(shí)例
*/
ApiService getApiService();
}
3. 在MyApplication實(shí)例化AppComponent:
單例的依托于他所在的Component中,所以需要在Application中進(jìn)行實(shí)例化。
public class MyApplication extends Application {
private AppComponent mAppComponent;
@Override
public void onCreate() {
super.onCreate();
mAppComponent = DaggerAppComponent.builder().appMoudle(new AppMoudle(this)).build();
}
public AppComponent getAppComponent() {
return mAppComponent;
}
}
4.自定義@Scope:
@Scope
@Retention(RUNTIME)
public @interface PerActivity {
}
5. 讓其他Component依賴:
@PerActivity
@Component(modules = {UserModule.class},dependencies = AppComponent.class)
public interface UserComponet {
void inject(MainActivity activity);
}
@PerActivity
@Component(modules = UserModule.class,dependencies = AppComponent.class)
public interface LoginComponent {
void inject(LoginActivity activity);
}
6. 調(diào)用
MainActivity:
DaggerUserComponet.builder().
appComponent(((MyApplication)getApplicationContext()).getAppComponent()).
build().inject(this);
LoginActivity:
DaggerLoginComponent.builder().
appComponent(((MyApplication)getApplicationContext()).getAppComponent()).
build().inject(this);
打印結(jié)果:
D/TAG: provideApiService: com.example.ggxiaozhi.dagger2.ApiService@4a7c3e8c
D/TAG: MainActivity-->mApiService1= com.example.ggxiaozhi.dagger2.ApiService@4a7c3e8c
D/TAG: MainActivity-->mApiService2= com.example.ggxiaozhi.dagger2.ApiService@4a7c3e8c
D/TAG: LoginActivity-->mApiService= com.example.ggxiaozhi.dagger2.ApiService@4a7c3e8c
可以看到這次全局都是用的一個(gè)單例了。
注意:
- 可以看到第4步我們自定義@Scope注解PerActivity,因?yàn)閏omponent的dependencies與component自身的scope不能相同,即組件之間的scope不同。所以我們自己定義。
- Singleton的組件不能依賴其他scope的組件,只能其他scope的組件依賴Singleton的組件 如下:
AppComponent已經(jīng)用@Singleton修飾就不能再去依賴(dependencies=XXX.class)別的Component。
clipboard.png -
但是其他scope的組件 可以依賴其他組件:
clipboardd.png
@Subcomponent
作用有些類似Component中的dependencies作用。特點(diǎn):
- Subcomponent同時(shí)具備兩種不同生命周期的scope, SubComponent具備了父Component擁有的Scope,也具備了自己的Scope。
- SubComponent的Scope范圍小于父Component
我們用代碼使用體會(huì)下:
FComponent
//第一步
@Module
public class FModule {
@Singleton
@Provides
public User provideUser() {
return new User();
}
}
//第二步
@Singleton
@Component(modules = FModule.class)
public interface FComponent {
//需要將SubComponent 追加到被依賴的Component中
CComponent addCComponent();
}
//第三步
public class MyApplication extends Application {
private AppComponent mAppComponent;
private FComponent mFComponent;
@Override
public void onCreate() {
super.onCreate();
mAppComponent = DaggerAppComponent.builder().appMoudle(new AppMoudle(this)).build();
mFComponent = DaggerFComponent.builder().build();
}
public AppComponent getAppComponent() {
return mAppComponent;
}
public FComponent getFComponent() {
return mFComponent;
}
}
CComponent:
@Module
public class CModule {
@PerActivity
@Provides
public UserStroe provideUser(User user) {
return new UserStroe(user);
}
}
@PerActivity
@Subcomponent(modules = CModule.class)
public interface CComponent {
void Inject(Main2Activity activity);
}
調(diào)用:
public class Main2Activity extends AppCompatActivity {
private static final String TAG = "Main2Activity";
@Inject
UserStroe mUserStroe;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
((MyApplication)getApplicationContext()).getFComponent().getCComponent().Inject(this);
Log.d(TAG, "onCreate: "+mUserStroe);
}
}
首先我們先創(chuàng)建FComponent,他屬于App級(jí)別的。我們?cè)贛yApplication創(chuàng)建它。FComponent中調(diào)用者提供CComponent。然后有CComponent。這和我們之前使用有些不同。之前我們都是通過Avtivity級(jí)別創(chuàng)建,然后填入App級(jí)別的參數(shù)。這個(gè)使用正好相反。優(yōu)勢(shì):
不需要在被依賴的Component顯示提供依賴
不需要使用更多的DaggerXXXXComponent對(duì)象來創(chuàng)建依賴,僅需要在被依賴Component中增加 XXXComponent addXXComponent(XXXModule) 方法
這個(gè)如果不太理解也沒有關(guān)系,會(huì)使用就行。
懶加載Lazy和強(qiáng)制重新加載Provider
這個(gè)比較簡單,我就列舉一個(gè)簡單的例子。
public class Container{
@Inject Lazy<User> lazyUser; //注入Lazy元素
@Inject Provider<User> providerUser; //注入Provider元素
public void init(){
DaggerComponent.create().inject(this);
User user1=lazyUser.get();
//在這時(shí)才創(chuàng)建user1,以后每次調(diào)用get會(huì)得到同一個(gè)user1對(duì)象
User user2=providerUser.get();
//在這時(shí)創(chuàng)建user2,以后每次調(diào)用get會(huì)再強(qiáng)制調(diào)用Module的Provides方法一次,
//根據(jù)Provides方法具體實(shí)現(xiàn)的不同,可能返回跟user2是同一個(gè)對(duì)象,也可能不是。
}
}
注意事項(xiàng)(重要)分析
- componet 的 inject 方法接收父類型參數(shù),而調(diào)用時(shí)傳入的是子類型對(duì)象則無法注入
- component關(guān)聯(lián)的modules中不能有重復(fù)的provide
- module 的 provide 方法使用了 scope ,那么 component 就必須使用同一個(gè)注解
- module 的 provide 方法沒有使用 scope ,那么 component 和 module 是否加注解都無關(guān)緊要,可以通過編譯
- component的dependencies與component自身的scope不能相同,即組件之間的scope不同
- Singleton的組件不能依賴其他scope的組件,只能其他scope的組件依賴Singleton的組件
- 沒有scope的component不能依賴有scope的component
- 一個(gè)component不能同時(shí)有多個(gè)scope(Subcomponent除外)
- @Singleton 的生命周期依附于component,同一個(gè)module provide singleton ,不同component 也是不一樣
- Component注入的Activity 在其他Component中不能再去注入
- dagger2是跟著生命周期的綁定Activity(Fragment)onDestory 對(duì)象也會(huì)銷毀
- 創(chuàng)建實(shí)例的方法和引用實(shí)例都不能用private修飾
剛開始使用一定總會(huì)遇到很多錯(cuò)誤,遇到錯(cuò)誤不要著急。如果注意事項(xiàng)中的錯(cuò)誤沒有犯的話一定會(huì)減少很多錯(cuò)誤。
結(jié)語
終于寫完了。本來不我自己就不喜歡長文章。不知不覺寫的有點(diǎn)多。對(duì)我這種小白,看源碼寫博客。真的很費(fèi)心,學(xué)過的技術(shù)忘的很快,很多東西理解不透徹,想把博客寫好寫深還是很有難度的。不過如果你看到了這篇文章,希望有錯(cuò)誤很問題請(qǐng)留言一起探討。Dagger2也是在我用MVP構(gòu)建項(xiàng)目時(shí)候使用的,可能學(xué)習(xí)的不是很深入。不過相信把這兩篇文章的代碼敲一邊。平常的使用一定沒有問題的。最后希望大家一起加油?。。?/p>

