安卓项目实战之强大的网络请求框架okGo使用详解(二):深入理解Callback之自定义JsonCallback

版权声明:转载必须注明本文转自郭子轩的博客 https://blog.csdn.net/gpf1320253667/article/details/83515861

前言

JSON是一种取代XML的数据结构,和xml相比,它更小巧但描述能力却不差,由于它的小巧所以网络传输数据将减少更多流量从而加快了传输速度,目前客户端服务器返回的数据大多都是基于这种格式的,相应的我们了解的关于json的解析工具主要有两个:Gson(Google官方出的)和fastjson(阿里巴巴工程师开发的),本篇开篇主要针对Gson解析作简要回顾。

json解析之Gson工具使用简要回顾

json有json对象和json数组之分,对象以“{”(左括号)开始,“}”(右括号)结束,数组以“[”(左中括号)开始,“]”(右中括号)结束,如下:

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

Gson工具完成java对象和json格式字符串之前的转换如下:( toJson 和 fromJson )

// 使用new方法创建Gson对象
Gson gson = new Gson();

// toJson 将bean对象转换为json字符串
String jsonStr = gson.toJson(user, User.class);

// fromJson 将对象格式的json字符串转为bean对象
Student user= gson.fromJson(jsonStr, User.class);

// 将集合转成json字符串
String jsonStr2 = gson.toJson(list);

// 将数组格式的json转化成List时需要使用到TypeToken getType()**
List<User> retList = gson.fromJson(jsonStr2,new TypeToken<List<User>>(){}.getType());

一般情况下我们都会根据json字符串定义对应的数据实体类,通常遵循如下的规则:
1,看到JSON结构里面有{ }你就定义一个类,看到[ ]你就定义一个List即可,最后只剩下最简单的如String、int等基本类型直接定义就好。,
2,内部嵌套的类,请使用public static class className { }。
3,类内部的属性名,必须与JSON串里面的Key名称保持一致。

统一的服务器返回数据实体类的定义

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

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

我们真正需要的是data所包含的数据,而code只使用一次,message则几乎不用。如果Gson不支持泛型或不知道Gson支持泛型的同学一定会这么定义实体类:

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

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

public class PersonResponse {
    public int code;
    public String message;
    public Person data;
}

很明显code,和message被重复定义了多次,通过泛型的话我们可以将code和message字段抽取到一个Result的类中,这样我们只需要编写data字段所对应的实体类即可,更专注于我们的业务逻辑。如:

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

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

okGo之自定义jsonCallback

上面也说了,json现在是主流的两端数据传输方式,因此定义一个针对服务器端json字符串的返回做解析的回调还是十分有必要的。
我们都知道网络的数据是以流的形式进行传递的,我们在解析数据的时候,把流先解析成JSON字符串,然后在通过Gson解析成对象这个过程是没必要的,如果大量的请求发生,是不是就有大量的数据要被转成JSON字符串,数据量小还行,一旦较大,连请求都发生OOM了,这并不是我们希望看到的,那么有没有办法能直接把流解析成对象呢,不要转成中间的字符串对象,当然是可以的,Gson框架也为我们提供了这个方法,解析代码如下:
在这里插入图片描述
在网络请求框架okGo使用详解(一)的文章中对于自定义callback的深入理解中我们知道,定义一个callback我们可以通过继承AbsCallback来实现,主要重写里面的convertResponse方法来提供一种实现,完成服务器端返回数据和泛型指定格式之间的转换,okGo框架内置的几种callback都分别额外定义了一个实现convert接口的转换器类,然后在自定义callback类的convertResponse方法中其实又调用了转换器对象的convertResponse方法完成的转换,其实我们完全可以不额外定义转换器类,直接将转换的解析逻辑代码放在自定义callback类的convertResponse方法中,如下:
在这里插入图片描述Retrofit默认的GsonConvert的核心也就是上面这几行代码实现。
然后自定义的jsonCallback就可以这样来使用了:
在这里插入图片描述请求方式1对应的url返回的json格式为:json对象字符串,即以 { 开始以 } 结尾,Login是根据该结果封装的实体类,底层其实借助Gson以流的方式完成的json字符串和对象之间的转化。
请求方式2对应的url返回的json格式为:json数组字符串,即以 [ 开始以 ] 结尾,List< ServerModel >是根据该结果封装的实体类,底层其实借助Gson以流的方式完成的json字符串和集合之间的转化。

注意:
如果上面这点代码在运行时还是有问题,强烈建议不要自定义Callback了,直接用框架内置的StringCallback,写法极其简单,就这样,再次强调第一行的泛型一定要写String,才能用StringCallback,否则无法编译通过。

服务器返回json有固定数据格式的情况下对JsonCallback的再优化

场景: 大多数情况,服务器返回的数据格式都是有个统一的规范的,就像我们上面gson简介末尾说的那样,服务器端返回给我们的json格式规定如下形式:

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

那么我们可以使用泛型,分离基础包装与实际数据,这样子需要定义两个javabean,一个全项目通用的LzyResponse,一个单纯的业务模块需要的数据,具体的定义如下例所示:
在这里插入图片描述对于这种方式,我们在创建JsonCallback的时候,需要这么将LzyResponse< ServerModel>或LzyResponse<List< ServerModel>>整体作为一个泛型传递,相当于传递了两层,泛型中又包含了一个泛型,使用方法如下:
在这里插入图片描述那么JsonCallback中就需要做改动了,详细的原理就不说了,直接上代码,详细看注释:
在这里插入图片描述(上图中的0以及其他的错误码和错误信息提示根据自己项目指定的错误码和错误描述来修改)
一般来说服务器会和客户端约定一个数表示请求数据成功,上面示例中假设code为0时代表获取数据成功,这里的请求数据成功指的是json字符串中data对应的部分有数据获取到才算成功而非一次网络请求的成功与否,例如当我们执行用户登录操作时,正常情况下登录成功服务器端会返回给我们用户的信息以对象的形式在data之后,如下:

{"code":"0","message":"success","data":{"name":"gpf","sex":"man"}}

但是当用户名或者密码输入错误时,服务器端返回的json格式如下,此时网络请求是正常的,但是出错了,没有获取到返回的具体数据,因此data数据部分为空:

{"code":"111","message":"用户名或者密码错误!"}

很明显上面的这种情况我们应该回调onError方法的,但是如果我们没有在自定义的JsonCallback的convertResponse方法中对成功获取到数据中包含错误情况的判断逻辑时(也就是上面请求成功并且有数据时拿到code的值进行判断的那部分代码),仍会回调onSuccess方法,然后我们在onSuccess回调中再拿到错误码code,来显示错误提示,这样做的结果是,把无论正确的还是错误的数据,都交给了onSuccess处理,在使用上不太友好,逻辑分层混乱。

优化后的方式,这种方式不仅可以正确的解析数据,而且能在解析的过程中判断错误码,并根据不同的错误码抛出不同的异常(这里抛出异常并不会导致okgo挂掉,而是以异常的形式通知okgo回调onError,并且将该异常以参数的形式传递到onError,这样做后,在onSuccess中处理的一定是成功的数据,在onError中处理的一定是失败的数据,达到了友好的逻辑分层。

异常解析onError方法对应修改如下:(根据自己项目指定的错误码和错误描述来修改)

在这里插入图片描述在onError方法中我们使用操作符instanceof区分OkGo自己的异常和我们自定义Callback时用户自定义抛出的异常

注意:以上这种优化方法只适用于服务器返回此固定数据格式的情况,同时我也推荐服务端对返回的数据做一次统一包装,就像上面的示例一样,用code和msg包一层。

猜你喜欢

转载自blog.csdn.net/gpf1320253667/article/details/83515861