[置顶] NoHttp进阶之自定义请求深度定制

NoHttp自定义请求之深度定制

技术交流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嘛,那么我们也来模仿着写一个。

二. 用FastJson自定义写一个FastJsonRequest

  首先分析下基类,需要传入一个泛型,根据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写一个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呢?这就用到了二元泛型,泛型不好的同学可以先消化一下上面的两个自定请求,再来看这个。

四. 一个二元泛型自定请求解析所有的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接受响应,并且加上我们业务逻辑每次都要被重新解析,所以还需要简化一下,我们继续往下看。

结合自己APP的数据结构封装

  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

你可能感兴趣的:(android,框架,json,NoHttp,自定义请求)