技术交流1群:46523908
技术交流2群:46505645
NoHttp 源码及Demo托管在Github欢迎大家Star:https://github.com/Y0LANDA/NoHttp
NoHttp是专门做Android网络请求与下载的框架。
NoHttp在下面的连接做Android干货直播,需要提升逼格的同学们欢迎来吐槽。
直播视频下载传送门
直播浏览器观看地址
直播YY观看频道:56600481
很多用NoHttp的同学们觉得NoHttp接口很方便,用一下就喜欢上了,但是有人又问在请求Json的时候怎么传入一个JavaBean,NoHttp就帮我自动解析出来呢?今天我带大家来一步步做一个NoHttp自定请求和深度定制自定义请求,如何一个自定义请求对象解决所有的JavaBean解析。
本次博客中的源代码请移步:http://download.csdn.net/detail/yanzhenjie1003/9460137
NoHttp默认提供了StringRequest、ImageRequest、JsonObjectRequest、JsonArrayRequest,我们从这几个类来看看如何扩展一个我们的自定义请求。
平时的开发中同学们用FastJson来解析Json应该比较多了,那我们先尝试着写一个FastJson的自定义请求,所以我们先来看看JsonObjectRequest这个类的代码:
// 第一个注意点,继承RestRequest<T>类
public class JsonObjectRequest extends RestRequest<JSONObject> {
public static final String ACCEPT = "application/json;q=1";
public JsonObjectRequest(String url) {
super(url);
}
public JsonObjectRequest(String url, RequestMethod requestMethod) {
super(url, requestMethod);
}
@Override
public String getAccept() {
return ACCEPT;
}
@Override
public JSONObject parseResponse(String url, Headers responseHeaders, byte[] responseBody) {
// 第二个注意点,解析数据
String jsonStr = StringRequest.parseResponseString(url, responseHeaders, responseBody);
try {
return new JSONObject(jsonStr);
} catch (Exception e) {
try {
return new JSONObject("{}");
} catch (JSONException exception) {
}
}
return null;
}
}
我们看到无非就是继承了RestRequest这个泛型基类,然后在parseResponse中根据responseHeaders和responseBody解析了服务端的数据,显示把数据通过StringRequest的一个static方法解析为String,然后用解析了String成JSONObject嘛,那么我们也来模仿着写一个。
首先分析下基类,需要传入一个泛型,根据parseResponse来看,这个泛型就是我们想要的结果的类型,那么我们传入FastJson的JSONObject就可以了,根据Http协议,getAccept()方式应该是返回客户端能接受什么数据,这里写成和NoHttp默认的JsonObjectReqeust一样的。代码如下:
public class FastJsonRequest extends RestRequest<JSONObject> {
public FastJsonRequest(String url) {
super(url);
}
public FastJsonRequest(String url, RequestMethod requestMethod) {
super(url, requestMethod);
}
@Override
public JSONObject parseResponse(String url, Headers responseHeaders, byte[] responseBody) {
String jsonStr = StringRequest.parseResponseString(url, responseHeaders, responseBody);
try {
return JSON.parseObject(jsonStr);
} catch (Exception e) {
return JSON.parseObject("{}");
}
}
@Override
public String getAccept() {
return JsonObjectRequest.ACCEPT;
}
}
是不是很简单。但是同学们又会有问题,那我拿到JsonObject还不是要解析成一个JavaBean,还是不好用,那么既然掌握NoHttp自定义请求的原理后,我们是不是可以再写一个JavaBean的请求呢?
继续用FastJson解析,来我们写一个UserInfo的自定义请求,还是和上一个FastJson一样,传入一个JavaBean的泛型,有FastJaon解析Json成这个JavaBean,代码如下:
public class UserInfoRequest extends RestRequest<UserInfo> {
public UserInfoRequest(String url, RequestMethod requestMethod) {
super(url, requestMethod);
}
public UserInfoRequest(String url) {
super(url);
}
@Override
public UserInfo parseResponse(String url, Headers responseHeaders, byte[] responseBody) {
String result = StringRequest.parseResponseString(url, responseHeaders, responseBody);
try {
return JSON.parseObject(result, UserInfo.class);
} catch (Exception e) {
return new UserInfo(); // 如果服务端数据错误,返回空构造
}
}
@Override
public String getAccept() {
return JsonObjectRequest.ACCEPT;
}
}
有没有很爽啊,想定义什么类型的请求都可以,只需要在parseResponse中解析成这个对象就可以了。但是人们的贪婪是无限的。有人就问了,那我有很多个请求是不是要建很多个JavaBeanRequest了?答案是不用。
本篇博客的精髓和重点就出现了,如果自定义一个请求能解析多个JavaBean呢?这就用到了二元泛型,泛型不好的同学可以先消化一下上面的两个自定请求,再来看这个。
A. 既然NoHttp用了泛型来支持很多的自定义请求,我们是不是也可以利用泛型来自定我们的JavaBean的请求呢?
我们在上面继承RestRequest时需要传入一个类型,这个类型才是我们的最后要的结果,我们以UserInfoRequest为例来看看代码:
// 这里传入了UserInfo
public class UserInfoRequest extends RestRequest<UserInfo> {
...
}
B. 所以我们要想兼容所有的JavaBean,这里的UserInfo要能是一个任意类型,也就是泛型,我们来尝试着写一个BeanJsonRequest:
public class BeanJsonRequest extends RestRequest<E> {
...
}
C. 大家这样写,在E这里肯定会编译不过,因为无缘无故从哪里冒出E这个类呢?所以要在前面声明这个泛型:
public class BeanJsonRequest<E> extends RestRequest<E> {
}
D. 声明了这个泛型后同时也是一个一石二鸟的写法,也实现了我们B中说的要求,传入任意已JavaBean的类型——泛型。
这样写就不会编译不过了,这个E就代表我们所有JavaBean。我们再来看看怎么new这个BeanJsonRequest:
BeanJsonRequest<UserInfo> beanJsonRequest = new BeanJsonRequest<>(url, UserInfo.class);
完整的代码补上:
public class BeanJsonRequest<E> extends RestRequest<E> {
public BeanJsonRequest(String url) {
super(url);
}
public BeanJsonRequest(String url, RequestMethod requestMethod) {
super(url, requestMethod);
}
@Override
public E parseResponse(String url, Headers responseHeaders, byte[] responseBody) {
String string = StringRequest.parseResponseString(url, responseHeaders, responseBody);
try {
return JSON.parseObject(string, E.class);// 注意这里
} catch (Exception e) {
return null;
}
}
@Override
public String getAccept() {
return JsonObjectRequest.ACCEPT;
}
}
E. 看到这里的同学先别急还没完呢,这样写了之后发现return JSON.parseObject(string, E.class);
居然报错了,为什么?哈哈,因为E是一个泛型,不是一个存在类,E.class不存在,所以报错了。那么怎么解决呢?就要我们传一个泛型进来,所以完整的代码应该是:
public class BeanJsonRequest<E> extends RestRequest<E> {
private Class<E> clazz;
public BeanJsonRequest(String url, Class<E> clazz) {
this(url, RequestMethod.GET, clazz);
}
public BeanJsonRequest(String url, RequestMethod requestMethod, Class<E> clazz) {
super(url, requestMethod);
this.clazz = clazz;
}
@Override
public E parseResponse(String url, Headers responseHeaders, byte[] responseBody) {
String string = StringRequest.parseResponseString(url, responseHeaders, responseBody);
try {
return JSON.parseObject(string, clazz);
} catch (Exception e) {
E instance = null;
try {
// 服务端返回数据格式错误时,返回一个空构造
// 但是前提是传进来的JavaBean必须提供了默认实现
instance = clazz.newInstance();
} catch (Exception e1) {
}
return instance;
}
}
@Override
public String getAccept() {
return JsonObjectRequest.ACCEPT;
}
}
F. 最终的使用方法:
BeanJsonRequest<UserInfo> beanJsonRequest = new BeanJsonRequest<>(url, UserInfo.class);
// 或者
BeanJsonRequest<UserInfo> beanJsonRequest = new BeanJsonRequest<>(url, method, UserInfo.class);
到这里其实就可以了,完全可以解析所有的JavaBean了。但是在实际开发中,我们每次都要根据不同的泛型new一个Listener接受响应,并且加上我们业务逻辑每次都要被重新解析,所以还需要简化一下,我们继续往下看。
A. 对于一般人呢,上面的方法足够了,但是我们还可以封装的更好。假设一个情况,我们的服务端的Json数据结构是这样的:
{
"data": { "uname": "yolanda", "upwd": "123" },
"error": 0,
"msg": "成功" }
B. 我们就可以把data、error和msg这三者封装到BaseJavaBean中:
public class BaseJavaBean {
/** * 业务状态码 */
@JSONField(name = "error")
private int error;
/** * 业务状态码对应的提示 */
@JSONField(name = "msg")
private String msg;
/** * 数据 */
@JSONField(name = "data")
private String data;
public boolean isSuccess() {
// 这里假设业务码为1时业务正确
return getError() == 1;
}
...
}
C. 那我们每次使用的时候:
BeanJsonRequest<BaseJavaBean> beanJsonRequest = new BeanJsonRequest<>(url, BaseJavaBean.class);
那我们怎么把data中的这个实体在解析出来呢?这里就更简单了,只需要一个方法,就可以解析所有的data。
D. 在BaseJavaBean中加一个解析Data的方法,完整的代码就是
public class BasicJavaBean {
/** * 服务端业务数据 */
@JSONField(name = "data")
private String data;
/** * 服务端业务错误码 */
@JSONField(name = "error")
private int error;
/** * 业务码对应的消息 */
@JSONField(name = "msg")
private String message;
/** * 业务是否成功 */
public boolean isSuccess() {
return getError() == 1;
}
/** * 你的{@link E}必须提供默认无参构造 * * @param clazz 要解析的实体类的class * @return 实体类 */
public <E> E parseData(Class<E> clazz) {
E e = null;
try {
e = JSON.parseObject(getData(), clazz);
} catch (Exception e1) {
// 服务端数据格式错误时,返回data的空构造
try {
e = clazz.newInstance();
} catch (Exception e2) {
}
}
return e;
}
}
E. 我们写一个接受data的实体类
public class UserInfoContent {
@JSONField(name = "uname")
private String userName;
@JSONField(name = "upwd")
private String userPwd;
}
F.看官们会用了吗?哈哈,来看下演示代码吧,用UserInfoContent结合上面的可以解析所有JavaBean的BeanJsonRequest
:
Request<BasicJavaBean> request = new BeanJsonRequest(url);
接受响应:
private HttpCallBack<BasicJavaBean> listener = new HttpCallBack<BasicJavaBean>() {
@Override
public void onSucceed(int what, Response<BasicJavaBean> response) {
BasicJavaBean javaBean = response.get();
if (javaBean.isSuccess()) {
// 这里就是掉用BasicJavaBean.parseData()方法的
UserInfoContent content = javaBean.parseData(UserInfoContent.class);
String s = "用户名:" + content.getUserName() + ";密码:" + content.getUserPwd();
} else {
// 业务处理层数据失败
}
}
};
我们可以看到UserInfoContent content = javaBean.parseData(UserInfoContent.class);
就是调用了解析data的方法,并且我们可以用一个listener来接受所有的数据噢。
F. 到这里博客就结束啦,完美解决一个Request完成所有的JavaBean解析。
NoHttp 源码及Demo托管在Github欢迎大家Star:https://github.com/Y0LANDA/NoHttp