开发记录:Android Retrofit Callback 封装(小封装 提高效率)

前言

我们服务器后台返回数据的统一格式:

{
    "code": 200,
    "data": {
        "title": "balbalbla",
        "img": "http://jexxxxx.png",
        "text": "balabalbalba"
    },
    "message": "SUCCESS"
}

解析返回的数据

最低级的方式,用GsonFormat来直接对这个返回体进行解析成一个Bean类,但这样的话,每个Bean类都会有code、message成员,而data又是一个不确定的数据结构。

于是我们把code、message抽出来,data通过泛型,将他们封装成一个专门用于接收自家后台传来的数据RspModel。此后,我们只要解析、编写data体内容的Bean类,传入RspModel中,如返回的data实际是一个User类,那么我们就用RspModel来接收。如此一来,整个返回体也可以统一、规范管理起来,特别是对code的解析,特有帮助。

public class RspModel {

    public final static int CODE_SUCCESS = 200;//成功
    public final static int CODE_FAIL = 400;//失败
    public final static int CODE_UNAUTHORIZED = 401;//未认证(签名错误)
    public final static int CODE_NOT_FOUND = 404;//接口不存在
    public final static int CODE_INTERNAL_SERVER_ERROR = 500;//服务器内部错误

    private int code;
    private String message;
    private T data;

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

开始请求一个接口

对于Retrofit的基本使用和基础封装,这里就不讲述了,不是重点。

每位使用Retrofit的猿们,应该都会有个RemoteService来通过@GET、@POST等注解来编写接口方法。

通过上面的封装后,这里我们就酱子来编写接口了。Call的泛型不再是各个独立的Bean类,而是由RspModel统一管理统一格式的新Bean类。

public interface RemoteService {

    @GET("getUserById")
    Call> getUserByName(@query(userId) String userId);
}

接着就是调用这个接口了:

...
RemoteService service = Network.remote(); //不展开讲述此处的封装

service.getUserByName(id).enqueue(new Callback>() {
      @Override
       public void onResponse(Call> call, Response> response) { 
       RspModel> rspModel = response.body();
            if (rspModel != null) {
            if (rspModel.getCode() == RspModel.CODE_SUCCESS) {
                //成功读取
                //正式处理Data数据
                User user = rspModel.getData();
                ...

            } else {
                Factory.decodeRspCode(rspModel, new DataSource.FailedCallback() {
                    @Override
                    public void onDataNotAvailable(String strRes) {
                        //处理错误码结果
                    }
                });
            }
        }



        @Override
        public void onFailure(Call> call, Throwable t) {
               //处理异常信息
               ViseLog.e(t);
        }
        });
    }

细心的你会发现,enqueue( )方法里面的Callback有两个回调方法,我们在里面编写的代码逻辑中,有部分是固定的操作,就是
1. 判断response.body
2. 判断RspModel的Code
3. 解析出Data并进行后续处理(最终目的操作,使用数据,使用方式是不同的)
3. 处理错误(错误码、异常信息)

所有的网络回调都需要做上面的操作,最终拿data去做后续的分发使用。

那么,对于这个网络请求方法的使用者,他关心的是什么呢,就是有数据,给我数据;没数据,告诉我到底发生什么错误了。对RspModel的解析应该是不知情,也不需要知道。RspModel也是有可能发生改变的,可能在code、Message、data字段基础上,多了个字段,或者上述字段改了名字。如果像上面那样,每个enqueue( )方法里面的Callback里面都独立做一遍解析,会造成两个问题1. 重复代码多 2.一旦RspModel发生变化,解析过程要全部修改。一个项目,几十个上百个接口都有可能,我怎么可能去修改几十次上百次呢。

所以,要封装对RspModel的解析逻辑,暴露出最终得到数据或错误信息的接口。

把解析逻辑封装到Retrofit.Callback

public abstract class MyRetrofitCallback implements Callback> {


    @Override
    public void onResponse(Call> call, Response> response) {
        RspModel rspModel = response.body();
        if (rspModel != null) {
            if (rspModel.getCode() == RspModel.CODE_SUCCESS) {
                //返回成功结果
                Log.d(rspModel.getMessage());
                success(rspModel.getData());
            } else { //返回的结果码为错误码
                //解析错误码
                Factory.decodeRspCode(rspModel, new DataSource.FailedCallback() {
                    @Override
                    public void onDataNotAvailable(String strRes) {
                        //对非success返回码
                        Log.d(rspModel.getMessage());
                        failed(strRes);

                    }
                });
            }
        } else {
            //rspModel == null
            failed("rspModel == null");
        }
    }

    @Override
    public void onFailure(Call> call, Throwable t) {
        failed(t.getMessage());

    }

    protected abstract void success(T data);
    protected abstract void failed(String errMsg);
}

以后,就直接用我们封装好的callback,不再使用Retrofit原生的Callback

service.getUserByName(id).enqueue(new MyRetrofitCallback() {
    @Override
    protected void success(User data) {
        if (data != null && !data.isEmpty()) {
            //读取到有数据
            callback.onDataLoaded(data); //自己封装的,用于数据读取层(网络、数据库)到使用层传递数据的统一接口
        } else {
            //数据库无数据
            callback.onDataNotAvailable("无新远程指导订单数据");
        }

    }

    @Override
    protected void failed(String msg) {
        callback.onDataNotAvailable(msg);

    }
});

看,这样,代码就很清爽,而且AndroidStudio的自动代码补全功能,会根据RemoteService Call< T >里面写的泛型类名,自动为我们的MyRetrofitCallback< T >补上名字,开发起来就很快了。

回调中做少量特别的处理逻辑,就可以把数据直接扔给使用层了,再也不怕写错写漏返回体的解析逻辑了。

知识点标签

接口、抽象类、泛型、重构、封装、拓展性

敲重点

用个抽象类实现一个接口,并实现所需方法,把通用逻辑编写到接口方法中,最终暴露新的方法接口给调用者。

就类似加了个中间人:
原本:A搬砖,B涂水泥并砌砖
如今:A搬砖,AB涂水泥,B砌砖

不,应该是:
原本:A只会搬砖,B涂水泥并砌砖
如今:A会搬砖并涂水泥,B尽情发挥砌砖的天赋才华!

因为,搬砖和涂水泥的工作时固定的,而且还不能出错,每一面都要均匀涂抹。而砌砖呢,就是怎样好看怎样砌,这是很创造性的,可能每一块砖的砌法都不同。


起步慢没关系 走了弯路没关系 因为你一直没退场 这场马拉松 不退场的你 会越来越靠近终点

你可能感兴趣的:(Android,开发记录)