基于最原始、最愚蠢的网络请求返回包括网络请求状态,引发的一些思考
( 只需要编写body部分,而且body对应的bean可以通过泛型传入,代替Class作为参数传入,将解析逻辑抽取并做到可配置,且可分类配置 )
举个栗子先
// 举个栗子,下面这个Json作为我们要解析的返回数据
{
"header": {
"code": "SUCCESS",
"message": "响应成功"
},
"body": {
"verificationNum": "XX数量",
"info": [
{
"prodName": "产品名称",
"count": "份数",
"qrcode": "券号",
"verificationTime": "XX时间"
}
]
}
}
原始的解析
最原始的解析Demo:
public <T> void requestNetOld(final HashMap<String, String> requestMap,
final String url, final Class<? extends BaseBean> clz, final NetCallBack<T> netCallBack) {
new Thread() {
@Override
public void run() {
super.run();
// 网络请求
String result = postDownloadJson(requestMap, url);
if (netCallBack == null)
return;
final T orderState = (T) JSON.parseObject(result, clz);
mHandler.post(new Runnable() {
@Override
public void run() {
netCallBack.onBack(orderState);
}
});
}
}.start();
}
看看这个BaseBean:
public abstract class BaseBean {
private Header header;
// ......
public static class Header{
private String code;
private String message;
// ......
}
}
下面是具体的实现类:
public static class DemoBean extends BaseBean{
private Body body;
public Body getBody() {
return body;
}
public void setBody(Body body) {
this.body = body;
}
}
public static class Body{
// ......
}
来模拟下请求:
NetUtils.getInstance().requestNetOld(hashMap, url, DemoBean.class, new NetCallBack<DemoBean>() {
public void onBack(DemoBean bean){
}
});
痛点
恶心的Demo终于结束了,这里有几个痛点:
- DemoBean.class作为参数,每次都得传,麻烦。
- 每次请求除了编写Body类还需要编写一个继承与BaseBean看起来毫无意义的包装类。
不需要传Class类型了
我们经常会使用到泛型,考虑泛型传递Class类型进行json解析,这个思路很多人觉得行不通,因为Java的泛型只存在于编译器,运行期Class上的泛型就被擦除了,属于假泛型。
是的,Class类上的泛型运行期确实没有任何泛型信息了,而且即便是给对象的泛型变量赋上具体值,再debug这个对象的这个字段的时候,会发现还是Object类型,所以Class上没有,对象的成员变量上也没有,还有一个思虑是能够动态构建出一个不存在的class,就是几个class拼接成一个新的class,这个想法也是无法实现的,Java反射之后只能拿到类型,修改Type里面的类型,其实并没有真正改变Class类型,因为Java是静态语言。
[ 修正 start……
修正:Java是准动态语言,体现在几个方面:
1.反射机制
2.动态编译
3.动态执行javascript代码
4.动态字节码操作
5.动态转换类型
- 反射:只能动态修改对象属性,动态调用对象方法,动态生成对象,于Class无效。
- 动态编译:(此处无效)
- 可以做一个浏览器端编写java代码,上传服务器编译和运行的在线评测系统,需要进行安全检查。
- 服务器动态加载某些类文件进行编译
- 动态执行javascript代码:在这里无用。
- 动态字节码操作:( 很明显这种方式是可行的,应用也很帅气,只是苦于囊中羞涩——还不会~~ )
- 随着 AOP(Aspect Oriented Programming)的发展,代码动态生成已然成为 Java 世界中不可或缺的一环。本文将介绍一种小巧轻便的 Java 字节码操控框架 ASM,它能方便地生成和改造 Java 代码。著名的框架,如 Hibernate 和 Spring 在底层都用到了 ASM。
- ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
- 动态改变 Java 类就是要解决 AOP 的问题,提供一种得到系统支持的可编程的方法,自动化地生成或者增强 Java 代码。这种技术已经广泛应用于最新的 Java 框架内,如 Hibernate,Spring 等。
- 动态转换类型:从叫法上将貌似是我们需要的,Java自带这个功能,并且Java8经过了改进
直接上代码了:
public class NetCallBack3<T> {
public T body;
// ......
}
NetCallBack3 netCallBack3 = new NetCallBack3();
netCallBack3.setBody(new TextView(this));
TextView.class.cast(netCallBack3.getBody());
netCallBack3.body.toString();
// 仍要进行强转
TextView cBody = (TextView) netCallBack3.body;
try {
Field body = netCallBack3.getClass().getDeclaredField("body");
String name = body.getType().getName();
System.out.print(name);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
这里给body进行的具体赋值,并且将body进行了强转,但后面获取body引用给TextView的时候仍然需要进行强转,看看debug数据,发现class中的body类型还是object,这里的运行时强转,仅仅是针对对象的。
……修正 end ]
我从JavaEE同学的博客上知晓了一种方法可以在运行期拿到泛型类型(Hibernate 上有应用),就是创建对象时,其类上的泛型,可以通过父类的class的某个参数得到:
((Class<T>) (((ParameterizedType) (getClass().getGenericSuperclass())).getActualTypeArguments()[0]));
具体代码:
public abstract class NetCallBack2<T> {
private Class<T> tClass;
@SuppressWarnings("unchecked")
public Class<T> getTClz() {
if (tClass == null) {
tClass = ((Class<T>) (((ParameterizedType) (this.getClass()
.getGenericSuperclass())).getActualTypeArguments()[0]));
}
return tClass;
}
}
调用:
NetCallBack2<TextView> netCallBack = new NetCallBack2<TextView>() {};
Log.e("tag" ,netCallBack.getTClz().getName());
Log输出:
09-22 20:10:37.732 14149-14149/com.turbo.test E/tag: android.widget.TextView
得到了吧~~~,注意这里必须是创建类对象代码(至少是抽象类),并且要从父类类对象参数中获取
这个泛型可以用到抽象的回调上,完美!!
只需要编写Body部分的JavaBean
这个方法大多数人都知道,仅仅是我还是没想到(蠢了蠢了~~),解决办法就是先手动解析出Header部分以及Body部分,然后将body、header部分分别进行json解析。
public static String getBodyStr(String dataFormServer){
JSONObject jsonObject = null;
try {
jsonObject = new JSONObject(dataFormServer);
return jsonObject.optJSONObject("body").toString();
} catch (JSONException e) {
e.printStackTrace();
}
return errorJson;
}
String bodyStr = XXXX.getDataStr(response);
TTTTTT reportNewBean = new Gson().fromJson(bodyStr , TTTTTT.class);
Header部分同理,解析成对应Bean并且设置到逻辑流程中去,最好将Header的解析设置成可配置模块,解析返回一个HeaderConfig对象给请求框架即可。
附加(泛型):
看下泛型的跨类跨方法传递:
以下伪代码:
public class FirstDoor<T> {
SecondDoor<T> secondDoor;
public void firstMethod(CallBack<T> callBack){
secondDoor = new SecondDoor<T>();
secondDoor.setCallBack(callBack);
secondDoor.callBack1((T) new Object());
}
public SecondDoor<T> getSecondDoor(){
return secondDoor;
}
}
class SecondDoor<T> {
private CallBack<T> callBack;
public void setCallBack(CallBack<T> callBack) {
this.callBack = callBack;
}
public void setCallBack2(CallBack<T> callBack) {}
public void callBack1(T t) {
callBack.callback(t);
}
}
interface CallBack<T> {
public void callback(T t);
}
FirstDoor<TextView> firstDoor = new FirstDoor<>();
firstDoor.firstMethod(new CallBack<TextView>() {
@Override
public void callback(TextView textView) {
}
});
SecondDoor<TextView> secondDoor = firstDoor.getSecondDoor();
firstDoor.getSecondDoor().setCallBack2(new CallBack<TextView>() {
@Override
public void callback(TextView textView) {
}
});
最后这段调用还是自动生成了CallBack的类泛型,自动提示生成了callback方法的泛型参数。泛型跨类跨方法没有问题。
泛型小知识:
List<? extends Number> eList = null;
eList = new ArrayList<Integer>();
Number numObject = eList.get(0); //语句1,正确
//Type mismatch: cannot convert from capture#3-of ? extends Number to Integer
Integer intObject = eList.get(0); //语句2,错误
//The method add(capture#3-of ? extends Number) in the type List<capture#3-of ? extends Number> is not applicable for the arguments (Integer)
eList.add(new Integer(1)); //语句3,错误
List<? super Integer> sList = null;
sList = new ArrayList<Number>();
//Type mismatch: cannot convert from capture#5-of ? super Integer to Number
Number numObj = sList.get(0); //语句1,错误
//Type mismatch: cannot convert from capture#6-of ? super Integer to Integer
Integer intObj = sList.get(0); //语句2,错误
sList.add(new Integer(1)); //语句3,正确
? super T
表示一个特定的类,这个类可能是T,也可能是T的父类或者超类,甚至OTject
- 你可以将T或者T的子类的实例赋值给这个”变量”,因为T或者T的子类 肯定是一个OTject(或者其他任何T的超类)
- 但你无法将这个”变量”赋值给其他”变量”,因为无法确定这个”变量”的具体类型
? extends T
表示一个特定的类,这个类可能是T,也可能是T的任意子类
- 你可以把这个”变量”赋值给任意用T或者T的超类声明的”变量”
- 但你无法为这个”变量”赋值,因为根本无法知道这个”变量”的类到底是T还是T的哪个子类
有人可能疑惑为何extends add会失败,而super add成功,举个栗子就知道了:
List<TextView> list = new ArrayList<>();
list.add(new Button(this));
list.add(new EditText(this));
list.add(new AppCompatButton(this));
其实add的内容是<>内部总范围的子类即可,到此上面全部得到解释。
PS 其实Gson也是这么做的:
public class TypeToken<T> {
final Class<? super T> rawType;
final Type type;
final int hashCode;
protected TypeToken() {
this.type = getSuperclassTypeParameter(this.getClass());
this.rawType = Types.getRawType(this.type);
this.hashCode = this.type.hashCode();
}
static Type getSuperclassTypeParameter(Class<?> subclass) {
Type superclass = subclass.getGenericSuperclass();
if(superclass instanceof Class) {
throw new RuntimeException("Missing type parameter.");
} else {
ParameterizedType parameterized = (ParameterizedType)superclass;
return Types.canonicalize(parameterized.getActualTypeArguments()[0]);
}
}
public final Class<? super T> getRawType() {
return this.rawType;
}
}
public static Class<?> getRawType(Type type) {
if(type instanceof Class) {
return (Class)type;
} else if(type instanceof ParameterizedType) {
ParameterizedType className2 = (ParameterizedType)type;
Type rawType = className2.getRawType();
Preconditions.checkArgument(rawType instanceof Class);
return (Class)rawType;
} else if(type instanceof GenericArrayType) {
Type className1 = ((GenericArrayType)type).getGenericComponentType();
return Array.newInstance(getRawType(className1), 0).getClass();
} else if(type instanceof TypeVariable) {
return Object.class;
} else if(type instanceof WildcardType) {
return getRawType(((WildcardType)type).getUpperBounds()[0]);
} else {
String className = type == null?"null":type.getClass().getName();
throw new IllegalArgumentException("Expected a Class, ParameterizedType, or GenericArrayType, but <" + type + "> is of type " + className);
}
}