近些時(shí)間,由于接手一個(gè)嶄新的項(xiàng)目,就考慮到了使用目前市面上較為流行的一些框架,再三權(quán)衡之下,在項(xiàng)目中引入了標(biāo)題說(shuō)的幾個(gè)框架,在此會(huì)對(duì)其用法,整合進(jìn)行闡述,盡自己所能幫助大家.
轉(zhuǎn)載請(qǐng)注明出處 簡(jiǎn)書-liqinpeng
Dagger2
具體的Dagger2描述請(qǐng)大家自行百度,這里只闡述自己對(duì)其的一些理解
在查看下文的時(shí)候,我們需要了解以下幾個(gè)概念:
1:什么是依賴注入?
在此對(duì)依賴注入進(jìn)行解釋,我們將依賴 | 注入分開,
依賴
就是找你當(dāng)前對(duì)象中所依賴的其他對(duì)象,比如說(shuō)學(xué)生對(duì)象中存在著教師的引用,調(diào)用學(xué)生的聽課方法,就必然
需要用到教師這個(gè)對(duì)象,不然誰(shuí)去給學(xué)生講課呢?這里,學(xué)生對(duì)象就依賴了教師對(duì)象
注入
既然學(xué)生對(duì)象的創(chuàng)建要依賴于教師對(duì)象,或者說(shuō)學(xué)生對(duì)象的某些方法依賴了教師對(duì)象,那么在學(xué)生中教師我們可以通過(guò)如下方式來(lái)獲取
1:構(gòu)造方法傳入 2,需要用到教師的方法,參數(shù)傳入
我們來(lái)考慮一下問(wèn)題,我們需要在調(diào)用方法的時(shí)候創(chuàng)建學(xué)生,創(chuàng)建教師,然后將教師傳遞過(guò)去,是,這種思路沒(méi)毛病,接下來(lái)我們?cè)谡f(shuō)一下極端的問(wèn)題,
假如說(shuō)學(xué)生依賴了教師,教師依賴了教室,教室又依賴于黑板,等等等等,那么我們?cè)谡{(diào)用學(xué)生方法的時(shí)候,是不是需要?jiǎng)?chuàng)建出教師,教室,黑板等對(duì)象,這種方式看似沒(méi)毛病,實(shí)際上如果對(duì)象一級(jí)級(jí)的依賴于其他對(duì)象,
那么對(duì)我們的工作量是十分巨大的
依賴注入就這么誕生了,既然你要求說(shuō)你A對(duì)象依賴于B對(duì)象,甚至說(shuō)B依賴于C,一層層的嵌套下去,對(duì)于開發(fā)者來(lái)說(shuō),都不關(guān)心,因?yàn)槲覀冇辛薉agger2,他會(huì)自動(dòng)幫我們注入對(duì)象所需要的依賴對(duì)象(的依賴對(duì)象).
2:我為什么要使用他?
在上個(gè)問(wèn)題中我大概已經(jīng)解釋了為什么要使用這么一個(gè)框架,他會(huì)注入你要操作對(duì)象依賴的對(duì)象(的依賴對(duì)象),不管依賴了多少級(jí),都交給他去做吧
怎么使用他呢?
在你的項(xiàng)目gradle中導(dǎo)入
compile 'com.google.dagger:dagger:2.4'
apt 'com.google.dagger:dagger-compiler:2.4'
接下來(lái)我們介紹一下Dagger2提供給我們的注解(目前我們會(huì)用到的)
@Component(組成;成分) 模塊(組件)的集合
作為Dagger2的注入核心,在代碼編寫完成之后,dagger-compiler會(huì)為我們編譯生成對(duì)應(yīng)的類,命名如下 DaggerXxxComponent
下面是我在項(xiàng)目中用到的
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
void injectApplication(MApplication application);
}
我用@Componet對(duì)該接口進(jìn)行聲明,說(shuō)明其實(shí)Dagger2的一個(gè)模塊集,使用@Single告訴Dagger2,該組件是單例的,全局只有這一個(gè)
@Component既然是一個(gè)模塊集,就要求我們對(duì)應(yīng)有其他一系列模塊,當(dāng)然一個(gè)模塊也是可行的,寫法如下
@Component (modules = XxxModule.class)
如果有多個(gè)模塊,用大括號(hào)括起來(lái)
這時(shí)候不知道你有沒(méi)有想到這么一個(gè)問(wèn)題?
既然是模塊集,那A模塊集是否可以引用或者說(shuō)依賴于另一個(gè)模塊集呢?
有這種想法就太棒了,模塊集肯定也是模塊了,那一個(gè)模塊集引用其他模塊集當(dāng)然是可行的,用代碼體現(xiàn)的話是這樣的
@PreFragment
@Component(dependencies = AppComponent.class , modules = {FragmentModule.class} )
public interface FragmentComponent {
//個(gè)人設(shè)置
void injectSettingFragment(MySettingFragment mySettingFragment);
}
大家可以先行跳過(guò)@PreFragment這個(gè)注解,我們往下看
@Component聲明FrgamentComponent是組件集, modules聲明引用了FragmentModule.class這個(gè)組件,而dependencies就是我們需要注意的,依賴于另一個(gè)組件集,在此我們依賴的是上面的AppComponent組件集
畫個(gè)圖看一下


@Module(模塊; 組件)
組件提供給我們的功能就很強(qiáng)大了,需要在類首加上@Module以及對(duì)應(yīng)的作用域(就是聲明該module是一個(gè)單例的).編寫成代碼是這樣的
@Singleton
@Module
public class AppModule {
public Application context;
public AppModule(Application context) {
this.context = context;
}
}
對(duì)應(yīng)上我們剛才的AppComponent組件集,我們就要考慮一件事了,我怎么將組件集和組件進(jìn)行綁定,盡管我們的AppComponent聲明了自己是依賴于AppModule的
在代碼中這樣寫
DaggerAppComponent.builder().appModule(new AppModule(this)).build();
我們來(lái)還原一下這行代碼
DaggerAppComponent.Builder builder = DaggerAppComponent.builder();
builder = builder.appModule(new AppModule(this));
builder.build();
我們來(lái)解釋一下這三行代碼
第一句 構(gòu)建出來(lái)DaggerAppComponent組件集這個(gè)Builder對(duì)象,
第二句,給builder加入組件,加入AppModule組件
第三句,真正的創(chuàng)建好組件集對(duì)象
在這句話執(zhí)行完之后,就實(shí)現(xiàn)了容器的創(chuàng)建,
DaggerAppComponent.builder().appModule(new AppModule(this)).build().injectApplication(this);
這句話執(zhí)行完之后就將當(dāng)前對(duì)象告訴Dagger2容器,我需要用到你容器中的某些組件,他會(huì)自動(dòng)的去容器中尋找或創(chuàng)建,只需要在你需要的對(duì)象上加@Inject注解
就像這樣
public class MApplication extends Application {
@Inject
public Retrofit retrofit;
@Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.builder().appModule(new AppModule(this)).build().injectApplication(this);
}
這樣,我們按理說(shuō)就可以使用retrofit這個(gè)對(duì)象了,
但是,我們還不可以運(yùn)行這個(gè)程序,因?yàn)镈agger2不知道怎么創(chuàng)建Retrofit對(duì)象,在容器中也找不到該對(duì)象,
接下來(lái)我們來(lái)介紹一下Dagger2容器創(chuàng)建依賴對(duì)象的兩種方式
1: 如果依賴對(duì)象的構(gòu)造方法上加了@Inject注解,那么Dagger2就會(huì)調(diào)用該構(gòu)造方法去創(chuàng)建對(duì)象
2: 如果你要說(shuō) 你的依賴對(duì)象,是第三方框架提供的,你根本沒(méi)權(quán)利修改他的代碼,那么我要怎么創(chuàng)建該對(duì)象?
Dagger2給我們提供了一種方式
就像這樣
// 提供retrofit
@Provides
@Singleton
public Retrofit providerRetrofit(OkHttpClient client){
return new Retrofit.Builder()
.client(client)
.baseUrl(GlobalConstract.ip)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
}
在組件中提供某一對(duì)象,在其方法上加入@Provides注解,就相當(dāng)于通知Dagger2說(shuō),我給你提供Retrofit的創(chuàng)建方式,你如果不知道怎么創(chuàng)建,就來(lái)Module中找找看
繼續(xù)看,providerRetrofit該方法要求傳遞一個(gè)OkHttpClient 對(duì)象作為參數(shù),但是OkHttpClient 對(duì)象怎么創(chuàng)建呢? 他也是第三方的框架,我們是沒(méi)權(quán)利修改他的構(gòu)造方法的,怎么解決?
// 提供OkHttpClient
@Singleton
@Provides
public OkHttpClient providerOkHttpClient(){
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(10, TimeUnit.SECONDS);
OkHttpClient client = builder.build();
return client;
}
我們?cè)贛odule中加入OkHttpClient 的創(chuàng)建方式,那么providerRetrofit的參數(shù)就會(huì)由Dagger2去創(chuàng)建好在傳遞進(jìn)去
至此,我們的Retrofit對(duì)象方式也有了,我們需要在哪里使用,就在其聲明上加入@Inject注解
@Inject
public Retrofit retrofit;
@Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.builder().appModule(new AppModule(this)).build().injectApplication(this);
}
這樣就可以使用Retrofit對(duì)象了,是不是感覺(jué)很復(fù)雜?但是在你真正使用到的時(shí)候,你會(huì)感覺(jué)為什么Dagger2的管理是這么的舒服,這么讓人放心
這倆注解我們先不談,其實(shí)見名知意,大概也能了解個(gè)差不多了
@Singlet(單一) 單例需要用到的
@Scope(范圍) 作用域
我們來(lái)重點(diǎn)談?wù)勥@倆
@Inject 注入
既然是要注入,那么很明顯自己是需要將容器提供的對(duì)象拿出來(lái),并且注入到自己的對(duì)象中直接使用,不用自己去創(chuàng)建那一級(jí)級(jí)的對(duì)象關(guān)系鏈,簡(jiǎn)化工作
但是你自己創(chuàng)建的對(duì)象怎么知道哪些是需要自己去創(chuàng)建或者說(shuō)哪些是需要在容器中尋找的呢?@Injcet提供給我們的就是這么一個(gè)功能,只要你需要的對(duì)象頭上加了@Inject注解,就聲明了該對(duì)象需要在容器中去尋找,不用自己手動(dòng)創(chuàng)建
@Provides 提供
我在上面也說(shuō)過(guò)了,Dagger2提供給我們兩種創(chuàng)建對(duì)象的方式,而@Provides則是在第二種情況時(shí)使用的,第三方框架得類你沒(méi)權(quán)限修改,但是你可以告訴Dagger2該框架的某些對(duì)象是怎么創(chuàng)建的,這么告訴呢,就是@Provides了,只需要在model中寫下providerXXX()方法,返回XXX,在方法聲明頭用@Provides標(biāo)注一下,就完成了.
現(xiàn)在假設(shè)你已經(jīng)理解了Dagger2的一些簡(jiǎn)單用法,我就不再深入說(shuō)了,以后如果我們有時(shí)間,在來(lái)詳細(xì)分析,大家討論學(xué)習(xí),共同進(jìn)步
接下來(lái)的篇幅我會(huì)把自己最近學(xué)RxJava的心得全部分享出來(lái),如果我的代碼有問(wèn)題,想法有錯(cuò)誤,或者說(shuō)路是錯(cuò)的,希望大家能批評(píng)指正,萬(wàn)分感謝
Retrofit 和 RxAndroid的完美搭配
我暫時(shí)不去講Retrofit和RxAndroid的使用,網(wǎng)上的教程很多,也都寫的很棒,大家先自行百度了解學(xué)習(xí),之后我會(huì)一點(diǎn)點(diǎn)更新
哇,好像不寫他們?cè)趺词褂梦揖蜔o(wú)法下手碼字了... 腦袋都是懵的...
好了言歸正傳,我們繼續(xù)
我們都知道,在項(xiàng)目中,有著服務(wù)器給的各種接口,將接口分類整理之后可能會(huì)是這樣的

接口的聲明按照Retrofit的規(guī)范

至于為什么接口統(tǒng)一返回ResponseSet<T>呢,我是這么想的,
首先我們來(lái)看一下Responset這個(gè)類
public class ResponseSet<T> {
public Integer state;
public String msg;
public String action;
public T json;
}
我們?cè)趤?lái)看一下服務(wù)器返回的數(shù)據(jù)格式
{
"json": {
"have_home_info": false,
"have_baby": false,
"user_id": "f39da9fa5c3ff1bd015c3ff4584b0000"
},
"action": "login",
"state": 0
}
這個(gè)數(shù)據(jù)格式是不會(huì)變化的,在請(qǐng)求成功之后會(huì)返回action(你請(qǐng)求的接口),state(請(qǐng)求狀態(tài)碼,對(duì)應(yīng)請(qǐng)求結(jié)果),而json則是變化的,可能是一個(gè)user,一個(gè)vaccine,一個(gè)station.
如果請(qǐng)求失敗了會(huì)有服務(wù)端返回的失敗原因msg
這時(shí)候我們就要去考慮,既然所有的接口都會(huì)返回公共的部分,是每次都要寫一遍嗎? 這樣肯定是不正確的做法,于是就想到了繼承,想到了泛型,因?yàn)槲冶救讼刖毩?xí)一下泛型的使用就在此采用泛型了,ResponsetSet要有一個(gè)泛型,這個(gè)類型實(shí)際上就是服務(wù)器給返回的json字段轉(zhuǎn)換成的javabean類型,我們來(lái)解釋一下這樣做的好處.
既然我們選擇了在項(xiàng)目中使用RxAndroid,我們應(yīng)該知道filter這個(gè)方法了,過(guò)濾,對(duì),就是過(guò)濾,現(xiàn)在有這么一個(gè)需求,我需要將所有的非成功的方法全部攔截,不進(jìn)行處理,怎么做? 如果每次服務(wù)器返回?cái)?shù)據(jù)對(duì)應(yīng)你的一個(gè)javabean,你怎么做? 總不能寫一個(gè)個(gè)的過(guò)濾器吧?
這時(shí)候就體現(xiàn)我這么做的好處了,既然所有的請(qǐng)求最終都會(huì)返回一個(gè)ResponseSet,那么過(guò)濾器就唯一了,只需要這樣
public class RxResponseSetFilter implements Func1<ResponseSet,Boolean> {
@Override
public Boolean call(ResponseSet resultSet) {
Boolean flag = false;
if(resultSet.state == ResultCode.SUCCESS){
flag = true;
}else {
flag = false;
ToastUtil.show(resultSet.msg);
}
return flag;
}
}
以后只要我們需要過(guò)濾結(jié)果操作的時(shí)候用這個(gè)對(duì)象就可以了
解決了數(shù)據(jù)過(guò)濾之后,我們繼續(xù)
就拿登錄來(lái)當(dāng)例子吧
Observable<ResponseSet<LoginBean>> ob = mModel.login(username, pwd);
ob
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.filter(new RxResponseSetFilter())
.subscribe(new SimpleObserver<Boolean>() {
@Override
public void onCompleted() {
updateJPushRegisterId();
}
@Override
public void onNext(Boolean aBoolean) {
startMainHome(aBoolean);
}
});
我們暫且不談model層做了什么,現(xiàn)在讀一遍易讀性非常好的代碼.
先在io線程請(qǐng)求數(shù)據(jù),請(qǐng)求到時(shí)候切換回主線程,然后對(duì)結(jié)果進(jìn)行過(guò)濾,過(guò)濾掉非成功的操作,之后注冊(cè)消費(fèi)者SimpleObserver,做進(jìn)入主頁(yè)面操作,后臺(tái)更新推送id操作
現(xiàn)在我們把目光聚焦到這一段代碼,思考一下有什么是多余的,或者說(shuō)是以后的操作都需要重復(fù)的編寫,
我們發(fā)現(xiàn)網(wǎng)絡(luò)請(qǐng)求的io線程,切換回主線程,過(guò)濾操作都是需要重復(fù)做的,現(xiàn)在我們就要考慮一件事,怎么model層返回的事件進(jìn)行統(tǒng)一的變換,不需要重復(fù)寫代碼,即便是少寫一行也算事吧?
Rxandroid提供給我們這樣一組對(duì)象和方法 Transformer及compose(Transformer) 而compose會(huì)返回給我們一個(gè)觀察者(經(jīng)過(guò)一系列變換的觀察者),這樣我們就想清楚了,上代碼看一下
這個(gè)是切換線程
babyServes.addBabyCycle(babyId,content,pictures).compose(RxTransformUtils.<ResponseSet>defaultSchedulers());
public class RxTransformUtils {
public static <T> Observable.Transformer<T, T> defaultSchedulers() {
return new Observable.Transformer<T, T>() {
@Override
public Observable<T> call(Observable<T> tObservable) {
return tObservable
.unsubscribeOn(Schedulers.io())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
;
}
};
}
}
你可能會(huì)問(wèn)了,你現(xiàn)在想過(guò)濾結(jié)果,怎么辦,過(guò)濾結(jié)果這個(gè)操作不一定是所有的方法都需要過(guò)濾,而線程的切換卻是必須的,所以我在這沒(méi)有寫統(tǒng)一的過(guò)濾
在所有數(shù)據(jù)邏輯處理完成之后,是時(shí)候注冊(cè)消費(fèi)了,我們又考慮到好像有很多業(yè)務(wù)需要有一個(gè)加載框,在網(wǎng)絡(luò)請(qǐng)求結(jié)束后關(guān)閉,怎么做?
我是這樣做的,定義了兩個(gè)消費(fèi)者 SimpleObserver 和 ProgressObserver
看一下代碼
//就是一個(gè)實(shí)現(xiàn)了Subscriber接口的方法,空實(shí)現(xiàn)我們不關(guān)心的方法,我們關(guān)心的方法重寫一下就行
public class SimpleObserver<T> extends Subscriber<T> {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onNext(T o) {
}
}
//一個(gè)帶有進(jìn)度條的消費(fèi)者觀察者,是一個(gè)抽象類,抽象方法為injectContext需要在實(shí)例化的時(shí)候注入當(dāng)前界面的Context,
public abstract class ProgressObserver<T> extends Subscriber<T> {
private static final String TAG = "ProgressObserver";
private Context mContext;
private ProgressDialog dialog;
@Override
public void onCompleted() {
dialog.dismiss();
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
Log.i(TAG,"onError");
ToastUtil.show("服務(wù)器懵逼了...");
dialog.dismiss();
}
@Override
public abstract void onNext(T t);
@Override
public void onStart() {
super.onStart();
mContext = injectContext();
dialog = new ProgressDialog(mContext);
dialog.setMessage("加載中...");
dialog.setCancelable(false);
dialog.show();
}
//進(jìn)度條需要依賴于context,注入當(dāng)前界面的context就行
public abstract Context injectContext();
}
這樣就解決了需要進(jìn)度條的情況.
頭快炸了,第一次寫技術(shù)性的博客,不不,是第一次寫博客,也都是自己在平時(shí)寫代碼的時(shí)候總結(jié)的經(jīng)驗(yàn)和遇到的坑以及看自己糟糕透頂?shù)拇a的一次次改變,下次再寫吧,寫寫rxjava的變換,我遇到的實(shí)際情況是這樣的.
例:
現(xiàn)在用戶在注冊(cè)完成之后要進(jìn)行登錄,當(dāng)然是在后臺(tái)偷偷進(jìn)行登錄,怎么做?用rxjava怎么做?
很多人都是嵌入嵌入在嵌入,迷之縮進(jìn),我剛開始也是這樣
動(dòng)動(dòng)腦子想一下,我們下一篇博客繼續(xù)討論
我們始終都堅(jiān)信著一件事:
有問(wèn)題不可怕,可怕的是我們知道了問(wèn)題的所在還不去解決問(wèn)題
寫在最后:
第一次寫,有寫的不好的地方請(qǐng)大家批評(píng)指正,我會(huì)努力修改認(rèn)真吸取大家的經(jīng)驗(yàn)為我所用然后反哺大家的,評(píng)論我每天都會(huì)認(rèn)真看,認(rèn)真思考,認(rèn)真回復(fù)的,萬(wàn)份感謝
我是新來(lái)的,余生還請(qǐng)大家多多指教!????