1. 程式人生 > >rxjava+retrofit+mvp封裝

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,這些使用難度都不大。 框架程式碼