This article is mostly about solving problems, so most of the code is fairly simple, so take a look. (Although I tend to skip large chunks of code when I read technical articles).

The content doesn’t go very far into Retrofit’s source code, but Gson provides nice functions for us to call.

Because everyone’s network library is not necessarily the same, so the article is written in general, we can refer to their own network framework.

Recently while maintaining a company project, I found that the network library used by the old project required manual conversion of the result to the data model each time it was received. It did not support the automatic conversion of the requested result string to an instantiated object like Retrofit does, that is, passing in multiple levels of generics and returning instantiated objects, for example:

1mApiService.postUserRegister(username, password) 2 .subscribeOn(Schedulers.io()) 3 .observeOn(AndroidSchedulers.mainThread()) 4 .subscribe(new BaseSubscriber<Result<UserModel>>() { 5 @Override 6 public void onNext(Result<UserModel> mResult) { 7 UserModel mUserModel = mResult.getData(); 8 mLoginView.register(null, mUserModel); 9 log.d (TAG, "registered successfully: "+ muserModel.getUsername ()); 1011 }1213 @Override14 public void onApiException(ApiException e) {1516 }17 });

Copy the code

BaseSubscriber<> receives a generic, and

> is a multilevel generic, and in the network request callback, public void onNext(Result

mResult), MResult is an instantiated object, which is a very common operation.

In this case, you need to understand how to convert multilevel generics. Some students will think that using Gson to convert multilevel generics is very common. What is special about it?

The most commonly used Gson to generics

When we use Gson to convert generics, we usually use the following example:

1Gson gson = new Gson(); 2String jsonStr = "{'username':'Caspar','password':'123456'}"; 3UserModel userModel = gson.fromJson(jsonStr, UserModel.class);

Copy the code

When converting a Json String, we usually use the gson.fromjson (String,Class

) function. The first argument takes a String and the second argument takes a Class type, which is used to pass in the type to be converted.

If you go back to the Retrofit example, Result

instead of Result< userModel.class >, that’s not right.

And since the above example is a simple transformation, not a multilevel generics transformation, let’s look at Gson converting multilevel generics.

Gson transforms multilevel generics

The most common way to convert multilevel generics is to convert the List

type. For example, we now have a set of user data ArrayList

that needs to be converted as follows:

1Gson gson = new Gson(); 2String jsonStr = "[" +3 "{'username':'Java','password':'123456'}," +4 "{'username':'Python','password':'123456'}" +5 "]"; 6ArrayList<UserModel> list = gson.fromJson(jsonStr, new TypeToken<ArrayList<UserModel>>(){}.getType());

Copy the code

ArrayList

and ArrayList

. Class

. New TypeToken

>(){}.getType() is passed in as a type to get the instantiated List object.



The first attempt to apply multi-level generic transformations to a network framework

Now recall that Retrofit is used for example: The new BaseSubscriber

>() method is used to get the instantiated object. The method described in section 1 is only suitable for single generics, not multilevel generics. It seemed to be possible, so I wrote a paragraph:

                                    

1public <T> void convertJson (String json, Type typeOfT) {2 Gson gson = new Gson(); 3 T result = gson.fromJson(json, typeOfT); 4 httpClient.onSuccess(result); 5}

Copy the code

The httpClient can be interpreted as a network request calling the onSuccess callback, passing back the result of the instantiation. This looks like a multilevel generics implementation, but let’s think about how we would actually use this function.

                                        

1String jsonStr = http.getUserList(); 2convertJson<ArrayList<UserModel>>(jsonStr,new TypeToken<ArrayList<UserModel>>(){}.getType()){ 3 @Override 4 public void onSuccess(ArrayList<UserModel> result) { 56 } 7})

Copy the code

We need to call convertJson, pass in the generic convertJson

>, and in the function’s parameters, New TypeToken

>(){}.getType() {ArrayList

>(){}.


Learn how Retrofit was implemented

So then look at the Retrofit GsonConverterFactory. Class is how to implement, Retrofit of students should be used for GsonConverterFactory. The class is more familiar, we often use it when initialization, So Retrofit and Gson can work together.

We found a function in GsonConverterFactory:

                                                

1@Override 2public Converter<ResponseBody, ? > responseBodyConverter(Type type, Annotation[] annotations, 3 Retrofit retrofit) {4 TypeAdapter<? > adapter = gson.getAdapter(TypeToken.get(type)); 5 return new GsonResponseBodyConverter<>(gson, adapter); 6}

Copy the code

Seeing that the conversion code takes a parameter of Type Type and converts it to a TypeAdapter using Typetoken.get (), I decided that the conversion should be a focus here, so I followed up with the following functions to see what adapter did:

Find a convert method, use newJsonReader to convert the value to JsonReader, where the value is the result of the network request, and then use Adapter.read (JsonReader) to convert the result to the instantiated object. Woc? Is it that simple? !

The key is the following two lines:

1JsonReader jsonReader = gson.newJsonReader(value.charStream()); 23return adapter.read(jsonReader);

Copy the code

Finally realize

Gson.getadapter (typeToken.get (type)); gson.getAdapter(type); Typetoken. get(type) is required, and all we need is a type type.

There should be a lot of people in learning and dealing with the problem of generics, go back to search for a variety of problems, should have seen a familiar function :(this code should be familiar with the memory, not familiar with the need to read deeply, it doesn’t matter)

Public BookManager extends GenricManager<Book> 4 * 5 * @param clazz clazz The class to introspect 6 * @param index the Index of the generic ddeclaration,start from 0. 7 */ 8public static Class getSuperClassGenricType(Class clazz, int index) throws IndexOutOfBoundsException { 910 Type genType = clazz.getGenericSuperclass(); 1112 if (! (genType instanceof ParameterizedType)) {13 return Object.class; 14 }1516 Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); 1718 if (index >= params.length || index < 0) {19 return Object.class; 20 }21 if (! (params[index] instanceof Class)) {22 if (params[index] instanceof ParameterizedType) {23 try {24 return Class.forName(((ParameterizedType) params[index]).getRawType().toString()); 25 } catch (ClassNotFoundException e) {26 //e.printStackTrace(); 27 }28 //return (Class) ((ParameterizedType) params[index]).getActualTypeArguments()[0]; 29 }30 return Object.class; 31 }32 return (Class) params[index]; 33}

Copy the code

Type[] params = ((ParameterizedType) genType).getActualTypearguments (); We get a Type[] object, and params[0] is the Type object we need, so we can get the desired TypeAdapter with two lines of code:

                                                                

1    Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); 2    mTypeAdapter = (TypeAdapter<T>) gson.getAdapter(TypeToken.get(params[0]));

Copy the code

After obtaining the result of the network request, it is easy to convert the result into a multi-level generic instantiation object:

1String json = httpClient.getUserList(); 2return adapter.fromJson(json);

Copy the code

Encapsulate as a Callback:

1public abstract class HttpCallback<T> { 2 private TypeAdapter<T> mTypeAdapter; 3 4 public HttpCallback() { 5 Type genType = this.getClass().getGenericSuperclass(); 6 Gson gson = new Gson(); 7 Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); 8 mTypeAdapter = (TypeAdapter<T>) gson.getAdapter(TypeToken.get(params[0])); 9 }1011 public T convertData(Response response) {12 String json = response.body(); 13 T data = mTypeAdapter.fromJson(json); 14 onSuccess(data, response); 15 }1617 public abstract void onSuccess(T data, Response response); 18 public abstract void onError(Response response, Exception e); 19 public abstract void onFinish(); 2021}

Copy the code

You plug HttpCallback into your network library, call the convertData() method, and call onSuccess() to call back the result to the caller.

For external use refer to:

1http.getUserList(HttpCallback<ArrayList<UserModel>>(){2    @Override3    public void onSuccess(ArrayList<UserModel> data) {45    }6})

Copy the code

You can use Retrofit in the same way.

By encapsulating the network framework in this way, it finally becomes less resistant to maintaining old projects.

Say it again

This article is mostly about solving problems, so most of the code is fairly simple, so take a look. (Although I tend to skip large chunks of code when I read technical articles).

The content doesn’t go very far into Retrofit’s source code, but Gson provides nice functions for us to call.

Because everyone’s network library is not necessarily the same, so the article is written in general, we can refer to their own network framework.