前言
繼上篇文章,這篇我們來學習Dagger2的高級用法。如果沒看上篇文章的話最好先看下上篇文章再來學習本章穆役,因為本章是接續(xù)上篇文章來寫的,直接來看的話可能效果不是很好梳凛,Dagger2入門-基本使用(上)耿币。
注解
- @Qualifier: 要作用是用來區(qū)分不同對象實例
@Named 其實是@Qualifier的一種實現 - Scope
- Subcomponent
- Lazy與Provider
@Qualifier
上面也提到了,作用是用來區(qū)分不同對象的實例的韧拒。平時我們可能會對一個類創(chuàng)建不同的構造方法以滿足不同的需求淹接,假設現在現在ApiSevice有2個構造方法十性,根據不同情況調用不同方法。這時就要用到@Named標簽(@Named是@Qualifier的一種實現).
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個不同的構造方法塑悼,并且參數不同劲适。下面我們就要在UserModule分別創(chuàng)建這兩個對象的實例。
@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個構造方法但參數不同厢蒜。然后分別用@Named("dev")和@Named("release")注解霞势,表明這是2個不同的構造方法。
(提問:這里為什么我們可以直接引用參數參數中的context和url呢斑鸦?因為我們提供了providerUrl()和provideContext()所以可以直接使用)
那么我們看下MainActivity中如何調用的:
@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();
}
}
}
可以看到我們在modle中用@Named區(qū)分支示,在使用是只需在 @Inject時添加上@Named注解就會創(chuàng)建對應注解的實例,然后我們用一個is_Dev標簽鄙才,表明不同情況使用不同的對象颂鸿。我們來看下打印結果:
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
注意:我們在Moudle用了@Named標簽,在調用時也需要加上@Named標簽攒庵,如果在調用處不使用@Named注解就需要在Moudle中創(chuàng)建對應沒有用@Named注解的實例方法
通過字符串標記一個對象嘴纺,容易導致前后不匹配,所以除了使用這種方法浓冒,我們還可以通過自定義注解的方式栽渴。
那么如何實現自定義注解@Qualifier呢?很簡單稳懒,@Named就是@Qualifier的一種實現闲擦,我看他是怎么實現的,我們就照葫蘆畫瓢被:
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
/** The name. */
String value() default "";
}
@Qualifier :注明是Qualifier(關鍵詞)
@Documented :標記在文檔
@Retention(RUNTIME) :運行時級別
可以看到@Qualifier決定它關鍵性的作用是用來區(qū)分不同對象的场梆。里面的String類型就是我們之前填寫的("dev")和("release")用來區(qū)分的墅冷,那么我也照著寫我們的自定義的標簽:
@Qualifier
@Retention(RUNTIME)
public @interface Dev {
}
@Qualifier
@Retention(RUNTIME)
public @interface Release {
}
我們創(chuàng)建了2個自定義注解,當然你全賦值過來也是沒有問題的或油,這里我去掉了不必要的部分寞忿。因為本身我們就不想用字符串區(qū)分了,所以我把字符串參數去掉了顶岸。
@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;
用法和上面是結果是一樣的腔彰。
@Singleton
我們先看下他的里面是什么樣子的:
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
友情提示:我剛學習的時候就總搞不懂總以為@Scope,@Singleton辖佣,@Qualifier霹抛,@Named是4個不同作用的操作符,其實他就是兩兩一對的卷谈,@Named是@Qualifier具體實現杯拐,@Singleton是@Scope的具體實現;@Scope和@Qualifier相當于不同作用注解的關鍵字
他就是用@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);
}
}
結果:
D/TAG: mApiService1= .ApiService@4a7c7904
D/TAG: mApiService2= .ApiService@4a7c7aa8
可以看到確實創(chuàng)建了2個實例凸郑,那么如何使用單例注解:
首先在Module中將創(chuàng)建實例的方法加上@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在使用時調用處正常書寫:
@Inject
ApiService mApiService1;
@Inject
ApiService mApiService2;
打印結果:
provideApiService: com.example.ggxiaozhi.dagger2.ApiService@4a7c5200
mApiService1= com.example.ggxiaozhi.dagger2.ApiService@4a7c5200
mApiService2= com.example.ggxiaozhi.dagger2.ApiService@4a7c5200
可以看到只調用了一次創(chuàng)建對象的方法裳食。2個對象其實是一個實例。
注意:
- module 的 provide 方法使用了 scope 芙沥,那么 component 就必須使用同一個注解
- @Singleton 的生命周期依附于component诲祸,同一個module被不同的@Component依賴結果也不一樣
- @Singleton分為Activity級別單例生命周期和全局的生命周期單例
這里第一點注意我們通過上面的事例比較容易理解,那么第二點是什么意思呢而昨?這句話的意思在于@Singleton 的生命周期依附于component救氯。那么實際測試下。我們在創(chuàng)建一個LoginAcyivity歌憨,然后MainActivity創(chuàng)建對象后直接跳轉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);
}
}
運行結果:
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已經加了@Singleton注解但是在MainActivity中是單例但是在LoginActivity又創(chuàng)建了不同的ApiService的實例甲抖,這就是上面提到的因為LoginComponent和UserComponent為兩個不同的@Component,@Singleton的生命周期依附于component心铃,同一個module provide singleton ,不同component 也是不一樣准谚。所以會看到這樣的結果。如果我們修改下代碼呢去扣?如下:
@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);
}
}
這也也是不行的。為什么?看打印結果:
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中會創(chuàng)建2個不同的實例唆铐,當然會創(chuàng)建2個不同的mApiService了。如果想實現全局單例就要用到自定義@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實例
*/
ApiService getApiService();
}
3. 在MyApplication實例化AppComponent:
單例的依托于他所在的Component中澳盐,所以需要在Application中進行實例化。
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. 調用
MainActivity:
DaggerUserComponet.builder().
appComponent(((MyApplication)getApplicationContext()).getAppComponent()).
build().inject(this);
LoginActivity:
DaggerLoginComponent.builder().
appComponent(((MyApplication)getApplicationContext()).getAppComponent()).
build().inject(this);
打印結果:
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
可以看到這次全局都是用的一個單例了令宿。
注意:
- 可以看到第4步我們自定義@Scope注解PerActivity叼耙,因為component的dependencies與component自身的scope不能相同,即組件之間的scope不同粒没。所以我們自己定義筛婉。
- Singleton的組件不能依賴其他scope的組件,只能其他scope的組件依賴Singleton的組件 如下:
AppComponent已經用@Singleton修飾就不能再去依賴(dependencies=XXX.class)別的Component。
-
但是其他scope的組件 可以依賴其他組件:
@Subcomponent
作用有些類似Component中的dependencies作用爽撒。特點:
- Subcomponent同時具備兩種不同生命周期的scope, SubComponent具備了父Component擁有的Scope入蛆,也具備了自己的Scope。
- SubComponent的Scope范圍小于父Component
我們用代碼使用體會下:
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);
}
調用:
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級別的哨毁。我們在MyApplication創(chuàng)建它。FComponent中調用者提供CComponent源武。然后有CComponent扼褪。這和我們之前使用有些不同。之前我們都是通過Avtivity級別創(chuàng)建粱栖,然后填入App級別的參數话浇。這個使用正好相反。優(yōu)勢:
不需要在被依賴的Component顯示提供依賴
不需要使用更多的DaggerXXXXComponent對象來創(chuàng)建依賴闹究,僅需要在被依賴Component中增加 XXXComponent addXXComponent(XXXModule) 方法
這個如果不太理解也沒有關系幔崖,會使用就行。
懶加載Lazy和強制重新加載Provider
這個比較簡單渣淤,我就列舉一個簡單的例子岖瑰。
public class Container{
@Inject Lazy<User> lazyUser; //注入Lazy元素
@Inject Provider<User> providerUser; //注入Provider元素
public void init(){
DaggerComponent.create().inject(this);
User user1=lazyUser.get();
//在這時才創(chuàng)建user1,以后每次調用get會得到同一個user1對象
User user2=providerUser.get();
//在這時創(chuàng)建user2,以后每次調用get會再強制調用Module的Provides方法一次砂代,
//根據Provides方法具體實現的不同蹋订,可能返回跟user2是同一個對象,也可能不是刻伊。
}
}
注意事項(重要)分析
- componet 的 inject 方法接收父類型參數露戒,而調用時傳入的是子類型對象則無法注入
- component關聯的modules中不能有重復的provide
- module 的 provide 方法使用了 scope ,那么 component 就必須使用同一個注解
- module 的 provide 方法沒有使用 scope 捶箱,那么 component 和 module 是否加注解都無關緊要智什,可以通過編譯
- component的dependencies與component自身的scope不能相同,即組件之間的scope不同
- Singleton的組件不能依賴其他scope的組件丁屎,只能其他scope的組件依賴Singleton的組件
- 沒有scope的component不能依賴有scope的component
- 一個component不能同時有多個scope(Subcomponent除外)
- @Singleton 的生命周期依附于component荠锭,同一個module provide singleton ,不同component 也是不一樣
- Component注入的Activity 在其他Component中不能再去注入
- dagger2是跟著生命周期的綁定Activity(Fragment)onDestory 對象也會銷毀
- 創(chuàng)建實例的方法和引用實例都不能用private修飾
剛開始使用一定總會遇到很多錯誤,遇到錯誤不要著急晨川。如果注意事項中的錯誤沒有犯的話一定會減少很多錯誤证九。
結語
終于寫完了。本來不我自己就不喜歡長文章共虑。不知不覺寫的有點多愧怜。對我這種小白,看源碼寫博客妈拌。真的很費心拥坛,學過的技術忘的很快,很多東西理解不透徹,想把博客寫好寫深還是很有難度的猜惋。不過如果你看到了這篇文章丸氛,希望有錯誤很問題請留言一起探討。Dagger2也是在我用MVP構建項目時候使用的著摔,可能學習的不是很深入缓窜。不過相信把這兩篇文章的代碼敲一邊。平常的使用一定沒有問題的梨撞。最后希望大家一起加油1⑾础O愎蕖卧波!