关于设置OkGo自定义回调JsonCallback的相关整理

基础通用版

1,通用的OkGo数据解析方法:

public abstract class JsonCallback<T> extends AbsCallback<T> {

    private Type type;
    private Class<T> clazz;

    public JsonCallback() {
    }

    public JsonCallback(Type type) {
        this.type = type;
    }

    public JsonCallback(Class<T> clazz) {
        this.clazz = clazz;
    }

    // convertResponse方法中定义的是Gson解析换换逻辑
    @Override
    public T convertResponse(okhttp3.Response response) throws Throwable {
        ResponseBody body = response.body();
        if (body == null) return null;
        T data = null;
        Gson gson = new Gson();
        JsonReader jsonReader = new JsonReader(body.charStream());
        if (type != null) {
            data = gson.fromJson(jsonReader, type);
        } else if (clazz != null) {
            data = gson.fromJson(jsonReader, clazz);
        } else {
            Type genType = getClass().getGenericSuperclass();
            Type type = ((ParameterizedType) genType).getActualTypeArguments()[0];
            data = gson.fromJson(jsonReader, type);
        }
        return data;
    }

}

上面的方法已经可以满足项目的需求了,可以用来直接转换类似如下的几种格式:
1,数据类型A-最外层数据类型是JsonObject,data 数据也是JsonObject

{
	"code":0,
	"msg":"请求成功",
	"data":{
		"id":123456,
		"name":"张三",
		"age":18
	}	
}

2,数据类型B-最外层数据类型是JsonObject,data 数据是JsonArray

{
	"code":0,
	"msg":"请求成功",
	"data":[
		{
			"id":123456,
			"name":"张三",
			"age":18
		},
		{
			"id":123456,
			"name":"张三",
			"age":18
		},
		{
			"id":123456,
			"name":"张三",
			"age":18
		}
	]
}

3,数据类型C-没有固定的 msg、code 字段格式包装,服务器任意返回对象

"data":{
	"id":123456,
	"name":"张三",
	"age":18
}	

4,数据类型D-最外层数据类型是JsonArray,内部数据是JsonObject

[
	{
		"id":123456,
		"name":"张三",
		"age":18
	},
	{
		"id":123456,
		"name":"张三",
		"age":18
	},
	{
		"id":123456,
		"name":"张三",
		"age":18
	}
]

使用方法如下(例如接口返回格式如上面的格式1):
1,根据返回值json结构定义对应的实体类

public class UserResponse {
    public int code;
    public String msg;
    public User data;
}

2,执行OkGo网络请求:

OkGo.<UserResponse>get("https://wanandroid.com/banner/json")
                .tag(this)
                .execute(new JsonCallback<UserResponse>() {
                    @Override
                    public void onSuccess(Response<UserResponse> response) {
                       
                    }
                });

针对不同的数据返回格式,主要区别就是定义的实体类不一样,调用方法完全一致。

高端定制版

场景: 大多数情况,服务器返回的数据格式都是有个统一的规范的,就像上面的类型格式A和B那样(A和B也算统一规范,因为基础包装一致),那么我们可以使用泛型,分离基础包装与实际数据,这样子需要定义两个javabean,一个全项目通用的LzyResponse,一个单纯的业务模块需要的数据。

这种方法只适合服务器返回有固定数据格式的情况,如果服务器的数据格式不统一,不建议使用该种方式。

比如通过和公司后台协商,统一了服务器端返回json的格式,如后台接口返回的数据格式只可能为如下两类:

{"code":"0","message":"success","data":{}}
{"code":"0","message":"success","data":[]}

我们真正需要的是data所包含的数据部分,如果继续按照通用版的方式来定义实体类则是这样的:

public class UserResponse {
    public int code;
    public String message;
    public User data;
}

当使用其它接口的时候又得根据该接口返回的json字符串重新定义一个XXXResponse的实体类,并将data的类型改成XXX,如下:

public class Result<T> {
    public int code;
    public String message;
    public T data;
}

很明显code,和message被重复定义了多次,显的有点重复,感觉不是很优雅,因此我们可以通过泛型将code和message字段抽取到一个全项目通用的LzyResponse的类中,这样我们只需要编写data字段所对应的实体类即可,更专注于我们的业务逻辑。如下:

public class LzyResponse<T> {
    public int code;
    public String message;
    public T data;
}

那么对于data字段是User时则可以写为 Result< User > ,当是个列表的时候为 Result<List< User >>,其它同理。

优化的JsonCallback解析类如下:(需根据实际的项目进行修改,最下面的数据成功返回时的code,和对不同errcode的异常抛出)

public abstract class JsonCallback2<T> extends AbsCallback<T> {

    private Type type;
    private Class<T> clazz;

    public JsonCallback2() {
    }

    public JsonCallback2(Type type) {
        this.type = type;
    }

    public JsonCallback2(Class<T> clazz) {
        this.clazz = clazz;
    }

    @Override
    public T convertResponse(Response response) throws Throwable {

        if (type == null) {
            if (clazz == null) {
                // 如果没有通过构造函数传进来,就自动解析父类泛型的真实类型(有局限性,继承后就无法解析到)
                Type genType = getClass().getGenericSuperclass();
                type = ((ParameterizedType) genType).getActualTypeArguments()[0];
            } else {
                return parseClass(response, clazz);
            }
        }

        if (type instanceof ParameterizedType) {
            return parseParameterizedType(response, (ParameterizedType) type);
        } else if (type instanceof Class) {
            return parseClass(response, (Class<?>) type);
        } else {
            return parseType(response, type);
        }
    }

    private T parseClass(Response response, Class<?> rawType) throws Exception {
        if (rawType == null) return null;
        ResponseBody body = response.body();
        if (body == null) return null;
        JsonReader jsonReader = new JsonReader(body.charStream());

        if (rawType == String.class) {
            //noinspection unchecked
            return (T) body.string();
        } else if (rawType == JSONObject.class) {
            //noinspection unchecked
            return (T) new JSONObject(body.string());
        } else if (rawType == JSONArray.class) {
            //noinspection unchecked
            return (T) new JSONArray(body.string());
        } else {
            T t = Convert.fromJson(jsonReader, rawType);
            response.close();
            return t;
        }
    }

    private T parseType(Response response, Type type) throws Exception {
        if (type == null) return null;
        ResponseBody body = response.body();
        if (body == null) return null;
        JsonReader jsonReader = new JsonReader(body.charStream());

        // 泛型格式如下: new JsonCallback<任意JavaBean>(this)
        T t = Convert.fromJson(jsonReader, type);
        response.close();
        return t;
    }

    private T parseParameterizedType(Response response, ParameterizedType type) throws Exception {
        if (type == null) return null;
        ResponseBody body = response.body();
        if (body == null) return null;
        JsonReader jsonReader = new JsonReader(body.charStream());

        Type rawType = type.getRawType();                     // 泛型的实际类型
        Type typeArgument = type.getActualTypeArguments()[0]; // 泛型的参数
        if (rawType != LzyResponse.class) {
            // 泛型格式如下: new JsonCallback<外层BaseBean<内层JavaBean>>(this)
            T t = Convert.fromJson(jsonReader, type);
            response.close();
            return t;
        } else {
            if (typeArgument == Void.class) {
                // 泛型格式如下: new JsonCallback<LzyResponse<Void>>(this)
                SimpleResponse simpleResponse = Convert.fromJson(jsonReader, SimpleResponse.class);
                response.close();
                //noinspection unchecked
                return (T) simpleResponse.toLzyResponse();
            } else {
                // 泛型格式如下: new JsonCallback<LzyResponse<内层JavaBean>>(this)
                LzyResponse lzyResponse = Convert.fromJson(jsonReader, type);
                response.close();
                int code = lzyResponse.errorCode;
                // 一般来说服务器会和客户端约定一个数表示成功,如200,其余的表示失败,如400,300等,这里根据实际情况罗列并抛出
                if (code == 0) {
                    //noinspection unchecked
                    return (T) lzyResponse;
                } else if (code == 400) {
                    throw new IllegalStateException("Token已过期");
                } else if (code == 300) {
                    throw new IllegalStateException("用户名密码错误");
                } else {
                    // 直接将服务端的错误信息抛出,onError中可以获取
                    throw new IllegalStateException("错误代码:" + code + ",错误信息:" + lzyResponse.errorMsg);
                }
            }
        }
    }

}

其中需要用到的Convert类如下:(该类不用修改)

public class Convert {

    private static Gson create() {
        return Convert.GsonHolder.gson;
    }

    private static class GsonHolder {
        private static Gson gson = new Gson();
    }

    public static <T> T fromJson(String json, Class<T> type) throws JsonIOException, JsonSyntaxException {
        return create().fromJson(json, type);
    }

    public static <T> T fromJson(String json, Type type) {
        return create().fromJson(json, type);
    }

    public static <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
        return create().fromJson(reader, typeOfT);
    }

    public static <T> T fromJson(Reader json, Class<T> classOfT) throws JsonSyntaxException, JsonIOException {
        return create().fromJson(json, classOfT);
    }

    public static <T> T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {
        return create().fromJson(json, typeOfT);
    }

    public static String toJson(Object src) {
        return create().toJson(src);
    }

    public static String toJson(Object src, Type typeOfSrc) {
        return create().toJson(src, typeOfSrc);
    }

    public static String formatJson(String json) {
        try {
            JsonParser jp = new JsonParser();
            JsonElement je = jp.parse(json);
            return JsonConvertor.getInstance().toJson(je);
        } catch (Exception e) {
            return json;
        }
    }

    public static String formatJson(Object src) {
        try {
            JsonParser jp = new JsonParser();
            JsonElement je = jp.parse(toJson(src));
            return JsonConvertor.getInstance().toJson(je);
        } catch (Exception e) {
            return e.getMessage();
        }
    }
}

需要引入的依赖:

compile 'com.google.code.gson:gson:2.8.0'
compile 'com.readystatesoftware.chuck:library:1.0.4'

全局通用的LzyResponse类和SimpleResponse定义如下:(需修改字段名和接口返回的一致

public class LzyResponse<T> implements Serializable {

    private static final long serialVersionUID = 5213230387175987834L;

    public int errorCode = -1;
    public String errorMsg;
    public T data;

    @Override
    public String toString() {
        return "LzyResponse{\n" +//
                "\tcode=" + errorCode + "\n" +//
                "\tmsg='" + errorMsg + "\'\n" +//
                "\tdata=" + data + "\n" +//
                '}';
    }
}
public class SimpleResponse implements Serializable {

    private static final long serialVersionUID = -1477609349345966116L;

    public int errorCode;
    public String errorMsg;

    public LzyResponse toLzyResponse() {
        LzyResponse lzyResponse = new LzyResponse();
        lzyResponse.errorCode = errorCode;
        lzyResponse.errorMsg = errorMsg;
        return lzyResponse;
    }
}

其中SimpleResponse类是为了处理服务器返回data部分为空的情况,如下:

{
	"code":300,
	"msg":"用户名或密码错误",	
}

上面优化之后的JsonCallback,只有code为200时才会回调onSuccess方法,非200的情况都会回调到onError方法中,在onError方法中我们可以通过instanceof操作符来判断类型从而来区分是OkGo自己的异常还是我们自定义Callback时用户抛出的异常。

使用方法如下:

OkGo.<LzyResponse<List<BannerBean>>>get("https://wanandroid.com/banner/json")
                .tag(this)
                .execute(new JsonCallback<LzyResponse<List<BannerBean>>>() {
                    @Override
                    public void onSuccess(Response<LzyResponse<List<BannerBean>>> response) {
                        // 回调onSuccess就是返回了200
                        String desc = response.body().data.get(0).getDesc();
                        Toast.makeText(CollectActivity.this, desc, Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onError(Response<LzyResponse<List<BannerBean>>> response) {
                        Throwable exception = response.getException();
                        if(exception != null) exception.printStackTrace();
                        if(exception instanceof UnknownHostException || exception instanceof ConnectException){
                            ToastUtil.showShort("服务器连接失败,请检查后重试!");
                        }else if(exception instanceof SocketTimeoutException){
                            ToastUtil.showShort("网络请求超时!");
                        }else if(exception instanceof HttpException){
                            ToastUtil.showShort("服务器维护,请稍后再试(404,500)!");
                        }else if(exception instanceof StorageException){
                            ToastUtil.showShort("SD卡不存在或者没有权限!");
                        }
                        else if(exception instanceof IllegalStateException){
                            String message = exception.getMessage();
                            ToastUtil.showShort(message);
                        }
                    }
                });

其实通用版和定制版的主要区别就是:网络请求成功非200的情况是回调onSuccess还是回调onError。

扩展1:OkGo+RxJava2:

添加依赖:

compile 'com.lzy.net:okrx2:2.0.2'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'com.google.code.gson:gson:2.8.0'
compile 'com.readystatesoftware.chuck:library:1.0.4'

使用方式如下:

public static Observable<ResponseData<ArticleListVO>> getHomeData(int page) {
        String url = AppConst.BASE_URL + "article/list/" + page + "/json";
        return OkGo.<LzyResponse<ArticleBean>>get(url)
                .cacheMode(CacheMode.FIRST_CACHE_THEN_REQUEST)  //使用缓存
                .converter(new JsonConvert<LzyResponse<ArticleBean>>() {
                })
                .adapt(new ObservableBody<LzyResponse<ArticleBean>>());
    }

这里有两个特殊的方法:

  • .converter():该方法是网络请求的转换器,功能是将网络请求的结果转换成我们需要的数据类型。
  • .adapt():该方法是方法返回值的适配器,功能是将网络请求的Call对象,适配成我们需要的Observable对象。

因此我们需要自定义一个全局通用的JsonConvert类:

public class JsonConvert<T> implements Converter<T> {
    // 跟上面的定制版JsonCallback只有类名和继承关系不一样,剩余部分完全一致,直接复制即可,同时修改对应位置为符合实际项目
}

adapt的参数:
Observable开头,表示返回的对象是Observable对象
Body结尾,表示返回对象的泛型是T,没有外层的Response包装

OkRx使用缓存

默认情况下,我们使用的是异步请求来适配的,使用方式和okgo使用缓存一样,指定cacheKey和cacheMode就行了,如下:

OkGo.<LzyResponse<List<BannerBean>>>get("https://wanandroid.com/banner/json")
                .cacheKey("banners")
                .cacheMode(CacheMode.FIRST_CACHE_THEN_REQUEST) // 使用缓存
                .converter(new JsonConvert<LzyResponse<List<BannerBean>>>() {
                })
                .adapt(new ObservableResponse<LzyResponse<List<BannerBean>>>())
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Response<LzyResponse<List<BannerBean>>>>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                        addDisposable(d);
                    }

                    @Override
                    public void onNext(@NonNull Response<LzyResponse<List<BannerBean>>> lzyResponseResponse) {
                        boolean fromCache = lzyResponseResponse.isFromCache();
                        LzyResponse<List<BannerBean>> body = lzyResponseResponse.body();
                        List<BannerBean> data = body.getData();
                        String desc = data.get(0).getDesc();
                        if (fromCache){
                            ToastUtil.showShort("来自缓存:"+desc);
                        }else{
                            ToastUtil.showShort("不是来自缓存:"+desc);
                        }
                    }

                    @Override
                    public void onError(@NonNull Throwable exception) {
                        if(exception != null) exception.printStackTrace();
                        if(exception instanceof UnknownHostException || exception instanceof ConnectException){
                            ToastUtil.showShort("服务器连接失败,请检查后重试!");
                        }else if(exception instanceof SocketTimeoutException){
                            ToastUtil.showShort("网络请求超时!");
                        }else if(exception instanceof HttpException){
                            ToastUtil.showShort("服务器维护,请稍后再试(404,500)!");
                        }else if(exception instanceof StorageException){
                            ToastUtil.showShort("SD卡不存在或者没有权限!");
                        }
                        else if(exception instanceof IllegalStateException){
                            String message = exception.getMessage();
                            ToastUtil.showShort(message);
                        }
                    }

                    @Override
                    public void onComplete() {

                    }
                });

那么对于okrx缓存回调成功和网络请求回调成功都是回调onNext()方法,那么怎么区分呢,回调的Response对象中有个isFromCache方法,就是表示当前回调是来自缓存还是网络。

注意事项:

  • 不同的缓存模式会导致onNext()方法具有不同的回调次数,可能一次或者两次
  • 如果使用同步方法来适配的话,缓存模式CacheMode.FIRST_CACHE_THEN_REQUEST是不生效的,异步才生效,原因是同步请求没法返回两次数据。

取消请求

我们可以在基类(BaseActivity或者BasePresenter中)创建这么两个方法,把每一个请求的Disposable对象都交给由统一的CompositeDisposable对象去管理。

private CompositeDisposable compositeDisposable;

public void addDisposable(Disposable disposable){

        if(compositeDisposable == null){
            compositeDisposable = new CompositeDisposable();
        }
        compositeDisposable.add(disposable);

    }

    public void dispose(){
        if(compositeDisposable != null){
            compositeDisposable.dispose();
        }
    }

然后在Observer对象的onSubscibe方法中,把Disposable添加到CompositeDisposable中:

OkGo.<LzyResponse<List<BannerBean>>>get("https://wanandroid.com/banner/json")
                .converter(new JsonConvert<LzyResponse<List<BannerBean>>>() {
                })
                .adapt(new ObservableResponse<LzyResponse<List<BannerBean>>>())
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Response<LzyResponse<List<BannerBean>>>>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                        addDisposable(d);
                    }

                    @Override
                    public void onNext(@NonNull Response<LzyResponse<List<BannerBean>>> lzyResponseResponse) {
                        boolean fromCache = lzyResponseResponse.isFromCache();
                        LzyResponse<List<BannerBean>> body = lzyResponseResponse.body();
                        List<BannerBean> data = body.getData();
                        String desc = data.get(0).getDesc();
                        if (fromCache){
                            ToastUtil.showShort("来自缓存:"+desc);
                        }else{
                            ToastUtil.showShort("不是来自缓存:"+desc);
                        }
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {

                    }

                    @Override
                    public void onComplete() {

                    }
                });

最后我们在界面销毁或者需要取消的地方调用取消请求的方法:

@Override
    protected void onDestroy() {
        super.onDestroy();
        dispose();
    }

扩展2:OkGo+RxJava2+MVP

项目结构如下:
在这里插入图片描述
源码地址:https://github.com/gpf0205/MVPOkgoDemo

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

智玲君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值