笔者将通过11篇博客对个人开源框架进行讲解,本篇为第10篇,讲解发现问题,改善。
如果有兴趣一起讨论本框架的内容,请加QQ群:271335749
回忆一下上一篇中,对第三方解析框架的支持。
开发者可以根据自己的需求来使用不同的Listener。在自定义Listener时,开发者也可以根据自己的需求,来实现相应的兼容第三方解析框架的Listener。
在选择框架自带的Listener时,并没问题。
但是,自定义Listener并支持第三方解析框架时,比如NetCustomGsonListener
abstract public class NetCustomGsonListener<Page, T> extends NetHandleListener {
@Override
protected void onReceivedRet(NetRetBean netRetBean) throws JSONException {
String jsonString = netRetBean.getServerData();
// 使用Gson解析
Page page = NetGsonUtil.parseItemByGson(getClass(), 0, jsonString);
JSONObject jsonObject = new JSONObject(jsonString);
JSONArray jsonArray = jsonObject.getJSONArray("list");
List<T> list = new ArrayList<>();
for (int i = 0; i < jsonArray.length(); i++) {
String string = jsonArray.getString(i);
// 使用Gson解析
T t = NetGsonUtil.parseItemByGson(getClass(), 1, string);
list.add(t);
}
Map<String, Object> map = new HashMap<>();
map.put("page", page);
map.put("list", list);
netRetBean.setServerObjectMap(map);
handleResult(netRetBean);
}
@SuppressWarnings("unchecked")
@Override
protected void onSuccess(CallbackCode successCode, NetRetBean netRetBean) {
Map<String, Object> map = netRetBean.getServerObjectMap();
onSuccess((Page) map.get("page"), (List<T>) map.get("list"));
}
/**
* 运行在ui线程,返回多个实体
*
* @param ts 当前bean的List
*/
abstract protected void onSuccess(Page page, List<T> ts);
}
这里要非常注意的一点是,传入的两个Beam,都必须是符合gson规则的,如果哪一个不符合的话,那么他就不能够解析成功。
那么这样做的问题也就很明显了,有时候我传入的两个Bean只有第一个符合gson规则,第二个用默认的解析,要怎么办?
你就必须针对这种需求,再实现不同的监听器
abstract public class NetCustomGsonListener_1<Page, T> extends NetHandleListener {
@Override
protected void onReceivedRet(NetRetBean netRetBean) throws JSONException {
String jsonString = netRetBean.getServerData();
// 使用Gson解析
Page page = NetGsonUtil.parseItemByGson(getClass(), 0, jsonString);
JSONObject jsonObject = new JSONObject(jsonString);
JSONArray jsonArray = jsonObject.getJSONArray("list");
List<T> list = new ArrayList<>();
for (int i = 0; i < jsonArray.length(); i++) {
String string = jsonArray.getString(i);
// 使用默认解析
T t = NetBaseBeanUtil.parseItem(getClass(), 1, string);
list.add(t);
}
Map<String, Object> map = new HashMap<>();
map.put("page", page);
map.put("list", list);
netRetBean.setServerObjectMap(map);
handleResult(netRetBean);
}
@SuppressWarnings("unchecked")
@Override
protected void onSuccess(CallbackCode successCode, NetRetBean netRetBean) {
Map<String, Object> map = netRetBean.getServerObjectMap();
onSuccess((Page) map.get("page"), (List<T>) map.get("list"));
}
/**
* 运行在ui线程,返回多个实体
*
* @param ts 当前bean的List
*/
abstract protected void onSuccess(Page page, List<T> ts);
}
就目前的来说,他可能使用默认解析,gson,fastjson,也就是三种情况。
那么两个泛型的话,3*3就是9种情况,要写9个Listener来适配不同的情况,来供开发者使用?
如果是N个泛型,就要写3的N次方个。
而且这还只是异步请求的情况,同步请求还都要跟着实现一份代码,那么就要再乘以2,这是多么蛋疼啊。
我想开发者也不想这样实现,就算实现了,具体使用时要用哪个Listener,也要找半天。
回想一下上一篇的内容,框架中提供的Listener只带1个泛型的,所以针对不同的需求,比如单个Bean的,要实现3的1次方个,即3个Listener,List<Bean>的也要3个。然后每一个都要复制一份同步请求的实现,最后一共实现了12个Listener。
不过在框架中实现12个是可以接受的,但是让开发者自己使用时来实现,这显然不科学。
那么,有没有办法避免这种情况呢?
我们回忆上一篇中讲到的,在initByJson方法中,支持第三方解析,出现的尴尬的情况
public class NetUserBean extends NetBaseBean {
private String id;
private String name;
@Override
public void initByJson(JSONObject jsonObject) throws JSONException {
this.id = jsonObject.optString("id");
this.name = jsonObject.optString("name");
/**
* 本类使用fastjson和gson会出现下面的情况,因为他们没有提供在当前类中去解析当前类
* 即 fromJson(jsonString, getClass(), this) 这样的方法
* 而是解析后返回一个新的对象,所以就会出现下面这样尴尬的情况
*/
// NetUserBean netUserBean = new Gson().fromJson(jsonObject.toString(), NetUserBean.class);
// this.id = netUserBean.getId();
// this.name = netUserBean.getName();
// NetUserBean netUserBean = JSON.parseObject(jsonObject.toString(), NetUserBean.class);
// this.id = netUserBean.getId();
// this.name = netUserBean.getName();
}
}
gson和fastjson只能将解析完的对象返回,那么能不能直接利用这个对象呢?
再来看看使用initByJson的地方
public class NetBaseBeanUtil {
public static <T extends NetBaseBean> T parseItem(Class aClass, int tIndex, JSONObject jsonObject) throws JSONException {
T t = getBean(aClass, tIndex);
t.initByJson(jsonObject);
return t;
}
@SuppressWarnings("unchecked")
private static <T extends NetBaseBean> T getBean(Class aClass, int tIndex) {
Class<T> entityClass = (Class<T>) ((ParameterizedType) aClass.getGenericSuperclass()).getActualTypeArguments()[tIndex];
T entity = null;
try {
// 使用newInstance创建实例的类,必须有无参构造方法
entity = entityClass.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return entity;
}
}
parseItem方法里,先创建T的实例,再将t用jsonObject解析,再把实例返回。
思来想去,笔者想到了解决方法,直接让initByJson方法拥有返回值就行了,如果initByJson的返回值是T,那么parseItem方法就可以改写了
public static <T extends NetBaseBean> T parseItem(Class aClass, int tIndex, JSONObject jsonObject) throws JSONException {
T t = getBean(aClass, tIndex);
t = t.initByJson(jsonObject);
return t;
}
再简单点
public static <T extends NetBaseBean> T parseItem(Class aClass, int tIndex, JSONObject jsonObject) throws JSONException {
T t = getBean(aClass, tIndex);
return t.initByJson(jsonObject);
}
那么,NetUserBean应该是这样的
public class NetUserBean extends NetBaseBean {
private String id;
private String name;
@Override
public NetUserBean initByJson(JSONObject jsonObject) throws JSONException {
this.id = jsonObject.optString("id");
this.name = jsonObject.optString("name");
return this
// return new Gson().fromJson(jsonObject.toString(), NetUserBean.class);
// return JSON.parseObject(jsonObject.toString(), NetUserBean.class);
}
}
可以看出,这样的话,开发者可以在initByJson里面选择自己想要用到的解析方式,在使用第三方解析的时候,也非常方便,一行代码搞定。
因为initByJson方法改变了,所以父类也需要改变。NetBaseBean就会变成这样
abstract public class NetBaseBean {
/**
* 子类实现这个方法,在其中解析json
*
* @param jsonObject 将要解析的JsonObject
* @throws JSONException
*/
abstract public NetUserBean initByJson(JSONObject jsonObject) throws JSONException;
}
怎么看起来怪怪的,这样写的话,NetUserBean类是没有问题,可是其它类的这个方法,也都要返回NetUserBean?明显不能这样做。
再想一下第一篇中介绍的概念,以及本框架非常重要的知识,对,泛型,再次使用他。
返回的类型不能是NetUserBean,而是泛型T,而且T是继承自NetBaseBean的类。
其实在将NetBaseBeanUtil的parseItem方法实现成下面这样的时候
public static <T extends NetBaseBean> T parseItem(Class aClass, int tIndex, JSONObject jsonObject) throws JSONException {
T t = getBean(aClass, tIndex);
return t.initByJson(jsonObject);
}
就已经说明了initByJson只能返回泛型T,且T是继承NetBaseBean的。
那么,经过改写后的NetBaseBean,是这样的
abstract public class NetBaseBean {
/**
* 子类实现这个方法,在其中解析json
*
* @param jsonObject 将要解析的JsonObject
* @throws JSONException
*/
abstract public <T extern NetBaseBean> T initByJson(JSONObject jsonObject) throws JSONException;
}
子类在继承他的时候,要把返回结果改成当前类,比如NetUserBean就要把initByJson方法的返回值改成NetUserBean,否则编译不能通过。
为了不污染原来的代码,这里重新定义了一个类,NetCommonBean
package com.chenjian.net.bean;
import org.json.JSONException;
import org.json.JSONObject;
/**
* 作者: ChenJian
* 时间: 2016.12.30 20:33
*/
abstract public class NetCommonBean {
/**
* 如果要选择性的使用android自带解析,或者otherJson(第三方解析框架,如gson、fastjson)解析的话,
* 那么子类要继承本类,而不是继承{@link com.chenjian.net.bean.NetBaseBean},
* otherJson无法在本类中对自己解析,而initByJson返回类型为void,正是在本类中对自己进行解析,
* initByJson兼容otherJson会有很别扭的写法,只能在initByJson中使用otherJson解析后再给每个字段赋值
* <p>
* 详情请看
* {@link com.chenjian.net.demo.bean.NetUserBean}
*
*
* 子类重写这个方法时,因为要返回,所以请将返回类型 T 改成具体类型,不然编译会报错
* 具体使用请看以下两个类
* {@link com.chenjian.net.demo.bean.NetNewsBean}
* {@link com.chenjian.net.util.NetCommonBeanUtil}
*
* @param jsonObject 将要解析的JsonObject
* @param <T> T
* @return 子类的具体类型
* @throws JSONException
*/
abstract public <T extends NetCommonBean> T getBeanByJson(JSONObject jsonObject) throws JSONException;
}
那么要使用这个类的话,NetBaseBeanUtil是不可以了,我们模仿他实现NetCommonBeanUtil
package com.chenjian.net.util;
import com.chenjian.net.bean.NetCommonBean;
import org.json.JSONException;
import org.json.JSONObject;
import java.lang.reflect.ParameterizedType;
/**
* NetCommonBean工具类,实现对泛型进行创建实例
* <p>
* 作者: ChenJian
* 时间: 2016.12.15 14:22
*/
public class NetCommonBeanUtil {
/**
* @param aClass 带泛型 T 的类,应该传入xxBeanListener监听器或其的子类。他的父类必须是带泛型T的
* @param tIndex 泛型 T 所在下标
* @param jsonObject 用来解析的 json
* @param <T> 泛型 T
* @return 返回泛型T的实例
* @throws JSONException
*/
public static <T extends NetCommonBean> T parseItem(Class aClass, int tIndex, JSONObject jsonObject) throws JSONException {
T t = getBean(aClass, tIndex);
t = t.getBeanByJson(jsonObject);
return t;
}
/**
* @param aClass 带泛型 T 的类,应该传入xxBeanListener监听器或其的子类。他的父类必须是带泛型T的
* @param tIndex 泛型 T 所在下标
* @param <T> 泛型 T
* @return 返回泛型T的实例
*/
@SuppressWarnings("unchecked")
private static <T extends NetCommonBean> T getBean(Class aClass, int tIndex) {
Class<T> entityClass = (Class<T>) ((ParameterizedType) aClass.getGenericSuperclass()).getActualTypeArguments()[tIndex];
T entity = null;
try {
// 使用newInstance创建实例的类,必须有无参构造方法
entity = entityClass.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return entity;
}
}
接下来开始使用。
假设服务端返回的数据是这样的
你需要定义一个Bean,继承自NetCommonBean
{
"code":"00001",
"message":"hello",
"time":"1479807260",
"data":{
"title":"title",
"desc":"desc",
"date":"date"
}
}
package com.chenjian.net.demo.bean;
import com.chenjian.net.bean.NetCommonBean;
import org.json.JSONException;
import org.json.JSONObject;
/**
* 作者: ChenJian
* 时间: 2016.12.30 20:40
*/
public class NetNewsBean extends NetCommonBean {
private String title;
private String desc;
private String date;
@Override
public NetNewsBean getBeanByJson(JSONObject jsonObject) throws JSONException {
// 你要自己解析:
// this.title = jsonObject.optString("title");
// this.desc = jsonObject.optString("desc");
// this.date = jsonObject.optString("date");
// return this;
// 你要用Gson解析,只需要一行代码:
// return new Gson().fromJson(jsonObject.toString(), NetNewsBean.class);
// 你要用fastjson解析,只需要一行代码
// return JSON.parseObject(jsonObject.toString(), NetNewsBean.class);
return null;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
@Override
public String toString() {
return "NetNewsBean{" +
"title='" + title + '\'' +
", desc='" + desc + '\'' +
", date='" + date + '\'' +
'}';
}
}
这个Bean是符合gson和fastjson规范的,在getBeanByJson方法里面,可以选择自己要用到的解析方式。
接着,我们开始使用
private void otherjson() {
UrlParse urlParse = new UrlParse(UrlConst.BASE_URL).appendRegion(UrlConst.INFO);
/**
* 需要将NetSingleBeanListener中onReceivedRet方法里的 T t = NetBaseBeanUtil.parseItem(getClass(), 0, object);
* 改成 T t = NetCommonBeanUtil.parseItem(getClass(), 0, object); 才能用
* NetSingleBeanListener中的泛型继承类也要修改成NetCommonBean而不是NetBaseBean
* 不然编译不通过,因为NetNewsBean继承的是NetCommonBean而不是NetBaseBean
*
* 当然,你所有的bean都能继承NetCommonBean,在使用通用或者自定义Listener(非otherjson)时候,
* 把其中的NetBaseBeanUtil.parseItem改成NetCommonBeanUtil.parseItem就行了,
* 这样用户就可以在bean里面自行选择是否使用otherjson了
*/
// NetHelper.get(urlParse.toString(), new NetSingleBeanListener<NetNewsBean>() {
// @Override
// protected void onError(CallbackCode errorCode, NetRetBean netRetBean) {
//
// }
//
// @Override
// protected void onSuccess(NetNewsBean newsBean) {
// System.out.println(newsBean.toString());
// }
// });
}
正如注释当中所说,需要将NetSingleBeanListener进行修改成下面这样才行
package com.chenjian.net.listener.async;
import com.chenjian.net.bean.NetCommonBean;
import com.chenjian.net.bean.NetRetBean;
import com.chenjian.net.listener.common.CallbackCode;
import com.chenjian.net.util.NetCommonBeanUtil;
import org.json.JSONException;
import org.json.JSONObject;
/**
* 返回是单个Bean的网络请求Listener
* <p>
* 作者: ChenJian
* 时间: 2016.12.15 14:54
*/
abstract public class NetSingleBeanListener<T extends NetCommonBean> extends NetHandleListener {
@Override
protected void onReceivedRet(NetRetBean netRetBean) throws JSONException {
JSONObject object = new JSONObject(netRetBean.getServerData());
T t = NetCommonBeanUtil.parseItem(getClass(), 0, object);
netRetBean.setServerObject(t);
handleResult(netRetBean);
}
@SuppressWarnings("unchecked")
@Override
protected void onSuccess(CallbackCode successCode, NetRetBean netRetBean) {
onSuccess((T) netRetBean.getServerObject());
}
/**
* 运行在ui线程,返回单个实体
*
* @param t 当前bean
*/
abstract protected void onSuccess(T t);
}
当然改成这样后,NetSingleBeanListener就不在能给NetBaseBean的子类用了,只能给NetCommonBean的子类用。
最后再来梳理一下使用。
如果服务端返回的数据是这样的
{
"code":"00001",
"message":"hello",
"time":"1479807260",
"data":{
"title":"title",
"desc":"desc",
"date":"date"
}
}
你的做法就有很多种了。最通用的做法,就是定义一个Bean,继承自NetCommonBean
package com.chenjian.net.demo.bean;
import com.chenjian.net.bean.NetCommonBean;
import org.json.JSONException;
import org.json.JSONObject;
/**
* 作者: ChenJian
* 时间: 2016.12.30 20:40
*/
public class NetNewsBean extends NetCommonBean {
private String title;
private String desc;
private String date;
@Override
public NetNewsBean getBeanByJson(JSONObject jsonObject) throws JSONException {
// 你要自己解析:
// this.title = jsonObject.optString("title");
// this.desc = jsonObject.optString("desc");
// this.date = jsonObject.optString("date");
// return this;
// 你要用Gson解析,只需要一行代码:
// return new Gson().fromJson(jsonObject.toString(), NetNewsBean.class);
// 你要用fastjson解析,只需要一行代码
// return JSON.parseObject(jsonObject.toString(), NetNewsBean.class);
return null;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
@Override
public String toString() {
return "NetNewsBean{" +
"title='" + title + '\'' +
", desc='" + desc + '\'' +
", date='" + date + '\'' +
'}';
}
}
在getBeanByJson方法里,根据自己的喜好和需求,来用不同的方法来解析。
然后就可以使用NetSingleBeanListener来请求
private void otherjson() {
NetHelper.get("url", new NetSingleBeanListener<NetNewsBean>() {
@Override
protected void onError(CallbackCode errorCode, NetRetBean netRetBean) {
}
@Override
protected void onSuccess(NetNewsBean newsBean) {
System.out.println(newsBean.toString());
}
});
}
或者你已经确定使用的是gson,你的Bean只需要符合gson规范就可以了
public class NetNewsBean {
private String title;
private String desc;
private String date;
}
直接使用NetSingleGsonListener
private void otherjson() {
NetHelper.get("url", new NetSingleGsonListener<NetNewsBean>() {
@Override
protected void onError(CallbackCode errorCode, NetRetBean netRetBean) {
}
@Override
protected void onSuccess(NetNewsBean netNewsBean) {
}
});
}
或者你已经确定使用的是fastjson,你的Bean只需要符合fastjson规范就可以了
public class NetNewsBean {
private String title;
private String desc;
private String date;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
}
直接使用NetSingleFastjsonListener
private void otherjson() {
NetHelper.get("url", new NetSingleFastjsonListener<NetNewsBean>() {
@Override
protected void onError(CallbackCode errorCode, NetRetBean netRetBean) {
}
@Override
protected void onSuccess(NetNewsBean netNewsBean) {
}
});
}
从上面的内容可以看出,你使用gson或者fastjson,其实也不需要用到NetSingleGsonListener和NetSingleFastjsonListener,你只要继承NetCommonBean,在getBeanByJson方法中选择自己想要用到的解析方式来解析就行了。但使用NetSingleGsonListener或者NetSingleFastjsonListener,Bean不需要继承NetCommonBean,可以不要写getBeanByJson代码。不过如果不介意多写一行解析直接返回的代码的话,笔者觉得NetSingleGsonListener和NetSingleFastjsonListener就是没用的,包括上一篇中讲解的为了支持第三方框架实现的那些Listener,都是没用的,只要开发者使用NetCommonBean就行了。
再回顾一下本篇开始时候说的问题,如果要支持第三方解析框架,在自定义Listener时会有问题,也就是自定义的Listener不能通用,当泛型有多个的时候,要适应每一种情况,就要写非常多类似的Listener。如果使用NetCommonBean,就根本不需要专门为第三方解析做兼容,不管是自定义Listener还是使用框架普通的Listener,用户都可以在Bean里面的getBeanByJson方法里面选择自己想要的解析方式。
可以说,如果你的项目需要用到第三方解析,而且在多个人协作开发的时候,不确定谁会用到第三方解析,那么就用NetCommonBean,不要再用NetBaseBean了,让实现Bean的人自己在Bean的内部选择用哪一种解析方式,自定义Bean时也不要再考虑多种情况了。当然框架自带的兼容第三方解析的所有Listener,比如NetSingleGsonListener,也可以删掉了。如果留着用也行,用他们时,可以少写一些代码。
但是很遗憾,本框架目前没有用到NetCommonBean。当然你可以修改少量代码就能用了。
因为NetCommonBean只是为了解决第三方解析框架而设计的,个人认为,在Bean中的非静态共有方法,也就是getBeanByJson方法,他返回的是一个本类对象,也就是下面这样
public class NetNewsBean extends NetCommonBean {
private String title;
private String desc;
private String date;
@Override
public NetNewsBean getBeanByJson(JSONObject jsonObject) throws JSONException {
// 你要自己解析:
// this.title = jsonObject.optString("title");
// this.desc = jsonObject.optString("desc");
// this.date = jsonObject.optString("date");
// return this;
// 你要用Gson解析,只需要一行代码:
// return new Gson().fromJson(jsonObject.toString(), NetNewsBean.class);
// 你要用fastjson解析,只需要一行代码
// return JSON.parseObject(jsonObject.toString(), NetNewsBean.class);
return null;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
@Override
public String toString() {
return "NetNewsBean{" +
"title='" + title + '\'' +
", desc='" + desc + '\'' +
", date='" + date + '\'' +
'}';
}
}
这看起来非常的奇怪,如果用到gson和fastjson时,在内存中,还浪费了一个对象空间。显然NetBaseBean中的initByJson方法看起来更规范。只能怪第三方解析的缺陷,不能在本类中将本类进行解析。
为什么是我去改变去兼容第三方框架呢?而不是第三方框架去兼容我呢?
做为一名程序员如果没有一点坚持,那跟咸鱼还有什么差别???
说不定哪一天,gson和fastjson提供了下面这样的解析方式
fromJson(jsonString, getClass(), this)
那么使用NetBaseBean也就可以和他们融合了。
当然到最后,还是要提醒,如果你要使用第三方解析框架,请用NetCommonBean,把用到NetBaseBeanUtil的地方,都改成NetCommonBeanUtil,框架中自带的支持第三方解析的Listener,可用可不用。
至此,发现问题,改善,解决了。
下一篇,将进行总结