rxjava+retrofit+mvp封裝
簡介 工作期間有空我就學習rxjava2的使用,現在結合mvp的架構給大家封裝出一個開發框架,考慮到程式碼的重用性,資料介面的加密解密,我這裡做出了可以商用的,不單是學習。 框架程式碼下載
本框架所用到包
compile 'com.android.support:design:25.3.1' compile 'com.android.support:cardview-v7:25.0.1' compile 'com.github.bumptech.glide:glide:3.7.0' //http compile 'io.reactivex.rxjava2:rxjava:2.1.1' compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:converter-gson:2.3.0' compile 'com.squareup.retrofit2:converter-scalars:2.3.0' compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' compile 'com.squareup.okhttp3:logging-interceptor:3.8.1' compile 'com.tbruyelle.rxpermissions2:rxpermissions:
[email protected]' //rxbind compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0' compile 'com.jakewharton.rxbinding2:rxbinding-support-v4:2.0.0' compile 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.0.0' //上拉下拉控制元件 compile 'com.jcodecraeer:xrecyclerview:1.5.9' //圖片壓縮庫 compile 'id.zelory:compressor:2.1.0' //動畫 compile 'com.nineoldandroids:library:2.4.0' //輪播圖-bug,點選回撥不能再內部類中,更新圖片list要是新的物件 compile 'com.youth.banner:banner:1.4.10'
都是常用的,結合了網上很多大牛的想法封裝出來,由於工作時間忙,一直沒有系統的整理處理,可能還有不足請見諒,現在給大家瞅瞅…
先看使用方法,不然怎麼知道好不好
裡面原始碼檔案demo資料夾放的都是例子,LoginPresenter2,Recycle2Presenter都是封裝後使用,LoginPresenter,RecyclePresenter是未封裝, 1.來一個登入的介面 由於是mvp架構,我們建立LoginPresenter2,裡面是處理view層傳來的資料交給model層,這裡考慮到每個業務模組一般都有網路訪問,那麼每次都寫這樣的介面很煩啊,所以做了封裝,建立了基類,直接繼承使用就可以。
public class LoginPresenter2 extends HttpPresenter<LoginContract.ILoginView> {
public LoginPresenter2(LoginContract.ILoginView view) {
super(view);
}
@Override //第一種傳參方式 map
public void doHttpRequest(Map<String, String> map) {
mModel.httpMap("http://120.24.44.102/fuc/api/web/userapi/login.html", GlobalCode.encryArgs(map))
.subscribe(new Consumer<ResponseBody>() {
@Override
public void accept(@NonNull ResponseBody responseBody) throws Exception {
String result = new String(responseBody.string());
//顯示網路返回的資料
mView.showHttpResponse(result);
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
GlobalCode.printLog(Log.getStackTraceString(throwable));
}
});
}
@Override //第二種傳參方式 物件
protected void doHttpRequest2(Object object) {
final LoginRequest loginRequest = (LoginRequest) object;
mModel.httpMap("http://120.24.44.102/fuc/api/web/userapi/login.html", setArgField(loginRequest))
.subscribe(new Consumer<ResponseBody>() {
@Override
public void accept(@NonNull ResponseBody responseBody) throws Exception {
String result = new String(responseBody.string());
//顯示網路返回的資料
mView.showHttpResponse(result);
//本地測試 資料物件轉化
LoginResponse loginResponse = GlobalCode.getHttpResponse(result, LoginResponse.class);
GlobalCode.printLog(loginResponse.getType_id() + "");
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
GlobalCode.printLog(throwable);
}
});
}}
//契約類
public class LoginContract {
interface ILoginView extends BaseView {
void showHttpResponse(String str);
ImageView showImgBg();
}
// interface ILoginPresenter extends BasePresenter {
// void doHttpRequest(Map<String, String> map);
// }
// interface IModel extends BaseModel {
// Observable<ResponseBody> httpMap(Map<String, String> map);
// }
}
看到請求簡單嗎,用map攜帶請求引數,這裡動態傳入URL,不用每次都去寫一個observable,注意到就是HttpPresenter類,就是我封裝後的,
public class HttpPresenter<T extends BaseView> extends BasePresenterImpl<T> implements GlobalPresenter {
protected GlobalModel mModel;
public HttpPresenter(T view) {
super(view);
this.mModel = new GlobalModel() {
@Override
public Observable<ResponseBody> httpMap(String url, Map<String, String> map) {
return ApiEngine.getInstance().getApiService().doRequestUrl(url, map)
.compose(RxSchedulers.<ResponseBody>io2main())
.doOnSubscribe(new Consumer<Disposable>() { //在subscribe()前呼叫
@Override
public void accept(@NonNull Disposable disposable) throws Exception {
addDispoable(disposable);
LoadingDialog.showprogress(mView.getCurContext(), "正在載入..");
}
})
.doOnTerminate(new Action() { //action沒有返回值,function有返回值
@Override
public void run() throws Exception {
LoadingDialog.dismissprogress();
System.out.println("onTerminate>>>>>");
}
});
}
};
}
@Override
public void doHttpRequest(Map<String, String> map) {
}
protected void doHttpRequest2(Object obj) {
}
}
//類中的BaseView,BasePresenterImpl,GlobalPresenter,都是一次寫好的基類,呼叫者繼承使用就好,這裡過載HttpPresenter(T view)看到動態建立observable,帶等待框,可控制訂閱disposable(防止activity退出網路仍然請求,持有View會記憶體洩漏)
@FormUrlEncoded
@POST()
Observable<ResponseBody> doRequestUrl(@Url String url, @FieldMap Map<String, String> map); //動態建立observable,共用post請求
看到我們返回的網路物件會是ResponeBody,由於商業軟體都是帶加密的,返回一般就是key="?",data="?",我這裡的使用方法是用//本地測試 資料物件轉化 LoginResponse loginResponse = GlobalCode.getHttpResponse(result, LoginResponse.class); GlobalCode.printLog(loginResponse.getType_id() + "");
大家一般用的都是物件或者map儲存我們從網路接收到json欄位,所有要寫用Gson解析的成
//解析一個物件 //存在錯誤收集 httpjson
public static <T> T getHttpResponse(String result, Class<T> cls) {
String httpResponse = result;
JSONObject jsonObject = null;
//返回的資料{code:0,message:0k,data:{}}
//我們要判斷返回碼200(200成功返回JsonObject物件),其他碼要彈出錯誤提示(return null)
jsonObject = httpJson(httpResponse);
if (null == jsonObject) return null;
String jsonstring = String.valueOf(jsonObject);
T t = null;
try {
JSONObject jsonObject1 = new JSONObject(jsonstring);
JSONObject jsondata = jsonObject1.getJSONObject("data");
// printLog(String.valueOf(jsondata));
Gson gson = new Gson();
t = gson.fromJson(String.valueOf(jsondata), cls);
} catch (JSONException e) {
e.printStackTrace();
}
return t;
}
咋LoginAty類支援toolbar,mvp解耦
public class LoginAty extends BaseActivity<LoginPresenter2> implements LoginContract.ILoginView
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login_aty);
}
@Override
protected LoginPresenter2 onCreatePresenter() {
return new LoginPresenter2(this);
}
@Override
public void showHttpResponse(String str) {
tv_txt.setText(str);
GlobalCode.printLog("get_log_text" + str);
}
封裝以後大大減少開發程式碼,很多重用的邏輯都在基類中寫好。
2.我們再看列表介面的請求有多快,
public class Recycle2presenter extends ReHttpListPresenter<ReContract.IRecycleView, IdCell> {
private ReAdapter mAdapter;
public Recycle2presenter(ReContract.IRecycleView view, String url) {
super(view, url);
}
@Override
protected Map<String, String> setArgsMap() {
mArgsMap.put("page", mCurrentPage + "");
mArgsMap.put("pagesize", 10 + "");
mArgsMap.put("phone", "13232508893");
mArgsMap.put("password", "123456");
mArgsMap.put("type", "PASSWORD");
return super.setArgsMap();
}
@Override
protected void setReAdapter() {
super.setReAdapter();
mAdapter = new ReAdapter(mView.getCurContext(), mDataList, R.layout.list_item, new GlobalReHolder.onItemGlobalClickListener() {
@Override
public void onItemClickListener(int position) {
Toast.makeText(mView.getCurContext(), "pp-" + position, Toast.LENGTH_SHORT).show();
}
});
mView.getReView().setAdapter(mAdapter);
}
@Override
protected void handlerData(String result, boolean ismore) {
HttpListResponse<IdCell> httpListResponse = GlobalCode.getHttpResponseList(str_json2, IdCell.class);
GlobalCode.printLog(httpListResponse+"");
List<IdCell> list = httpListResponse.getSelect();
mCurrentPage++;
mPageCount = httpListResponse.getPagination().getPageCount();
if (ismore) {
mView.getReView().loadMoreComplete();
} else {
mView.getReView().refreshComplete();
}
mDataList.addAll(list); //list=null -->bug?
mDataList.addAll(list); //list=null -->bug?
mDataList.addAll(list); //list=null -->bug?
mDataList.addAll(list); //list=null -->bug?
mAdapter.notifyDataSetChanged();
}
String str_json2 = "{\"code\":\"0\",\"message\":\"獲取成功\"," +
"\"data\":{\"select\":[{\"id\":4,\"user_id\":14,\"name\":\"羅泉清\",\"phone\":\"00989778278\",\"gender\":1,\"car_plate\":\"粵C2N111\",\"id_card\":\"441481199010121111\",\"car_type\":\"大貨車\"},{\"id\":6,\"user_id\":14,\"name\":\"羅泉清\",\"phone\":\"00989778005\",\"gender\":1,\"car_plate\":\"粵C2N111\",\"id_card\":\"441481199010124009\",\"car_type\":\"大貨車\"}]," +
"\"pagination\":{\"totalCount\":2,\"pageCount\":2}}}";}
如果你要建立一個列表,你只要建立行資料bean-Idcell,設配器ReAdapter,網路請求傳參在setArgsMap(),介面卡建立在setReAdapter(),獲取到上拉下拉分頁資料在handlerData()處理。 在ListAty我們解耦view層的顯示裡面只有View的初始化,其他業務邏輯都交給Recycle2Presenter實現。
public class ListAty extends BaseActivity<GlobalPresenter> implements ReContract.IRecycleView
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list_aty);
this.setBarTitle("通用類集合視窗");
}
//只要重寫該方法就可以建立新的listaty.
@Override
protected GlobalPresenter onCreatePresenter() {
return new Recycle2presenter(this,"http://120.24.44.102/fuc/api/web/userapi/login.html");
}
看到列表Activity,只寫了這麼少程式碼,是由於繼承封裝網路請求的RehttpListpresenter類,該類是專門用於Recycleview分頁上拉下拉的網路請求,給你看下里面構造方法實現網路請求的程式碼,依然是動態url
//必須繼承
public ReHttpListPresenter(T view, String url) {
super(view);
this.mHttpUrl = url;
this.mModel = new GlobalModel() {
@Override
public Observable<ResponseBody> httpMap(String url, Map<String, String> map) {
return ApiEngine.getInstance().getApiService().doRequestUrl(url + "?page=" + mCurrentPage, map) //注意加密
.compose(RxSchedulers.<ResponseBody>io2main())
.doOnSubscribe(new Consumer<Disposable>() {
@Override
public void accept(@NonNull Disposable disposable) throws Exception {
addDispoable(disposable);
}
});
}
};
//這裡是每次上拉下拉都會呼叫到資料接收方法,返回的依然是一個ResponseBody,
protected void loadData(Map<String, String> map, final boolean ismore) {
mModel.httpMap(mHttpUrl, GlobalCode.encryArgs(map)).subscribe(new Consumer<ResponseBody>() {
@Override
public void accept(@NonNull ResponseBody responseBody) throws Exception {
handlerData(responseBody.string(), ismore);
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
GlobalCode.printLog(Log.getStackTraceString(throwable));
if (ismore) {
mView.getReView().loadMoreComplete();
} else {
mView.getReView().refreshComplete();
}
}
});
}
對於接收到網路jsonobject文字,我們用
//返回一個JsonObject
//我的list資料格式可以看變數str_json2,有分頁,你們可以根據自己格式修改函式方法得到//自己想要的,GlobalCode是一個建立一個工具類。
HttpListResponse<IdCell> httpListResponse = GlobalCode.getHttpResponseList(str_json2, IdCell.class);
//public static <T> HttpListResponse<T> getHttpResponseList(String result, Class<T> cls)
我們注意一下加密,post請求是將引數放到body中,加密用的rsa,aes,我們發到伺服器介面的加密資料返回格式會是{code:0,message:"",key:"?",data:"?"},我的請求介面很多帶共用的引數,如token,每次請求介面都寫這些引數也是浪費,在post請求中用GlobalCode.encryArgs()處理公共引數,已經考慮到加密和不加密,你不要管太多,只傳引數就可以,當然傳物件也可以我做了封裝。。。
對於rxjava使用
大學那會就看rxjava1.0,覺得看不懂,很難用,不想去學,後來在工作中很多參考的專案都用了rxjava2,我就認真學習了,發現你明白以後,碼程式碼是真的快,不用管handler,不用去多次判斷某些邏輯(rxbinding),複雜的業務就也好用,例如多層網路巢狀,聯合判斷,多個網路請求後不同資料更新UI。 它自帶的執行緒排程實在太厲害,精髓是subscribeOn()指定未指定執行緒的observable(被觀察者),且第一次有效,observeOn()每次有效指定執行緒切換。我開始看他的執行緒排程方法位置也懵逼的,我靠什麼意思,後來看了很多文章感悟出來,有些總結話你每看一次都會有新的感悟,_。如果你要複雜的使用flatmap,scan,filter,map這些操作符,一定要把執行緒排程理解好,單純使用沒什麼切換的就寫個ObservableTransformer. RxTextUtil裡面有rxjava2的學習程式碼。Rxbinding,RxPermission,這些使用難度都不大。 框架程式碼