本文主要是为了解决问题,所以大部分代码都比较简单,所以可以看看。(虽然我平时看技术文章也会容易跳过大段大段的代码)。
内容并没有很深入Retrofit源码,主要是Gson提供了很好的函数给我们调用,大家放心食用。
由于每个人的网络库都不一定一样,所以文章中多是用通用的写法,大家可以参考到自己的网络框架中去。
最近在维护公司项目时,发现老项目用的网络库需要每次获得请求结果后手动对结果转换成数据模型,不支持像 Retrofit 那样能够自动转换请求结果的字符串为实例化对象,也就是传递多级泛型进去并返回实例化对象,例如:
1mApiService.postUserRegister(username, password)
2.subscribeOn(Schedulers.io())
3.observeOn(AndroidSchedulers.mainThread())
4.subscribe(newBaseSubscriber>() {
5@Override
6publicvoidonNext(Result mResult){
7UserModel mUserModel = mResult.getData();
8mLoginView.register(null, mUserModel);
9Log.d(TAG,"注册成功: "+mUserModel.getUsername());
10
11}
12
13@Override
14publicvoidonApiException(ApiException e){
15
16}
17});
代码中的接收一个泛型,而就是一个多级泛型,而在网络请求的回调中,,其中的就是一个实例化的对象,这是我们很常见的一个操作。
这时候就需要自己去了解如何转换多级泛型,有的同学会觉得,用Gson转换多级泛型很常见啊,有什么特别的,那我们先来看看我们最常见的用法。
最常用的Gson转泛型
我们平时使用Gson来转换泛型的时候,最常见的就是使用以下的例子:
1Gson gson =newGson();
2String jsonStr ="{'username':'Caspar','password':'123456'}";
3UserModel userModel = gson.fromJson(jsonStr, UserModel.class);
我们对一个Json字符串进行转换的时候,通常使用函数,第一个参数接收一个字符串,第二个参数接收一个Class类型,用来将需要转换的类型传递进去。
再回头看看Retrofit的例子,是,而不是,这样写可不对。
而且上述例子只是简单的转换,而不是多级泛型的转换,接下来看看Gson转换多级泛型。
Gson转换多级泛型
我们平时最常见的转换多级泛型,就是转换类型,例如现在有一组用户数据需要转换,代码如下:
1Gson gson =newGson();
2String jsonStr ="["+
3"{'username':'Java','password':'123456'},"+
4"{'username':'Python','password':'123456'}"+
5"]";
6ArrayList list = gson.fromJson(jsonStr,newTypeToken>(){}.getType());
我们传入的类型不是,也不是,我们在实际操作中发现这样写会报错,于是就开始上网搜索,得到的答案就是使用来作为类型传入,就可以得到实例化的List对象了。
第一次尝试将多级泛型转换应用在网络框架中
现在再回想一下,Retrofit是使用例如:方式,就能得到实例化对象,而我们在第一节中所讲的方法只适合单个泛型的转换,不适合多级泛型,再看看中的方法,好像是能够实现,于是就写了一段:
1publicvoidconvertJson(String json, Type typeOfT){
2Gson gson =newGson();
3T result = gson.fromJson(json, typeOfT);
4httpClient.onSuccess(result);
5}
这一段转换很简单,其中的可以理解为网络请求调用了onSuccess回调,将实例化的结果传递回去,这样看好像是实现了多级泛型,但是我们再想想,实际情况我们将会怎样使用这个函数呢?
1String jsonStr = http.getUserList();
2convertJson>(jsonStr,newTypeToken>(){}.getType()){
3@Override
4publicvoidonSuccess(ArrayList result){
5
6}
7})
我们需要调用convertJson,然后传入泛型,并且在函数的参数中,还需要再传一次类型,与我们预期的根本不一样,这样每次写网络请求那不累死去?
了解Retrofit是如何实现的
所以接着看了一下的是如何实现的,用过Retrofit的同学应该对比较眼熟,我们常在初始化的时候用到它,使得Retrofit和Gson能够配合使用。
我们发现在中有一个函数:
1@Override
2publicConverter responseBodyConverter(Type type, Annotation[] annotations,
3Retrofit retrofit) {
4TypeAdapter adapter = gson.getAdapter(TypeToken.get(type));
5returnnewGsonResponseBodyConverter(gson, adapter);
6}
发现转换代码接收一个类型的参数,然后用转换成,觉得这里的转换应该是一个重点,于是继续跟进下面的函数,看看使用adapter做了什么:
发现了一个convert方法,使用将value转换为,这里的value就是网络请求的结果,然后用就将结果转换成实例化对象了?Woc?这么简单的吗?!
重点就是如下两行:
1JsonReader jsonReader = gson.newJsonReader(value.charStream());
2
3returnadapter.read(jsonReader);
最终实现
既然发现了Gson还有这么神奇的用法,那我们像回推导,现在我们需要,TypeAdapter是由获取的,那就需要,这时候发现,我们需要的就是一个类型。
应该有很多人在学习和处理泛型这个问题上,回去搜索各种各样的问题,应该见到过一个熟悉的函数:(这段代码熟悉的应该有印象,不熟悉不用深读,没关系的)
1/**
2* 通过反射,获得定义Class时声明的父类的范型参数的类型.
3* 如public BookManager extends GenricManager
4*
5*@paramclazz clazz The class to introspect
6*@paramindex the Index of the generic ddeclaration,start from 0.
7*/
8publicstaticClassgetSuperClassGenricType(Class clazz,intindex)throwsIndexOutOfBoundsException{
9
10Type genType = clazz.getGenericSuperclass();
11
12if(!(genTypeinstanceofParameterizedType)) {
13returnObject.class;
14}
15
16Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
17
18if(index >= params.length || index
19returnObject.class;
20}
21if(!(params[index]instanceofClass)) {
22if(params[index]instanceofParameterizedType) {
23try{
24returnClass.forName(((ParameterizedType) params[index]).getRawType().toString());
25}catch(ClassNotFoundException e) {
26//e.printStackTrace();
27}
28//return (Class) ((ParameterizedType) params[index]).getActualTypeArguments()[0];
29}
30returnObject.class;
31}
32return(Class) params[index];
33}
其中就能获取到一个对象,而就是我们需要的对象了,于是我们可以通过两行代码得到我们想要的:
1Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
2mTypeAdapter = (TypeAdapter) gson.getAdapter(TypeToken.get(params[]));
在获取到网络请求结果之后,就可以很简单的将结果转换成多级泛型的实例化对象了:
1String json = httpClient.getUserList();
2returnadapter.fromJson(json);
封装成一个Callback:
1publicabstractclassHttpCallback{
2privateTypeAdapter mTypeAdapter;
3
4publicHttpCallback(){
5Type genType =this.getClass().getGenericSuperclass();
6Gson gson =newGson();
7Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
8mTypeAdapter = (TypeAdapter) gson.getAdapter(TypeToken.get(params[]));
9}
10
11publicTconvertData(Response response){
12String json = response.body();
13T data = mTypeAdapter.fromJson(json);
14onSuccess(data, response);
15}
16
17publicabstractvoidonSuccess(T data, Response response);
18publicabstractvoidonError(Response response, Exception e);
19publicabstractvoidonFinish();
20
21}
让自己的网络库中接入,调用方法,就可以将多级泛型转换,并且调用将结果回调给调用者。
在外部使用可参考:
1http.getUserList(HttpCallback>(){
2@Override
3publicvoidonSuccess(ArrayList data){
4
5}
6})
就可以实现和Retrofit一样的使用方法了。
将网络框架这样封装后,终于对维护老项目没有那么抵触了,舒坦!
再啰嗦一遍
本文主要是为了解决问题,所以大部分代码都比较简单,所以可以看看。(虽然我平时看技术文章也会容易跳过大段大段的代码)。
内容并没有很深入Retrofit源码,主要是Gson提供了很好的函数给我们调用,大家放心食用。
由于每个人的网络库都不一定一样,所以文章中多是用通用的写法,大家可以参考到自己的网络框架中去。
领取专属 10元无门槛券
私享最新 技术干货