RXjava+Retrofit+dagger的入門
前言:現在RXjava+Retrofit+dagger可謂是如日中天,當時頭一次接觸都有一種無從入手的感覺,那麼我來分享一下我的學習方式。將RXjava Retrofit dagger分開來學 最後整合到一起
RXjava篇
相信各位看官對RxJava早有耳聞,那麼關於什麼是RxJava我就不再贅述了,不知道的可自行百度。如果你已經大致瞭解過什麼是RxJava,想開始學習,那麼本文不失爲你良好的選擇,爲什麼這麼說呢,因爲我也是剛學幾天,正所謂“知己知彼,百戰不殆”。網上流傳,RxJava的入門門檻高,而經過我這幾天的學習,我反而不那麼認爲,精通我不敢說,但入門確實也不難,不信?我先來個簡單的例子預熱一下。
先創建個數據發射源,很好理解,就是發射數據用的:
Observable<String> sender = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext("Hi,Weavey!"); //發送數據"Hi,Weavey!"
}
});
再創建個數據接收源,同理,接收數據用的:
Observer<String> receiver = new Observer<String>() {
@Override
public void onCompleted() {
//數據接收完成時調用
}
@Override
public void onError(Throwable e) {
//發生錯誤調用
}
@Override
public void onNext(String s) {
//正常接收數據調用
System.out.print(s); //將接收到來自sender的問候"Hi,Weavey!"
}
};
好了,將發射源和接收源關聯起來:
sender.subscribe(receiver);
這樣就形成RxJava一個簡單的用法,sender發射"Hi,Weavey!"
,將會被receiver的onNext
的接收,通過這個例子,也許你會想到“異步”、“觀察者模式”,沒錯,這些都是RxJava所做的事情,並且讓他們變得更簡單和簡潔,而RxJava所有的一切都將圍繞這兩個點展開,一個是發射數據,一個是接收數據,是不是很通俗易懂?如果你理解了這點或者你已經知道RxJava就是這麼一回事,那麼恭喜你,你已經一隻腳跨進RxJava的大門了,如果不是!!!!那也無所謂,請繼續往下看...
論概念的重要性
網上關於RxJava的博文也有很多,我也看過許多,其中不乏有優秀的文章,但絕大部分文章都有一個共同點,就是側重於講RxJava中各種強大的操作符,而忽略了最基本的東西——概念,所以一開始我也看的一臉懵逼,看到後面又忘了前面的,腦子裏全是問號,這個是什麼,那個又是什麼,這兩個長得怎麼那麼像。舉個不太恰當的例子,概念之於初學者,就像食物之於人,當你餓了,你會想吃麪包、牛奶,那你爲什麼不去吃土呢,因爲你知道麪包牛奶是用來幹嘛的,土是用來幹嘛的。同理,前面已經說過,RxJava無非是發送數據與接收數據,那麼什麼是發射源,什麼是接收源,這就是你應該明確的事,也是RxJava的入門條件之一,下面就依我個人理解,對發射源和接收源做個歸類,以及RxJava中頻繁出現的幾個“單詞”解釋一通,說的不好還請海涵,歡迎補充。
基本概念
Observable:發射源,英文釋義“可觀察的”,在觀察者模式中稱爲“被觀察者”或“可觀察對象”;
Observer:接收源,英文釋義“觀察者”,沒錯!就是觀察者模式中的“觀察者”,可接收Observable、Subject發射的數據;
Subject:Subject是一個比較特殊的對象,既可充當發射源,也可充當接收源,爲避免初學者被混淆,本章將不對Subject做過多的解釋和使用,重點放在Observable和Observer上,先把最基本方法的使用學會,後面再學其他的都不是什麼問題;
Subscriber:“訂閱者”,也是接收源,那它跟Observer有什麼區別呢?Subscriber實現了Observer接口,比Observer多了一個最重要的方法unsubscribe( )
,用來取消訂閱,當你不再想接收數據了,可以調用unsubscribe( )
方法停止接收,Observer 在subscribe()
過程中,最終也會被轉換成 Subscriber 對象,一般情況下,建議使用Subscriber作爲接收源;
Subscription :Observable調用subscribe( )
方法返回的對象,同樣有unsubscribe( )
方法,可以用來取消訂閱事件;
Action0:RxJava中的一個接口,它只有一個無參call()方法,且無返回值,同樣還有Action1,Action2...Action9等,Action1封裝了含有 1個參的call()方法,即call(T t),Action2封裝了含有 2 個參數的call方法,即call(T1 t1,T2 t2),以此類推;
Func0:與Action0非常相似,也有call()方法,但是它是有返回值的,同樣也有Func0、Func1...Func9;
基本用法
-
Observable的創建
1.使用create( ),最基本的創建方式:normalObservable = Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { subscriber.onNext("create1"); //發射一個"create1"的String subscriber.onNext("create2"); //發射一個"create2"的String subscriber.onCompleted();//發射完成,這種方法需要手動調用onCompleted,纔會回調Observer的onCompleted方法 }});
2.使用just( ),將爲你創建一個Observable並自動爲你調用
onNext( )
發射數據:justObservable = Observable.just("just1","just2");//依次發送"just1"和"just2"
3.使用from( ),遍歷集合,發送每個item:
List<String> list = new ArrayList<>(); list.add("from1"); list.add("from2"); list.add("from3"); fromObservable = Observable.from(list); //遍歷list 每次發送一個 /** 注意,just()方法也可以傳list,但是發送的是整個list對象,而from()發送的是list的一個item** /
4.使用defer( ),有觀察者訂閱時才創建Observable,並且爲每個觀察者創建一個新的Observable:
deferObservable = Observable.defer(new Func0<Observable<String>>() { @Override //注意此處的call方法沒有Subscriber參數 public Observable<String> call() { return Observable.just("deferObservable"); }});
5.使用interval( ),創建一個按固定時間間隔發射整數序列的Observable,可用作定時器:
intervalObservable = Observable.interval(1, TimeUnit.SECONDS);//每隔一秒發送一次
6.使用range( ),創建一個發射特定整數序列的Observable,第一個參數爲起始值,第二個爲發送的個數,如果爲0則不發送,負數則拋異常:
rangeObservable = Observable.range(10, 5);//將發送整數10,11,12,13,14
7.使用timer( ),創建一個Observable,它在一個給定的延遲後發射一個特殊的值,等同於Android中Handler的postDelay( )方法:
timeObservable = Observable.timer(3, TimeUnit.SECONDS); //3秒後發射一個值
8.使用repeat( ),創建一個重複發射特定數據的Observable:
repeatObservable = Observable.just("repeatObservable").repeat(3);//重複發射3次
-
Observer的創建
mObserver = new Observer<String>() { @Override public void onCompleted() { LogUtil.log("onCompleted"); } @Override public void onError(Throwable e) { } @Override public void onNext(String s) { LogUtil.log(s); }};
ok,有了Observable和Obsever,我們就可以隨便玩了,任取一個已創建的Observable和Observer關聯上,即形成一個RxJava的例子,如:
justObservable.subscribe(mObserver);
mObserver的
onNext
方法將會依次收到來自justObservable的數據"just1"
、"just2"
,另外,如果你不在意數據是否接收完或者是否出現錯誤,即不需要Observer的onCompleted()
和onError()
方法,可使用Action1
,subscribe()
支持將Action1
作爲參數傳入,RxJava將會調用它的call
方法來接收數據,代碼如下:justObservable.subscribe(new Action1<String>() { @Override public void call(String s) { LogUtil.log(s); }});
以上就是RxJava最簡單的用法。看到這裏,我也不知道我寫的是否簡單明瞭,也許你會想,“哎呀,寫個異步的東西,怎麼這麼麻煩,爲什麼不用Thread+Handler呢”,那你就錯了,RxJava也以代碼的簡潔深受廣大用戶喜愛,簡潔不能理解爲代碼量少,而是隨着邏輯的複雜,需求的更改,代碼可依然能保持極強的閱讀性,舉個簡單的例子(前方高能預警~~~),領導要我從數據庫的用戶表查找出所有用戶數據,我二話不說拿出心儀的RxJava就寫:
Observable.create(new Observable.OnSubscribe<List<User>>() { @Override public void call(Subscriber<? super List<User>> subscriber) { List<User> userList = null; ··· //從數據庫獲取用戶表數據並賦給userList ··· subscriber.onNext(userList); } }).subscribe(new Action1<List<User>>() { @Override public void call(List<User> users) { //獲取到用戶信息列表 } });
但是,領導突然又不想要所有用戶了,只要名字叫“小明”的用戶,行吧,領導最大,我改(假設名字唯一):
Observable.create(new Observable.OnSubscribe<List<User>>() { @Override public void call(Subscriber<? super List<User>> subscriber) { List<User> userList = null; ··· //從數據庫獲取用戶表數據並賦給userList ··· subscriber.onNext(userList); } }).flatMap(new Func1<List<User>, Observable<User>>() { @Override public Observable<User> call(List<User> users) { return Observable.from(users); } }).filter(new Func1<User, Boolean>() { @Override public Boolean call(User user) { return user.getName().equals("小明"); } }).subscribe(new Action1<User>() { @Override public void call(User user) { //拿到謎之小明的數據 } });
搞定,這時候領導又說,我不要小明瞭,我要小明的爸爸的數據,(坑爹啊~~),我繼續改:
Observable.create(new Observable.OnSubscribe<List<User>>() { @Override public void call(Subscriber<? super List<User>> subscriber) { List<User> userList = null; ··· //從數據庫獲取用戶表數據並賦給userList ··· subscriber.onNext(userList); } }).flatMap(new Func1<List<User>, Observable<User>>() { @Override public Observable<User> call(List<User> users) { return Observable.from(users); } }).filter(new Func1<User, Boolean>() { @Override public Boolean call(User user) { return user.getName().equals("小明"); } }).map(new Func1<User, User>() { @Override public User call(User user) { //根據小明的數據user從數據庫查找出小明的父親user2 return user2; } }).subscribe(new Action1<User>() { @Override public void call(User user2) { //拿到謎之小明的爸爸的數據 } });
搞定,“還想怎麼改?領導請說···”。
以上例子,涉及到幾個操作符,初學者可能無法理解,但是無所謂,這不是重點,我的目的只是爲了向你展示RxJava在需求不斷變更、邏輯愈加複雜的情況下,依舊可以保持代碼簡潔、可閱讀性強的一面,沒有各種回調,也沒有謎之縮進!
Retrofit篇
Retrofit與okhttp共同出自於Square公司,retrofit就是對okhttp做了一層封裝。把網絡請求都交給給了Okhttp,我們只需要通過簡單的配置就能使用retrofit來進行網絡請求了,其主要作者是Android大神JakeWharton。
導包:
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'//Retrofit2所需要的包
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'//ConverterFactory的Gson依賴包
compile 'com.squareup.retrofit2:converter-scalars:2.0.0-beta4'//ConverterFactory的String依賴包
*這裏需要值得注意的是導入的retrofit2包的版本必須要一致,否則就會報錯。
首先定義我們請求的Api,我們假設是這樣的
http://106.3.227.33/pulamsi/mobileLogin/submit.html
與Okhttp不同的是,Retrofit需要定義一個接口,用來返回我們的Call對象,這裏示範的是Post請求:
public interface RequestServes { @POST("mobileLogin/submit.html") Call<String> getString(@Query("loginname") String loginname, @Query("nloginpwd") String nloginpwd); }
@GET
和@POST
,參數註解有@PATH
和@Query
等,我們只介紹常用的;前兩個顧名思義就是定義你的請求方式Get
or Post
,後面的@PATH
指的是通過參數填充完整的路徑,一般用法:"{name}") (
Call<User>
getUser(@Path("name") String name);
這裏的參數username會被填充至{name}
中,形成完整的Url請求地址,{name}
相當於一個佔位符;
@Query
就是我們的請求的鍵值對的設置,我們構建Call對象的時候會傳入此參數,
1 2 3 |
("mobileLogin/submit.html") Call<String> getString(@Query("loginname") String loginname, @Query("nloginpwd") String nloginpwd); |
@Query("loginname")
就是鍵,後面的loginname
就是具體的值了,值得注意的是Get和Post請求,都是這樣填充參數的;
接口寫完了之後我們需要來定義Retrofit對象來進行請求了;
創建一個Retrofit 對象
Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://106.3.227.33/pulamsi/") //增加返回值爲String的支持 .addConverterFactory(ScalarsConverterFactory.create()) //增加返回值爲Gson的支持(以實體類返回) .addConverterFactory(GsonConverterFactory.create()) //增加返回值爲Oservable<T>的支持 .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build();
這裏的baseUrl
加上之前@POST("mobileLogin/submit.html")
定義的參數形成完整的請求地址;
addConverterFactory(ScalarsConverterFactory.create())
的意思是構建了一個返回支持,如果你的Call對象的泛型接收另外的格式需要添加另外的支持,上述代碼已經列出;
接下來我們用這個Retrofit
對象創建一個RequestSerives
接口對象,也就是我們之前定義的那個接口,並且得到我們的Call對象;
RequestSerives requestSerives = retrofit.create(RequestSerives.class);//這裏採用的是Java的動態代理模式 Call<String> call = requestSerives.getString("userName", "1234");//傳入我們請求的鍵值對的值 |
導入Dagger2
使用Dagger2
之前需要一些配置,該配置是在Android Studio
中進行操作。
在工程的build.gradle
文件中添加android-apt
插件(該插件後面介紹)
buildscript {
....
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
// 添加android-apt 插件
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 23
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "com.mahao.alex.architecture"
minSdkVersion 15
targetSdkVersion 23
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:23.3.0'
// dagger 2 的配置
compile 'com.google.dagger:dagger:2.4'
apt 'com.google.dagger:dagger-compiler:2.4'
compile 'org.glassfish:javax.annotation:10.0-b28'// 添加java 註解庫
}
以上兩個配置就可以了。
android-apt
是Gradle
編譯器的插件,根據其官方文檔,主要兩個目的:
編譯時使用該工具,最終打包時不會將該插件打入到apk中。
能夠根據設置的源路徑,在編譯時期生成相應代碼。
在導入類庫時,
compile 'com.google.dagger:dagger:2.4'
apt 'com.google.dagger:dagger-compiler:2.4'
dagger
是主要的工具類庫。dagger-compiler
爲編譯時期生成代碼等相關的類庫。
在android-apt
的文檔中,也推薦使用這種方式。因爲,編譯時期生成代碼的類庫在運行期並不需要,那麼將其分爲兩個庫,(運行類庫dagger
)和(編譯器生成代碼類庫(dagger-compiler
)),那麼在打包時,就不需要將dagger-compiler
打入其中(用不到),減小APK 的大小。
Dagger2篇
Dagger2
的使用,需要大量的學習成本,不是很能夠容易的上手並使用。該博客將從簡單入手,儘可能的使用簡單的例子演示Dagger2
的功能。
一個東西需要先會用,然後才更好的學習原理。該篇博客的目的主要是講解如何使用。後面會有專門的分析源碼的博客。
在之前的分析中,通過Dagger2
的目的是將程序分爲三個部分。
- 實例化部分:對象的實例化。類似於容器,將類的實例放在容器裏。
- 調用者:需要實例化對象的類。
- 溝通橋樑:利用Dagger2
中的一些API 將兩者聯繫。
先看實例化部分(容器),在此處是Module
。
@Module //提供依賴對象的實例
public class MainModule {
@Provides // 關鍵字,標明該方法提供依賴對象
Person providerPerson(){
//提供Person對象
return new Person();
}
}
溝通部分Component
@Component(modules = MainModule.class) // 作爲橋樑,溝通調用者和依賴對象庫
public interface MainComponent {
//定義注入的方法
void inject(MainActivity activity);
}
使用者Actvity
中調用。
public class MainActivity extends AppCompatActivity{
@Inject //標明需要注入的對象
Person person;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 構造橋樑對象
MainComponent component = DaggerMainComponent.builder().mainModule(new MainModule()).build();
//注入
component.inject(this);
}
}
看一下Person
類
public class Person {
public Person(){
Log.i("dagger","person create!!!");
}
}
最後結果不在演示。其過程如下:
- 創建
Component
(橋樑),並調用注入方法。
// 構造橋樑對象
MainComponent component = DaggerMainComponent.builder().mainModule(new MainModule()).build();
//注入
component.inject(this);
- 查找當前類中帶有
@Inject
的成員變量。
@Inject //標明需要注入的對象
Person person;
- 根據成員變量的類型從
Module
中查找哪個有@Provides
註解的方法返回值爲當前類型。
@Provides // 關鍵字,標明該方法提供依賴對象
Person providerPerson(){
//提供Person對象
return new Person();
}
在使用過程出現了很多註解:
@Module
:作爲實例對象的容器。@Provides
:標註能夠提供實例化對象的方法。@Component
:作爲橋樑,注入對象的通道。@Inject
:需要注入的方法
如上使用有一種變通,修改MainModule
和Person
類。
@Module //提供依賴對象的實例
public class MainModule {
/*
@Provides // 關鍵字,標明該方法提供依賴對象
Person providerPerson(){
//提供Person對象
Log.i("dagger"," from Module");
return new Person();
}
*/
}
public class Person {
@Inject // 添加註解關鍵字
public Person(){
Log.i("dagger","person create!!!");
}
}
將Module
中的providePerson()
方法註釋,在Person
中添加@Inject
註解,依然能夠實現。
邏輯如下:
- 先判斷Module
中是否有提供該對象實例化的方法。
- 如果有則返回。結束。
- 如果沒有,則查找該類的構造方法,是否有帶有@Inject
的方法。如過存在,則返回。
@Singleton 單例註解
假如,對於同一個對象,我們需要注入兩次,如下方式
public class MainActivity extends AppCompatActivity{
@Inject
Person person;
@Inject
Person person2;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 構造橋樑對象
MainComponent component = DaggerMainComponent.builder().mainModule(new MainModule()).build();
//注入
component.inject(this);
// 打印兩個對象的地址
Log.i("dagger","person = "+ person.toString()+"; person2 = "+ person2.toString());
}
}
看一下結果:
person = com.mahao.alex.architecture.dagger2.Person@430d1620; person2 = com.mahao.alex.architecture.dagger2.Person@430d17c8
可見兩個對象不一致。也就是說創建了兩個對象。
可以在提供實例化對象的方法上添加@Singleton
註解
@Provides // 關鍵字,標明該方法提供依賴對象
@Singleton
Person providerPerson(){
return new Person();
}
同時,對於MainComponent
也需要添加註解,不添加會無法編譯
@Singleton
@Component(modules = MainModule.class) // 作爲橋樑,溝通調用者和依賴對象庫
public interface MainComponent {
//定義注入的方法
void inject(MainActivity activity);
}
此時在Log,會發現兩個對象的地址一樣,可見是同一個對象。
person = com.mahao.alex.architecture.dagger2.Person@4310f898; person2 = com.mahao.alex.architecture.dagger2.Person@4310f898
那麼不同的Activity
之間,能否保持單例呢?
創建一個新的Activity
,代碼如下:
public class Main2Actvity extends AppCompatActivity {
@Inject
Person person;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 構造橋樑對象
MainComponent component = DaggerMainComponent.builder().mainModule(new MainModule()).build();
//注入
component.inject(this);
Log.i("dagger","person = "+ person.toString());
}
}
結果如下:
person create!!!
person = com.mahao.alex.architecture.dagger2.Person@4310f898; person2 = com.mahao.alex.architecture.dagger2.Person@4310f898
person create!!!
person = com.mahao.alex.architecture.dagger2.Person@43130058
可見,@Singleton
只對一個Component
有效,即其單例所依賴Component
對象。
需要參數的實例化對象
Person
的構造方法發生了變化,需要傳入一個Context
,代碼如下:
public class Person {
private Context mContext;
public Person(Context context){
mContext = context;
Log.i("dagger","create");
}
}
這樣的話,我們需要修改MainModule
@Module //提供依賴對象的實例
public class MainModule {
private Context mContext;
public MainModule(Context context){
mContext = context;
}
@Provides
Context providesContext(){
// 提供上下文對象
return mContext;
}
@Provides // 關鍵字,標明該方法提供依賴對象
@Singleton
Person providerPerson(Context context){
return new Person(context);
}
}
- 修改
providerPerson
方法,傳入Context
對象。 - 添加
providesContext()
,用以提供Context
對象。
看一下使用
// 構造橋樑對象
MainComponent component = DaggerMainComponent.builder().mainModule(new MainModule(this)).build();
//注入
component.inject(this);
邏輯:
- 根據
@Inject
註解,查找需要依賴注入的對象。 - 從
MainModule
中根據返回值,找到providerPerson(Context context)
對象。 - 發現其需要傳入參數
Context
,找到moudule
中具有返回值爲Context
的方法providesContext()
。 - 最後就成功的構建了實例化對象。
可能會有疑問,我既然module
中已經保存了Context
對象,那麼爲什麼不直接使用Context
對象呢,因爲解耦,如果使用了保存的對象,會導致下次Context
獲取發生變化時,需要修改providerPerson(Context context)
中的代碼。
在編寫Module
中,不能出現傳入參數和返回參數一致的情況,會導致死循環。
很容易理解,需要的和獲取的是同一個方法,循環調用。
依賴一個組件
在使用中,往往會有依賴另一個組件的情況。比如,在AppMoudle
中能夠提供Context
對象,如下:
@Module
public class AppModule {
private Context mContext;
public AppModule(Context context){
mContext = context;
}
@Provides
Context providesContext(){
// 提供Context對象
return mContext;
}
}
而在另一個Module
中需要依賴Context
對象,那麼怎麼寫呢?
首先編寫當前AppModule
的Component
類
/**
*
* 全局的Component 組件
* Created by MH on 2016/7/18.
*/
@Component(modules = AppModule.class)
public interface AppComponent {
// 向其下層提供Context 對象
Context proContext();
}
在此種,因爲Module
中需要向下層提供Context
對象,而其與下層的聯繫時通過Component
,所以需要在這裏聲明一個其所提供對象的方法。以便下層Module
獲取。
/**
*
* 下層Module類
* Created by MH on 2016/7/18.
*/
@Module
public class ActivityMoudule {
@Provides
Person providePerson(Context context){
// 此方法需要Context 對象
return new Person(context);
}
}
/**
* 子的Component
* Created by MH on 2016/7/18.
*/
@Component(dependencies = AppComponent.class,modules = ActivityMoudule.class)
public interface ActivityComponent {
// 注入
void inject(MainActivity activity);
}
在子Component
中,有一句關鍵的註解dependencies = AppComponent.class
,添加了上層依賴。
看一下使用
// 依賴對象 Component
AppComponent appCom = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
// 子類依賴對象 ,並注入
DaggerActivityComponent.builder()
.appComponent(appCom)
.activityMoudule(new ActivityMoudule())
.build()
.inject(this);
在其中使用過程中,有很重的兩點。
- 父依賴的
Component
中需要添加提供對象的接口。 - 子依賴的
Component
中的註解中添加dependencies = AppComponent.class
。
@Qualifier 自定義標記
在使用中,會出現兩個方法返回對象相同時的情況,那麼如何區分呢。
Person
對象具有兩個構造方法,根據不同的參數值構造不同的方法。
public class Person {
private Context mContext;
public Person(Context context){
mContext = context;
Log.i("dagger","create");
}
public Person(String name){
Log.i("dagger",name);
}
}
ActivityModule
中添加@Named
標記
@Module
public class ActivityMoudule {
@Named("Context") // 通過context創建Person 對象
@Provides
Person providePersonContext(Context context){
// 此方法需要Context 對象
return new Person(context);
}
@Named("name") // 通過name創建Person 對象
@Provides
Person providePersonName(){
// 此方法需要name
return new Person("1234");
}
}
使用時,也需要添加此標記
public class MainActivity extends AppCompatActivity{
@Named("context") // 標記
@Inject
Person person;
@Named("name") // 標記
@Inject
Person person2;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注入
component.inject(this);*/
// 依賴對象 Component
AppComponent appCom = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
// 子類依賴對象 ,並注入
DaggerActivityComponent.builder()
.appComponent(appCom)
.activityMoudule(new ActivityMoudule())
.build()
.inject(this);
}
}
使用時,使用者的@Inject
上,必須要加入註解@Named("xxx")
,不然編譯期會報錯。
這樣使用過程中,雖然解決了問題,但是通過字符串標記一個對象,容易導致前後不匹配,可以通過自定義註解的方式解決。
添加兩個註解,分別對應Context
和name
。
@Qualifier // 關鍵詞
@Retention(RetentionPolicy.RUNTIME) // 運行時仍可用
public @interface PersonForContext {
// Context 對象的註解
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface PersonForName {
// name 對象的註解
}
在使用@Named("")
的地方替換爲上面的註解
@PersonForContext // 通過context創建Person 對象
@Provides
Person providePersonContext(Context context){
// 此方法需要Context 對象
return new Person(context);
}
@PersonForName // 通過name創建Person 對象
@Provides
Person providePersonName(){
// 此方法需要Context 對象
return new Person("123");
}
注入時:
@PersonForContext // 標記
@Inject
Person person;
@PersonForName // 標記
@Inject
Person person2;
Scope
在前面中提到@Singleton
註解,該註解能夠使同一個Component
中的對象保持唯一,即單例。
回憶一下,如下方式:
@Provides // 關鍵字,標明該方法提供依賴對象
@Singleton
Person providerPerson(Context context){
return new Person(context);
}
Module
中,對應方法中添加@Singleton
註解,同時其所在的Component
中,類生命上也需要添加註解
@Singleton
@Component(modules = MainModule.class) // 作爲橋樑,溝通調用者和依賴對象庫
public interface MainComponent {
}
如果我們看這個意思,感覺其內部應該做了很多的實現,用以達到單例。其實,沒我們想的那麼複雜。
看一下@Singleton
的實現
@Scope //註明是Scope
@Documented //標記在文檔
@Retention(RUNTIME) // 運行時級別
public @interface Singleton {}
通過@Scope
定義的一個新的註解。
在之前的,我們知道該單例是依託於他所在的Component
組件。那麼我們是否可以這樣理解,因爲方法上添加的@Scope
標記的註解和Component
上添加的@Scope
標記的註解相同(確實相同,同爲@Singleton
),就表明了該方法提供的實例對象在Component
保持唯一。保持唯一的條件是通過@Scope
標記的註解相同。
通過在上面的依賴層級上,Android
中通常定義兩個生命週期。
全局的生命週期PerApp
/**
* 全局的生命週期單例
*/
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface PerApp {
}
在使用中完全和@Singleton
相同。
@Module
public class AppModule {
private Context mContext;
public AppModule(Context context){
mContext = context;
}
@Provides
@PerApp // 添加該標記表明該方法只產生一個實例
Context providesContext(){
// 提供上下文對象
return mContext;
}
}
@PerApp // 因爲Module 中使用了該標記,所以需要在此添加
@Component(modules = AppModule.class)
public interface AppComponent {
// 向其下層提供Context 對象
Context proContext();
}
因爲單例的依託於他所在的Component
中,所以需要在Application
中進行實例化。
public class App extends Application {
// 爲什麼可以使用靜態
public static AppComponent appComponent;
@Override
public void onCreate() {
super.onCreate();
// 實例化
appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
}
}
爲什麼可以使用靜態的,因爲該AppComponent
對象的生命週期是整個App。那麼在使用中,其所在Module
中的實例化對象,可以保持全局單例。
一個Activity的生命週期PerActivity
有全局的單例,而對於一個Activity
,他也有些對象需要保持單例。我們需要定義該註解。
/**
* Activity 單例生命週期
*/
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface PerActivity {
}
會發現,除了定義名不一樣,其餘都和PerApp
一樣。在前面,說過這樣一句話:保持唯一的條件是通過@Scope
標記的註解相同。
@Module
public class ActivityMoudule {
@PersonForContext
@Provides
@PerActivity // 添加標記,生命其所構造的對象單例
Person providePersonContext(Context context){
// 此方法需要Context 對象
return new Person(context);
}
.....
}
@PerActivity // ActivityMoudule 中使用了該標記
@Component(dependencies = AppComponent.class,modules = ActivityMoudule.class)
public interface ActivityComponent {
// 注入
void inject(MainActivity activity);
}
使用方式,因爲其所保持的單例是在Activity
中,具體使用如下。
public class MainActivity extends AppCompatActivity{
@PersonForContext // 標記
@Inject
Person person;
@PersonForName // 標記
@Inject
Person person2;
/**
* 不使用靜態的,因爲該Component只是針對於該Activity,而不是全局的
*/
ActivityComponent activityComponent;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
activityComponent = DaggerActivityComponent.builder()
.appComponent(App.appComponent) // 添加了全局的AppComponent組件,可以使用全局的實例化對象
.activityMoudule(new ActivityMoudule())
.build();
activityComponent.inject(this);
對於具有依賴關係的Component,不能使用相同的Scope,如果使用相同的會帶來語意不明
懶加載Lazy和強制重新加載Provider
public class MainActivity extends AppCompatActivity{
@PersonForContext // 標記
@Inject
Lazy<Person> lazyPerson; // 注入Lazy元素
@PersonForName // 標記
@Inject
Provider<Person> providerPerson; // 注入Provider
/**
* 不使用靜態的,因爲該Component只是針對於該Activity,而不是全局的
*/
ActivityComponent activityComponent;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
activityComponent = DaggerActivityComponent.builder()
.appComponent(App.appComponent) // 添加了全局的AppComponent組件
.activityMoudule(new ActivityMoudule())
.build();
activityComponent.inject(this);
Person person = lazyPerson.get();// 調用該方法時纔會去創建Person,以後每次調用獲取的是同一個對象
// 調用該方法時纔回去創建Person1,以後每次調用都會重新加載Module中的具體方法,根據Module中的實現,可能相同,可能不相同。
Person person1 = providerPerson.get();
}
}
說這麼多都沒用,不如實踐一下。下面提供一個鏈接github上的一個開源項目,RXjava+Retrofit+dagger2的框架。希望這篇文章對想要學習RXjava+Retrofit+dagger2有一些幫助
點擊打開鏈接