继续延续上一篇 MonkeyLei:Retrofit+Rxjava-以自己的方式重头开始-入门篇 的使用入门,我们继续深入点使用。当然有几点问题:
-
addConverterFactory里面到底是个什么喵
-
addCallAdapterFactory到底又是什么,有什么用?再没有用Rxjava的时候,目前都没有用到这个.....(这个下一篇看看怎么用妮...今天就暂时喵一喵Converter)
1.1. addConverterFactory里面的->responseBodyConverter/requestBodyConverter怎么玩?(其实目前从方法名称看,一个是响应的结果转换,一个是请求体的转换)
1-1 其实上一篇我们就有个 ToStringConverterFactory.java 的转换器,搬砖了一两个项目了,应该还是能看个123。 里面主要就是响应结果做了一个中转处理(其中requestBodyConverter都没有用到,因为我们直接就是get请求,没有涉及到@Body 参数):
image什么情况下会触发requestBodyConverter呢?我们试试Post方法,然后随便给一个@Body参数 看:
// 添加ToStringConverterFactory解析器 - 返回字符串结果【本质:ResponseBody value -> value.string()】
// 额外添加一个Body参数,触发requestBodyConverter回调; @GET/@DELTE这些不支持Body
// @Body不能与@FormUrlEncoded共用,否则报错;而@Field偏偏是提交表单使用的,需要@FormUrlEncoded,So得出@Body和@Field也是不能同时使用的
@POST("users/{user}/repos")
fun listReposString(@Path("user") user: String, @Body data: String): Call<String>
image
然后 ToStringConverterFactory.java 里面打印下日志:
package com.hl.rxnettest;
import android.util.Log;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;
public class ToStringConverterFactory extends Converter.Factory {
private static final MediaType MEDIA_TYPE = MediaType.parse("text/plain");
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
if (String.class.equals(type)) {
return new Converter<ResponseBody, String>() {
@Override
public String convert(ResponseBody value) throws IOException {
Log.e("test", "responseBodyConverter");
return value.string();
}
};
}
return null;
}
@Override public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations,
Annotation[] methodAnnotations, Retrofit retrofit) {
Log.e("test", type.toString());
if (String.class.equals(type)) {
return new Converter<String, RequestBody>() {
@Override
public RequestBody convert(String value) throws IOException {
Log.e("test", "requestBodyConverter convert value=" + value);
return RequestBody.create(MEDIA_TYPE, value);
}
};
}
return null;
}
}
image
然后基于上一篇的工程调用下:
// 方式1: 创建网络请求接口实例 - 返回字符串结果
// var reposString = gitHubService.listReposString("FanChael")
var reposString = gitHubService.listReposString("FanChael", "s-requestBodyConverter")
reposString.enqueue(object : Callback<String>{ // object的作用是调用内部匿名类
override fun onResponse(call: Call<String>, response: Response<String>){
if (response.isSuccessful) {
// response.body() -> String
// Log.e("reposString-onResponse", response.body())
} else {
Log.e("reposString-onResponse", "请求错误了!")
}
}
override fun onFailure(call: Call<String>, t: Throwable){
Log.e("reposString-onFailure", t.message)
}
})
然后就可以发现,我们requestBodyConverter回调就会走了。
image有时候我们需要提前对请求的数据进行加密或者其他处理,同时请求的数据也需要解密之类的操作,这个时候我们就需要重写Converter.Factory的两个Converter方法,所以我们需要知道如何进行加密数据的处理,传递。
这里给网友的两种方式,不太一样... Retrofit 之加密和解密 https://blog.csdn.net/zr940326/article/details/51549310
这样的话,基本上大概就知道怎么利用回调做一些预处理了。官方API也是有的啦:Retrofit 2.3.0 API
imageOK!到这里有点了解了。。。。
**1-2 **这样我们就大概知道这个Convert到底做了啥。其实就是一个请求或者响应的预处理,我们想要拿到结果是一个对象,字符串,我们就需要定义一个转换器来实现,否则我们只能拿到原始的ResponseBody,自己进行处理!阿里的FastJson,小萌新再用,性能貌似不错https://blog.csdn.net/qq_29384639/article/details/81661029 -> 只是一个说法而已!Gson用户还蛮多的,fastjson使用可能比较方便,Gson官方,相对有些地方原始些罢了!
1-3 Now,我们可以跟踪下GsonConverterFactory的源码先看个大概....
1.3.1 从头开始跳转过去,先看设置的地方
image如下, 然后到create函数,主要是创建了一个Gson(我们有时候会自己创建Gson解析器来用,所以不陌生吧)
image其中就肯定有我们的响应和请求的回调了。。。你看网上的很多加密,解密,自定义转换器,基本都是这样的结构。或者都是基于这个进行了定制,增加了自己的一些逻辑!
imageGsonResponseBodyConverter.java
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package retrofit2.converter.gson;
import com.google.gson.Gson;
import com.google.gson.JsonIOException;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import java.io.IOException;
import okhttp3.ResponseBody;
import retrofit2.Converter;
final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final Gson gson;
private final TypeAdapter<T> adapter;
GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
T result = adapter.read(jsonReader);
if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
throw new JsonIOException("JSON document was not fully consumed.");
}
return result;
} finally {
value.close();
}
}
}
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package retrofit2.converter.gson;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okio.Buffer;
import retrofit2.Converter;
final class GsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
private static final MediaType MEDIA_TYPE = MediaType.get("application/json; charset=UTF-8");
private static final Charset UTF_8 = Charset.forName("UTF-8");
private final Gson gson;
private final TypeAdapter<T> adapter;
GsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override public RequestBody convert(T value) throws IOException {
Buffer buffer = new Buffer();
Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
JsonWriter jsonWriter = gson.newJsonWriter(writer);
adapter.write(jsonWriter, value);
jsonWriter.close();
return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
}
}
上面两个转换器的具体实现类,看不懂,呜呜。。。看来需要冷静了(后面研究下妮)。。。可以先看下网友怎么自定义,然后添加加密解密的:
Retrofit 之加密和解密 https://blog.csdn.net/zr940326/article/details/51549310
实现的关键:可以把GsonConverterFactory先搬过来,然后再关键的地方修改。比如这篇关于如何统一处理响应的codeRetrofit自定义GsonConverter处理所有请求错误情况 【基本就是改造的GsonConverterFactory,建议我们也这样做】,然后返回ApiException,最后到BaseSubscriber的onError中进行细节处理。就像小萌新之前封装的BaseSubscribers一样。。。如下,网友的处理:
image**自我说下 **上面关键的就是请求和响应的Type的处理,就是泛型的GsonResponseBodyConverter<T>、TypeAdapter<T>的处理,如何应对到我们请求的类型以及响应的类型。。。。
如果不考虑这些,我们都可以写死实现,比如:
Repo.kt
package com.hl.rxnettest
class Repo{
var id: Long = -1
public var name: String? = null
var full_name: String? = null
// class owner - not need
var html_url: String? = null
var description: String? = null
}
package com.hl.rxnettest;
import android.support.annotation.Nullable;
import android.util.Base64;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.List;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;
public class MGsonConverterFactory extends Converter.Factory {
private final Gson gson;
public MGsonConverterFactory(Gson gson) {
this.gson = gson;
}
@Nullable
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
return new Converter<ResponseBody, List<Repo>>() {
@Nullable
@Override
public List<Repo> convert(ResponseBody value) throws IOException {
// 获取返回的字符串结果
String result = value.string();
// 用Gson转换为对象列表 - 这里展示 Converter.FactoryJson转换数据到对象的过程
// -至于如何做到所有的对象,对象列表的转换,那就需要进一步去实现,同时这个type对应的对象的类型该如何运用还得进一步深入解读
List<Repo> repoList = gson.fromJson(result, new TypeToken<List<Repo>>(){}.getType());
// 返回转换后的对象列表 - GsonConverterFactory.java源码感觉有点难妮,自己太忒么菜了!
return repoList;
}
};
}
}
然后使用一把
@GET("users/{user}/repos")
fun listRepos(@Path("user") user: String): Call<List<Repo>>
.addConverterFactory(MGsonConverterFactory(Gson()))
// 方式2: 创建网络请求接口实例 - Gson解析后返回对象列表
var repos = gitHubService.listRepos("FanChael")
// 发起请求,UI线程需要异步请求
repos.enqueue(object : Callback<List<Repo>>{ // object的作用是调用内部匿名类
override fun onResponse(call: Call<List<Repo>>, response: Response<List<Repo>>){
if (response.isSuccessful) {
// response.body() -> List<Repo>
Log.e("repos-onResponse", response.body()?.get(0)?.name)
} else {
Log.e("repos-onResponse", "请求错误了!")
}
}
override fun onFailure(call: Call<List<Repo>>, t: Throwable){
Log.e("repos-onFailure", t.message)
}
})
But,我们不能这样写死,那样就玩球了呀。。。必须要适应所有的类型。我们这样实践主要是为了加深对这个过程的记忆理解,哪怕先肌肉理解也行呀!
Converter了解先到这。还得继续加深,然后加强理解和拜读......下一篇先了解下CallAdapter,官方api会下拜读一下。争取Retrofit过程都有所熟悉。然后再深入转换部分。。。注解,反射,泛型等。。。。。
https://github.com/FanChael/RxNet
加油,菜鸟的我们!
网友评论