Android-Retrofit2+Rxjava2之网络通用请求-初步封装

一直也是用MVP模式,也就结合Rx做网络请求,Rx子线程和UI线程的切换是相当的方便(小白后面准备看下相关的切换的源码)。MVP了,一般有presenter层处理网络请求,以及调用view相关的方法,实现数据和View直接的交互。

image

从图来看也是View仅仅涉及到Presenter层的交互。当前啦,实际上View里面也有实体类的影子,不过仅仅只是拿实体类进行了界面的相关展示 - 小白使用MVP就是这种情况,比如界面里面会有控件拿实体类进行设置的一些操作:

image

之前小白有封装过BaseAdapter,那个地方我已经将这个dataModle和控件的绑定放到了一起,如果那种情况下的话,界面里面的东西就更少了。或许还得看你如何做的封装。你不能说就可以完全分离,准确讲应该是减低耦合度! 使结构更加清下,逻辑更加明了。更便于维护和完善。

而MVC耦合度就高了

image

View涉及到实体类,控制类的相关处理,只能说代码多是一方面,逻辑性也烦,维护起来费劲。以前的东家的工程我现在看起来都有点费劲,该重写了。

关于这个MVVM模式,有童鞋也在用。配合dataBinding据说还可以。很早之前有尝试过案例,小白隐隐感觉貌似还行吧。后面再具体分析看看,借鉴的思路,说不定MVVM更好用妮?

步入正题。。了解了MVP,也尝试使用过的话,如果作为像小白这样的新手,我基本上会写很多presenter,基本就是一个界面一个,而且处理都很类似..加上之前的预处理啥的,基本都是很多相同的处理逻辑和代码。

image

感觉有点多了,对吧?而且下面还有对应的contract(View和Presenter的纽带)。之前我们也是进行了数据预处理Android-Retrofit2+Rxjava2之网络请求预处理-Func1、Subscriber ,这样的话基本上数据请求都是类似如下的方式(看个分页列表的请求吧):

image
image
image

每个列表页面基本都是这种操作??为何不封装一个请求方法呢? 如果是一个请求方法怎么根据不同的实体类(dataModel)的类型,通过请求的Json进行对应类型的转换呢?

小白起初想法:泛型 之前有接触和实践了泛型,所以小白就想,应该可以试一试.

但问题来了? 像retrifit这种泛型接口定义是不能够被正确执行的(小白本想按照这种思路,最后都写完逻辑了,发现不得行):

image

以上是不行的,关于这个有网友和我一样,感觉自己像“瓜皮”Retrofit2.0简单封装成统一接口处理网络请求 哈哈...

小白目前是这样的:1. 以String作为data的真实数据类型

image

2. 然后通过Fastjson将其转换为对应的实体类,然后将其结构返回给请求者的回调(onSuccess方法).

3. 如果你试图将拿到的data直接利用fastjson转换为泛型类型,那样不行的,会报错的(人家本来就需要一个实体类进行转换,你还给一个泛型)

image
image

那怎么搞了? 想要封装通用,又不想写太多的请求方法?小白想,可以提供一个接口,有视图界面负责创建和实现转换,并返回泛型类型。

image

就是这样,可以滴...

image

而视图里面实现如下:

image

这样就解决了这个转换实体类的问题。嗯嗯。貌似还可以的样纸。 小白又想,或许可以提供一个string转实体类的管理类,专门定义各种实体类的转换,并返回泛型类型,应该可以这样。后面看看咯...

然后继续回到这个请求接口定义的地方

image

我把之前需要的参数放到了@Url中进行设置,这样就实现了接口通用。省去了定义多个service的情况。

最后把这个Preseter泛型类型的基础请求服务层搞一下

image

基本上就有了雏形了。。小白感觉还行,目前还是简化了不少东西。

直接贴上三个相关类:

BaseControlPresenter.java

import com.x.finance.controller.BaseControlContract;
import com.x.finance.net.BaseSubscribers;
import com.x.finance.net.ExceptionHandle;
import com.x.finance.net.ResponseFunc;
import com.x.finance.net.RetrofitManager;
import com.x.finance.net.service.BaseService;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;

/*
*@Description: 请求服务表示层
*@Author: hl
*@Time: 2018/11/21 13:54
*/
public class BaseControlPresenter<D> implements BaseControlContract.Presenter{
    private BaseControlContract.View view;                  ///< 数据【对象】请求
    private BaseControlContract.RefreshView refreshView;   ///< 数据【对象】列表请求
    private BaseService baseService;                       ///< 请求服务
    private List<Subscription> subscriptionList = new ArrayList<Subscription>();
    private DataHandler<D> dataHandler;
    private DataListHandler<D> dataListHandler;

    private BaseControlPresenter(){
        this.baseService = RetrofitManager.getInstance()
                .getmRetrofit()
                .create(BaseService.class);
    }

    private BaseControlPresenter(BaseControlContract.View _view){
        this();
        this.view = _view;
    }

    private BaseControlPresenter(BaseControlContract.RefreshView _refreshView){
        this();
        this.refreshView = _refreshView;
    }

    public BaseControlPresenter(BaseControlContract.View _view, DataHandler<D> _dataHandler){
        this(_view);
        this.dataHandler = _dataHandler;
    }

    public BaseControlPresenter(BaseControlContract.View _view, DataListHandler<D> _dataListHandler){
        this(_view);
        this.dataListHandler = _dataListHandler;
    }

    public BaseControlPresenter(BaseControlContract.RefreshView _refreshView, DataHandler<D> _dataHandler){
        this(_refreshView);
        this.dataHandler = _dataHandler;
    }

    public BaseControlPresenter(BaseControlContract.RefreshView _refreshView, DataListHandler<D> _dataListHandler){
        this(_refreshView);
        this.dataListHandler = _dataListHandler;
    }

    public BaseControlPresenter(BaseControlContract.View _view, BaseControlContract.RefreshView _refreshView, DataHandler<D> _dataHandler){
        this();
        this.view = _view;
        this.refreshView = _refreshView;
        this.dataHandler = _dataHandler;
    }

    public BaseControlPresenter(BaseControlContract.View _view, BaseControlContract.RefreshView _refreshView, DataHandler<D> _dataHandler, DataListHandler<D> _dataListHandler){
        this();
        this.view = _view;
        this.refreshView = _refreshView;
        this.dataHandler = _dataHandler;
        this.dataListHandler = _dataListHandler;
    }

    @Override
    public void requestData(final String _funUrl, HashMap<String, String> _paramList) {
        ///< 显示进度条
        view.showDialog();
        ///< 加入Token参数
        _paramList.put("access_token", UserInfoControlPresenter.getToken());
        Subscription subscription = baseService.getData(_funUrl, _paramList)
                .subscribeOn(Schedulers.io())
                .map(new ResponseFunc<>(new ResponseFunc.CallMe<D, String>() {
                    @Override
                    public D onCall(String data, String requestTime) {
                        return dataHandler.String2Data(data);
                    }
                }))
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new BaseSubscribers<D>(view) {
                    @Override
                    public void onCompleted() {
                        view.disDialog();
                    }

                    @Override
                    public void onErrors(ExceptionHandle.ResponeThrowable responeThrowable) {
                        ///< 接下来就可以根据状态码进行处理...
                        int statusCode = responeThrowable.code;
                        switch (statusCode) {
                            case ExceptionHandle.ERROR.TOKEN:
                                UserInfoControlPresenter.clearAcount();
                                view.showToast("你的账号异常,请重新登录,谢谢!");
                                break;
                            default:
                                view.showToast(responeThrowable.message);
                                break;
                        }
                        onCompleted();
                    }

                    @Override
                    public void onNext(D data) {
                        view.<D>onSucess(data);
                    }
                });

        subscriptionList.add(subscription);
    }

    @Override
    public void requestDataList(final String _funUrl, final int _pageNum, final boolean _bIsRefresh) {
        ///< 不是刷新,则显示进度条加载
        if (!_bIsRefresh){
            ///< 显示进度条 - 首次刷新会显示,其他都是空运行
            refreshView.showDialog();
        }
        Subscription subscription = baseService.getDataList(_funUrl, _pageNum, UserInfoControlPresenter.getToken())
                .subscribeOn(Schedulers.io())
                .map(new ResponseFunc<>(new ResponseFunc.CallMe<List<D>, String>() {
                    @Override
                    public List<D> onCall(String data, String requestTime) {
                        return dataListHandler.String2DataList(data);
                    }
                }))
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new BaseSubscribers<List<D>>(refreshView) {
                    @Override
                    public void onCompleted() {
                        ///< 如果是刷新才调用刷新结束接口
                        if (_bIsRefresh){
                            if (1 == _pageNum){
                                refreshView.finishRefresh();
                            }
                            else{
                                refreshView.finishLoadMore();
                            }
                        }
                        else{   ///< @首次请求, 不是刷新,取消进度条
                            refreshView.disDialog();
                        }
                    }

                    @Override
                    public void onErrors(ExceptionHandle.ResponeThrowable responeThrowable) {
                        ///< 接下来就可以根据状态码进行处理...
                        int statusCode = responeThrowable.code;
                        switch (statusCode) {
                            case ExceptionHandle.ERROR.TOKEN:
                                UserInfoControlPresenter.clearAcount();
                                refreshView.showToast("你的账号异常,请重新登录,谢谢!");
                                break;
                            default:
                                refreshView.showToast(responeThrowable.message);
                                break;
                        }
                        if (!_bIsRefresh){
                            refreshView.retryDialog();
                        }
                        else{
                            refreshView.onRequestFailer();
                        }
                        onCompleted();
                    }

                    @Override
                    public void onNext(List<D> dataList) {
                        if (dataList.size() < 1){
                            if (1 == _pageNum){ ///< @再次请求首页如果没有数据的情况下,清空列表
                                refreshView.resetItemList();
                                refreshView.<D>onSucess(dataList);
                            }
                            else{
                                refreshView.showToast("沒有糖果了!");
                            }
                        }else{
                            if (1 == _pageNum){
                                refreshView.resetItemList();
                                refreshView.<D>onSucess(dataList);
                            }
                            else{
                                refreshView.<D>onSucess(dataList);
                            }
                        }
                    }
                });

        subscriptionList.add(subscription);
    }

    @Override
    public void releaseResource() {
        for (int i = 0; i < subscriptionList.size(); ++i){
            if (null != subscriptionList.get(i) && !subscriptionList.get(i).isUnsubscribed()) {
                subscriptionList.get(i).unsubscribe();
            }
        }
        subscriptionList.clear();
    }

    /*
    *@Description: JSON数据处理回调 - 调用者进行处理
    *@Author: hl
    *@Time: 2018/11/21 16:44
    */
    public interface DataListHandler<D>{
        List<D> String2DataList(String strData);    ///< JSON字符串转换为对象列表返回
    }
    public interface DataHandler<D>{
        D String2Data(String strData);              ///< JSON字符串转换为对象返回
    }
}

BaseControlContract.java

import java.util.HashMap;
import java.util.List;

/*
*@Description: 服务+视图层的纽带
*@Author: hl
*@Time: 2018/11/21 13:55
*/
public interface BaseControlContract {
    /**
     * 负责设置界面
     */
    interface View extends BaseView{
        <T> void onSucess(T t);
    }

    interface RefreshView extends BaseViewByRefresh{
        <T> void onSucess(List<T> listT);
    }

    /**
     * 负责请求网络获取数据
     */
    interface Presenter extends BasePresenter{
        /**
         * 请求数据【对象】
         * @param _funUrl
         * @param _paramList
         */
        void requestData(final String _funUrl, HashMap<String, String> _paramList);

        /**
         * 请求数据【对象】列表
         * @param _funUrl
         * @param _pageNum
         * @param _bIsRefresh
         */
        void requestDataList(final String _funUrl, int _pageNum, boolean _bIsRefresh);
    }
}

BaseService.java

import com.x.finance.net.HttpResponse;

import java.util.HashMap;

import retrofit2.http.FieldMap;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Query;
import retrofit2.http.QueryMap;
import retrofit2.http.Url;
import rx.Observable;

/*
*@Description: 服务请求方法
*@Author: hl
*@Params:
*  D - data对象、实体类、字符串....
*@Time: 2018/11/21 12:02
*/
public interface BaseService{
    @GET    ///< GET方法 - 获取数据【对象】
    Observable<HttpResponse<String>> getData(@Url String url, @QueryMap HashMap<String, String> paramMap);
    @GET    ///< GET方法 - 获取数据【对象】列表
    Observable<HttpResponse<String>> getDataList(@Url String url,
                                                  @Query("page") int pageNum,
                                                  @Query("access_token") String access_token);
    @FormUrlEncoded
    @POST   ///< POST方法 - 获取数据【对象】
    Observable<HttpResponse<String>> postData(@Url String url, @FieldMap HashMap<String, String> paramMap);
}

使用:

image

成功回调的地方:

image

小白的BaseViewByRefresh是基于BaseView的

BaseView.java

  /**
 * View负责显示数据,被Presenter调用来设置界面,可以是Activity, Fragment,或者View, Dialog
 * Created by hl on 2018/3/13.
 */

public interface BaseView {
    /**
     * 共有的一些UI操作,比如吐司,进度条显示,消失等
     */
    void showDialog();
    void disDialog();
    void retryDialog();
    void emptyDialog();
    void showToast(String msg);
    void showRunToast(String msg);
}

BaseViewByRefresh.java

  /**
 * View负责显示数据,被Presenter调用来设置界面,可以是Activity, Fragment,或者View, Dialog
 * Created by hl on 2018/3/13.
 */

public interface BaseViewByRefresh extends BaseView{
    /**
     * 结束下拉刷新
     */
    void finishRefresh();
    /**
     * 结束上拉加载刷新
     */
    void finishLoadMore();
    /**
     * 重置列表
     */
    void resetItemList();

    /**
     * 请求失败
     */
    void onRequestFailer();
}

大量的接口啥的....再结合泛型搞一搞...有些要结合之前的一些总结。然后逐步完善才行。

就先记录到这里吧。。后面再看看怎么完善,小白都不知道这样到底合不合理,哈哈.....

下班了,希望天天坚持,天天开心 - 黄磊

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356

推荐阅读更多精彩内容