四.mvp模式(使用MVPPluge插件稻扬,自動生成MVP的類文件以及該插件的改裝)
MVP模式理解起來很容易,結(jié)合安卓羊瘩,簡單概括:無非就是在activty中將網(wǎng)絡(luò)請求等與數(shù)據(jù)相關(guān)的操作封裝到一個(gè)P類中泰佳,activty中持有P的引用,同時(shí)P中又包含activty中調(diào)度View的一些方法尘吗,這樣activity中只寫業(yè)務(wù)邏輯逝她,請求獲取數(shù)據(jù)全靠P去實(shí)現(xiàn),好處是在請求很多睬捶,業(yè)務(wù)邏輯復(fù)雜的情況下整個(gè)activity的代碼依然清晰黔宛,同時(shí)activity的職責(zé)也更單一。
曾經(jīng)看到過一句話擒贸,大致的意思是:
"代碼的規(guī)范臀晃,業(yè)務(wù)邏輯的清晰,類之間的解耦等等一定是通過增加而不是減少代碼來實(shí)現(xiàn)的酗宋』蹋”
同樣,mvp的問題也就暴露出來了蜕猫,要實(shí)現(xiàn)activity中模塊的分離寂曹,需要增加P類,需要實(shí)現(xiàn)各種接口回右,因此隆圆,之前的項(xiàng)目中一直未使用mvp,都是在activity中寫到底翔烁,最多就是抽取一些方法到util渺氧,helper等類中。
這回雖然引入了mvp蹬屹,但是實(shí)際方法中同樣也是分情況來看侣背,如果要實(shí)現(xiàn)的功能很簡單白华,或者功能很邊緣化,甚至小迭代時(shí)間緊贩耐,任務(wù)重的時(shí)候還是直接activity中操作數(shù)據(jù)弧腥,只有在一些比較重要,核心的模塊里 通過mvp來做更優(yōu)雅的實(shí)現(xiàn)潮太。
題外話到此管搪,下面重點(diǎn)來說說這次項(xiàng)目搭建中mvp的使用過程。
使用過程:
1.android studio引用 MVPPlugin插件
2.MVPPlugin插件的使用
3.MVPPlugin插件的改造
可以看到這里mvp模式都是圍繞一個(gè)“ MVPPlugin插件”的東西铡买,這個(gè)插件是干嘛的更鲁? 其實(shí)就是一個(gè)幫助開發(fā)者一鍵生成mvp模式所需要的一些接口類,P類等奇钞。具體的使用方法可以看看這個(gè)
如果你之前也只是聽說并未真正使用過mvp模式開發(fā)澡为,那么這個(gè)插件將極大的節(jié)約你的mvp上手的時(shí)間成本,基本屬于拿來就用蛇券。 不過用歸用缀壤,插件幫你生成的那些類的源碼還是要好好看看,特別是基類纠亚,做到消化吸收塘慕!可能自己暫時(shí)寫不出來,但起碼要理解原理蒂胞。
五.網(wǎng)絡(luò)框架的接入(RXJava2+Retrofit2.0+OKHTTP)
這三個(gè)玩意兒無疑是目前灰惩寄兀火的項(xiàng)目,但凡做android開發(fā)骗随,可以說不一定用過蛤织,但絕對都聽過,了解過鸿染。
Rxjava:
是個(gè)很神奇的東西指蚜。網(wǎng)上的描述一大堆:什么 事件流推動,響應(yīng)式編程涨椒,觀察訂閱等等 我剛接觸的時(shí)候百度了半天摊鸡,也沒弄清楚到底是個(gè)啥,使用有啥好處蚕冬,直到看到這篇文章免猾,
我將作者稱為“管神”,水管的管囤热。
http://www.reibang.com/u/c50b715ccaeb([給初學(xué)者的RxJava2.0教程)
哈哈 有一個(gè)系列猎提,幫助上手Rxjava,但實(shí)際上你可能會跟我一樣旁蔼。 系列文章看了一遍锨苏,感覺懂了疙教,但是想在項(xiàng)目里面用,發(fā)現(xiàn)用不上蚓炬。
然后結(jié)合Retrofit2.0+OKHTTP直接先在項(xiàng)目里面用起來松逊,用熟練了再把文章看一遍,哦肯夏,恍然大悟-事件推動。
說得好像有點(diǎn)云里霧里犀暑,先別噴我驯击,咋先用上再說。
Retrofit2.0:
是個(gè)很方便的東西耐亏,說它方便徊都,是因?yàn)樗褚粋€(gè)接口,本身沒有實(shí)現(xiàn)广辰,沒有功能暇矫,但是你把Gson框架放進(jìn)去,它就能json解析择吊,你把OKHTTP放進(jìn)去它就能發(fā)送網(wǎng)絡(luò)請求李根,你把Rxjava放進(jìn)去它就能進(jìn)行發(fā)送事件流。
那么整個(gè)的連接起來是神馬几睛?
RXJava2+Retrofit2.0+OKHTTP+GSON=“發(fā)送 網(wǎng)絡(luò)請求 并自動解析json數(shù)據(jù)的事件流”
可能你會說了房轿,作者你又裝X,神馬框架不能發(fā)送網(wǎng)絡(luò)請求所森,然后解析json數(shù)據(jù)囱持?
但我會告訴你,這個(gè)X我裝定了焕济!重點(diǎn)是“事件流”三個(gè)字》鬃保現(xiàn)在你給我0分,等你把上面推薦的rxjava文章看完理解之后或許你會重新打出97分晴弃。
為了增加說服力掩幢,這里我給出一個(gè)復(fù)雜的場景:
同時(shí)壓縮兩張圖片,都壓縮成功后調(diào)用上傳接口肝匆,并將上傳接口返回的部分?jǐn)?shù)據(jù)作為參數(shù)粒蜈,請求另外一個(gè)接口。
這個(gè)需求涉及的東西還不少旗国,圖片壓縮枯怖,線程變換,網(wǎng)絡(luò)請求嵌套能曾。如果不用rxjava度硝,可能最常規(guī)的方法應(yīng)該是這樣:
開兩個(gè)子線程壓縮圖片肿轨,handler發(fā)送成功信號到主線程,while循環(huán)等待蕊程,兩個(gè)標(biāo)識都成功然后發(fā)送網(wǎng)絡(luò)請求椒袍,請求成功回調(diào)再嵌套一個(gè)請求。
上面的實(shí)現(xiàn)基本上能滿足這個(gè)需求藻茂,但是無論從代碼量驹暑,代碼可讀性等等而言都不是那么優(yōu)雅,但是如果采用rxjava辨赐,利用它自帶的線程切換优俘,操作符zip,事件流轉(zhuǎn)換flatMap的話掀序,你可以寫出一段邏輯清晰帆焕,代碼量少的鏈?zhǔn)酱a,不恭,叶雹,,换吧,
好吧折晦,可能這樣描述無法達(dá)到那種 “聽到后連我自己都害怕”的效果,式散,但是筋遭,請相信,暴拄,效果真的很牛X漓滔,誰用誰知道。
基本使用:
1.初始化一個(gè)OkHttpClient
OkHttpClient mOkHttpClient = new OkHttpClient.Builder()
.readTimeout(DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.SECONDS)
.writeTimeout(DEFAULT_WRITE_TIMEOUT_MILLIS, TimeUnit.SECONDS)
//設(shè)置連接超時(shí)時(shí)間
.connectTimeout(DEFAULT_CONNECT_TIMEOUT_MILLIS, TimeUnit.SECONDS)
//設(shè)置緩存
.cache(getDefaultCache())
//日志攔截器
.addInterceptor(loggingInterceptor)
//頭部攔截器
.addInterceptor(new HeadsInterceptor())
//設(shè)置SSH 兼容https
.sslSocketFactory(sslSocketFactory.sSLSocketFactory, sslSocketFactory.trustManager)
//默認(rèn)全部信任
.hostnameVerifier(SSHUtil.getHostnameVerifier())
.build();
2.通過Retrofit.Builder初始化一個(gè)RetrofitApi對象乖篷,過程中設(shè)置請求代理(就是前面mOkHttpClient )响驴,設(shè)置數(shù)據(jù)解析,設(shè)置Rxjaxa
Retrofit.Builder builder = new Retrofit.Builder()
//設(shè)置網(wǎng)絡(luò)請求實(shí)現(xiàn)
.client(mOkHttpClient)
//設(shè)置json數(shù)據(jù)解析實(shí)現(xiàn)
.addConverterFactory(GsonConverterFactory.create())
//RXJAVA適配器
.addCallAdapterFactory(RxJava2CallAdapterFactory.create());
//這里主域名的設(shè)置單獨(dú)來弄撕蔼,萬一后臺奇葩豁鲤,一個(gè)app里兩個(gè)主域名的話可以再建一個(gè)API_X的接口類,這里類似的單獨(dú)再配置他的baseurl
RetrofitApi httpApi = builder.baseUrl(Constants.APP_HOST)
.build()
.create(RetrofitApi.class);
//配置了RxJava2CallAdapterFactory即集合RXjava的話httpApi調(diào)用xxx方法可以直接返回Observable鲸沮,然后拿來subscribe
//不結(jié)合RXjava的話返回httpApi調(diào)用xxx方法返回Call對象琳骡,然后拿來enqueue發(fā)請求
//結(jié)合RXjava的優(yōu)點(diǎn):更靈活,鏈?zhǔn)秸{(diào)用讼溺,線程切換方便楣号,各種操作符效果吊炸天。 說白了就是可以在處理很復(fù)雜嵌套循環(huán)請求的時(shí)候利用RXJAVA,簡化開發(fā)
//不過RXJAVA自身的學(xué)習(xí)成本的確是個(gè)問題炫狱,我到現(xiàn)在也是云里霧里藻懒,不過既然是框架,视译,嬉荆,先用再說啦!
3.調(diào)用RetrofitApi實(shí)例里的業(yè)務(wù)接口并處理回調(diào)
httpApi.getIndexData().subscribe(new Observer<TDataBean<IndexMultBean>>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(TDataBean<IndexMultBean> value) {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
})
全局封裝說明:
真正使用過程中肯定不會每次請求都重復(fù)上面過程酷含,都會做封裝鄙早,這里是給出的封裝實(shí)例,可能并不完美第美,歡迎批評指出哈蝶锋!
三個(gè)類,分別是:
HttpUtil:初始化retrofit什往,并根據(jù)服務(wù)器主域名生成一個(gè)全局的 RetrofitApi對象
RetrofitApi: 注解的方式定義出具體的業(yè)務(wù)接口
ApiManager:網(wǎng)絡(luò)請求發(fā)起類,通過獲取HttpUtil中的retrofit實(shí)例慌闭,調(diào)用retrofit中定義的業(yè)務(wù)接口并傳入?yún)?shù)别威。
在MVP模式的Presenter 里調(diào)用方式如下:
public class LoginPresenter extends BasePresenterImpl<LoginContract.View> implements LoginContract.Presenter {
private void textHttp() {
ApiManager.login("acount","pwdString").subscribe(new HttpObserver<TDataBean<UserInfo>>(mView) {
@Override
public void onSucceed(TDataBean<UserInfo> value) {
}
@Override
public void onDefeat(TDataBean<UserInfo> value) {
super.onDefeat(value);
}
});
}
}
HttpObserver繼承自O(shè)bserver<T>,對請求結(jié)果做了進(jìn)一步的封裝驴剔,具體見源碼
部分源碼展示:
1.添加引用依賴
//OKhttp
compile 'com.squareup.okhttp3:okhttp:3.9.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.7.0'
//retrofit
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
compile 'com.squareup.retrofit2:converter-gson:2.2.0'
//rxjava
compile 'io.reactivex.rxjava2:rxjava:2.0.1'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
2.請求模塊初始化
public class HttpUtil {
//超時(shí)時(shí)間
private static final long DEFAULT_READ_TIMEOUT_MILLIS = 20;//提交數(shù)據(jù)超時(shí)時(shí)間
private static final long DEFAULT_WRITE_TIMEOUT_MILLIS = 20;//提交數(shù)據(jù)超時(shí)時(shí)間
private static final long DEFAULT_CONNECT_TIMEOUT_MILLIS = 10;//請求超時(shí)時(shí)間
//緩存大小
private static final long HTTP_RESPONSE_DISK_CACHE_MAX_SIZE = 10 * 1024 * 1024;
//OkHttpClient
private OkHttpClient mOkHttpClient;
//API接口
private RetrofitApi httpApi;
private HttpUtil() {
initOKHttp();
initRetrofit();
}
public static HttpUtil getInstance() {
return ClassHolder.instace;
}
private static class ClassHolder {
static final HttpUtil instace = new HttpUtil();
}
private void initOKHttp() {
//日志攔截器省古,專業(yè)處理請求過程中的日志 ,header、body數(shù)據(jù)
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
//根據(jù)包的類型控制日志輸出與否
loggingInterceptor.setLevel(BasePackageUtil.isApkDebugable() ? HttpLoggingInterceptor.Level.BODY : HttpLoggingInterceptor.Level.NONE);
//SSH相關(guān)設(shè)置丧失,處理https請求的豺妓,默認(rèn)全部信任,不添加的話遇到https請求可能會出異常布讹,WebView琳拭,Picasso,Gilde等框架都會有此問題
SSLParams sslSocketFactory = SSHUtil.getSslSocketFactory();
mOkHttpClient = new OkHttpClient.Builder()
.readTimeout(DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.SECONDS)
.writeTimeout(DEFAULT_WRITE_TIMEOUT_MILLIS, TimeUnit.SECONDS)
//設(shè)置連接超時(shí)時(shí)間
.connectTimeout(DEFAULT_CONNECT_TIMEOUT_MILLIS, TimeUnit.SECONDS)
//設(shè)置緩存
.cache(getDefaultCache())
//日志攔截器
.addInterceptor(loggingInterceptor)
//頭部攔截器
.addInterceptor(new HeadsInterceptor())
//設(shè)置SSH 兼容https
.sslSocketFactory(sslSocketFactory.sSLSocketFactory, sslSocketFactory.trustManager)
//默認(rèn)全部信任
.hostnameVerifier(SSHUtil.getHostnameVerifier())
.build();
}
private void initRetrofit() {
Retrofit.Builder builder = new Retrofit.Builder()
//設(shè)置網(wǎng)絡(luò)請求實(shí)現(xiàn)
.client(mOkHttpClient)
//設(shè)置json數(shù)據(jù)解析實(shí)現(xiàn)
.addConverterFactory(GsonConverterFactory.create())
//RXJAVA適配器
.addCallAdapterFactory(RxJava2CallAdapterFactory.create());
//這里主域名的設(shè)置單獨(dú)來弄描验,萬一后臺奇葩白嘁,一個(gè)app里兩個(gè)主域名的話可以再建一個(gè)API_X的接口類,這里類似的單獨(dú)再配置他的baseurl
httpApi = builder.baseUrl(Constants.APP_HOST)
.build()
.create(RetrofitApi.class);
//配置了RxJava2CallAdapterFactory即集合RXjava的話httpApi調(diào)用xxx方法可以直接返回Observable膘流,然后拿來subscribe
//不結(jié)合RXjava的話返回httpApi調(diào)用xxx方法返回Call對象絮缅,然后拿來enqueue發(fā)請求
//結(jié)合RXjava的優(yōu)點(diǎn):更靈活,鏈?zhǔn)秸{(diào)用呼股,線程切換方便耕魄,各種操作符效果吊炸天。 說白了就是可以在處理很復(fù)雜嵌套循環(huán)請求的時(shí)候利用RXJAVA彭谁,簡化開發(fā)
//不過RXJAVA自身的學(xué)習(xí)成本的確是個(gè)問題吸奴,我到現(xiàn)在也是云里霧里,不過既然是框架,奄抽,蔼两,先用再說啦!
}
public OkHttpClient getmOkHttpClient() {
return mOkHttpClient;
}
public RetrofitApi getHttpApi() {
return httpApi;
}
public Cache getDefaultCache() {
String dir = BaseFileUtil.getHttpCacheDir();
File file = new File(dir);
Cache cache = new Cache(file, HTTP_RESPONSE_DISK_CACHE_MAX_SIZE);
return cache;
}
}
3.API封裝與調(diào)用
public interface RetrofitApi {
/*獲取首頁數(shù)據(jù)*/
@POST(Constants.FUNCTION_INDEX)
Observable<TDataBean<IndexMultBean>> getIndexData();
/*登錄*/
@POST(Constants.FUNCTION_LOGIN)
Observable<TDataBean<UserInfo>> login(@Body JsonObject object);
}
public class ApiManager {
//統(tǒng)一設(shè)置線程
private static <T> Observable<T> subscribeOn(Observable<T> observable) {
return observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
//獲取RetrofitApi實(shí)例
private static RetrofitApi getJavaApi() {
return HttpUtil.getInstance().getHttpApi();
}
/***************************************業(yè)務(wù)邏輯接口**************************************/
/*獲取首頁數(shù)據(jù)*/
public static Observable<TDataBean<IndexMultBean>> getIndexData() {
Observable<TDataBean<IndexMultBean>> observable = getJavaApi().getIndexData();
return subscribeOn(observable);
}
/*獲取首頁數(shù)據(jù)*/
public static Observable<TDataBean<UserInfo>> login(String username, String pwd) {
JsonObject data = new JsonObject();
data.addProperty("mobile", username);
data.addProperty("password", pwd);
data.addProperty("device_token", DeviceUtils.getDeviceId());
Observable<TDataBean<UserInfo>> observable = getJavaApi().login(data);
return subscribeOn(observable);
}
}
完整的demo地址:
https://gitee.com/lunguoguo/revised_development_framework.git (已更新)