本文写作的目的是为自己以后温故知新而留的笔记,文章中所有的代码均是我逐行测试过的,读者可以将代码直接copy到自己的项目中直接使用。
Retrofit是由square公司开发的,最近很火而且值得信赖,与rxjava,okhttp等有很好兼容性。square在github上发布了很多优秀的Android开源项目。例如:leakcanary(排查内存泄露),android-times-square(日历控件),dagger(依赖注入),picasso(异步加载图片),okhttp(网络请求),retrofit(网络请求)等。retrofit是REST安卓客户端请求库。使用retrofit可以进行GET,POST,PUT,DELETE等请求方式。其实Retrofit只是一个框架,他的底层的实现是由okhttp实现的(关于okhttp的封装和使用比较不错的文章和工具有呵呵鸿洋的okhttpUtil。想用可以自己去研究和封装)。这里会介绍Retrofit的配置,使用,源码解析;Retrofit比较鲜明的特性就是使用注解的方式来实现功能,同时提供了利于扩展的api,有利于我们实现高度的定制。
总之就是很牛,你值得拥有。
retrofit2官网地址:https://github.com/square/retrofit/
下面我准备从环境配置到源码解析一一来为大家拨开Retrofit的面纱,由浅极深。没什么难理解的东西,对于初期接触的你和使用了但是不了解内部精髓的你相信会有一些帮助。
下面都是在AS中的配置使用,因为相信都该转到AS上来了。
compile ‘com.squareup.retrofit2:retrofit:2.2.0’ 配置Retrofit
compile ‘com.google.code.gson:gson:2.8.0’ 配置gson
compile ‘com.squareup.okhttp3:okhttp:3.6.0’ 配置okhttp
compile ‘com.squareup.okio:okio:1.11.0’ 配置操作流的okio
compile ‘com.squareup.retrofit2:converter-gson:2.2.0’ 配置了一个默认的Retrofit中用的json转化器工具(当然还有别的,至于作用后面会详细介绍,这里就认为是处理发送和接受的数据的工具)
其他的如:
Gson: com.squareup.retrofit2:converter-gson
Jackson: com.squareup.retrofit2:converter-jackson
Moshi: com.squareup.retrofit2:converter-moshi
Protobuf: com.squareup.retrofit2:converter-protobuf
Wire: com.squareup.retrofit2:converter-wire
Simple XML: com.squareup.retrofit2:converter-simplexml
Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars
先来理清文章的脉络:
a.GET请求
b.POST请求
c.文件的上传(单文件,监听上传的进度功能)
d.文件的下载(下载进度监听功能实现)
e.自定义ConverterFactory,自定义Converter
f.最后是源码的分析
retrofit在使用的过程中,需要定义一个接口对象,我们首先演示一个最简单的get请求,代码如下所示:
//注释接口
public interface RetrofitHttpService {
@GET//个人喜欢不将baseUrl后面的部分放在注解方法后面作为值,所以我这里只是写方法名,当然要在方法中加上@Url String url
Call getMessage(@Url String url, @QueryMap Map params);
//定义了回调参数的类型为String
//方法的名称getMessage,参数为拼接完好的url和?后面的参数(放在集合map中并用@QueryMap进行注解)
//个人比较懒其实还可以将参数一个个传入并使用@Query作为注解,那么就会写好几个@Query String param作为参数并用“,”(逗号)隔开,那么在增加和删除参数的时候就会要更改接口带来麻烦
}
//具体演示功能Activity
public class NoticeCenterActivity extends AppCompatActivity {
private ListView showLv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_notice_center);
showLv = (ListView) findViewById(R.id.show_Lv);
Intent intent = getIntent();
User user = (User) intent.getSerializableExtra("user");
getNetData(user);
}
private void getNetData(User user) {
//accessToken, userId, stateId, startTime, endTime前面是get请求要的几个参数
String accessToken = user.getToken();
String userId = user.getId();
String stateId = "1";
String startTime = "2016-7-07";
String endTime = "2017-3-31";
Map params = new HashMap();
params.put("accessToken",accessToken);
params.put("userId",userId);
params.put("stateId",stateId);
params.put("startTime",startTime);
params.put("endTime",endTime);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(“你自己的baseUrl”)//设置baseUrl如http://www.baidu.com
.addConverterFactory(StringGsonConverterFactory.create())//添加请求和返回数据处理器工厂,用来处理发送的数据和返回的数据
.build();
RetrofitHttpService service = retrofit.create(RetrofitHttpService.class);//创建接口的实例
Call call = service.getMessage(“你自己拼接好的完整的Url”, params);//生成请求队列并加入
call.enqueue(new Callback() {//执行请求操作
@Override
public void onResponse(Call call, Response response) {
//这里返回的是原始的json字符串,自己解析(实际使用中要自己在外面封装一层,直接返回一个实体类)
String rawStr = response.body();
Log.e("rawStr",rawStr);
try {
/**
*返回的数据结构:
{
"result": {
},
"error": {
"message": "",
"code": "0"
}
}
*
*/
JSONObject jsonObject = new JSONObject(rawStr);
JSONObject jsonObject2 = jsonObject.getJSONObject("error");
JSONArray jsonArray = jsonObject.getJSONArray("result");
Log.e("jsonArray",jsonArray.toString());
List messages = GsonUtil.jsonToList(jsonArray.toString(),NoticeMessage.class);
MessageAdapter adapter = new MessageAdapter(NoticeCenterActivity.this, messages);
showLv.setAdapter(adapter);
} catch (Exception e) {
Log.e("JSONException",e.toString());
e.printStackTrace();
}
}
@Override
public void onFailure(Call call, Throwable t) {
Log.e("onFailure", t.toString());
}
});
}
}
/**
* Created by gxo on 2017/3/31.
* 自定义的转换器类。
* 请求方面转化为json格式,最后封装成ok3的RequestBody;
* 返回数据方面返回最原始的json字符串,为了便于自己对返回数据进行解析。
*/
public class StringGsonConverterFactory extends Converter.Factory{
private Gson gson;
private StringGsonConverterFactory(Gson gson){
this.gson = gson;
}
public static StringGsonConverterFactory create(){
return new StringGsonConverterFactory(new Gson());
}
@Override
public Converter, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter> adapter = gson.getAdapter(TypeToken.get(type));
return new CustomGsonRequestConverter<>(gson, adapter);
}
@Override
public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
return new StringGsonResponseConverter(gson);
}
}
/**
* Created by gxo on 2017/3/29.
* 请求数据转化工具类。
* 这里就是最原始的GsonConverterFactory中的那个RequestConverter
*/
class CustomGsonRequestConverter<T> implements Converter<T, RequestBody>{
private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
private static final Charset UTF_8 = Charset.forName("UTF-8");
private final Gson gson;
private final TypeAdapter adapter;
public CustomGsonRequestConverter(Gson gson, TypeAdapter 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());
}
}
/**
* Created by gxo on 2017/3/31.
* 自定义的返回数据转化器类
* 转化返回最原始的json字符串
*/
public class StringGsonResponseConverter implements Converter<ResponseBody,String>{
private Gson gson;
public StringGsonResponseConverter(Gson gson){
this.gson = gson;
}
@Override
public String convert(ResponseBody value) throws IOException {
return value.string();
}
}
/**
* Created by gxo on 2017/3/30.
* json格式数据转化工具类使用Gson
*/
public class GsonUtil {
private static Gson gson = null;
static {
if (gson == null) {
gson = new Gson();
}
}
private GsonUtil() {
}
/**
* bean转成json
*/
public static String GsonString(Object object) {
String gsonString = null;
if (gson != null) {
gsonString = gson.toJson(object);
}
return gsonString;
}
/**
* json转成bean
*/
public static T GsonToBean(String gsonString, Class cls) {
T t = null;
if (gson != null) {
t = gson.fromJson(gsonString, cls);
}
return t;
}
/**
* json转成list
* 解决泛型问题
*/
public static List jsonToList(String json, Class cls) {
List list = new ArrayList();
JsonArray array = new JsonParser().parse(json).getAsJsonArray();
for(final JsonElement elem : array){
list.add(gson.fromJson(elem, cls));
}
return list;
}
/**
* json转成list中有map的
*/
public static List
说明:
上来就搞得好像很复杂但是很简单,
1.首先自己写个接口,注明什么请求,方法中注明要请求的url,参数
2.Retrofit实例的构造
3.获得接口实例并获得请求队列将请求任务加入其中
4.执行请求,并完成异步回调
上面的代码中所用的注意点和各个类,方法的作用已经写的很清楚了,仔细看很容易。不会的先只记住功能下面都会详细分析。
相信大家对Retrofit有了些许的了解,那么接下来我们先来总体罗列一下Retrofit中所有的功能和注意点
常用的请求类型
@GET,@POST,@PUT,@DELETE
但是我相信绝大部分时候只要@GET,@POST足以
addConverterFactory中转化器工厂类(很简单,现在你不用完全了解,后面有详细分析,并让你有能力自己写一个)这里可以自定义属于自己的工厂类,实现高度的定制,利于扩展。但是我个人认为不需要过多的写这个类,而是应该回调原始的json字符串,最后再封装一层,获得最终想要的实体类。(这里只是考虑返回普通json数据的情况,如果是下载文件等特殊情况直接做io操作并返回自己想要的信息即可)
先看代码:交代一下情景,简单的一个登陆界面
接口如下,有注释:
public interface RetrofitHttpService {
@GET
@Streaming
Call downloadFile(@Url String url);
@POST
@Multipart
Call uploadFile(@Url String url,@Part("accessToken") RequestBody body, @Part MultipartBody.Part part);
//使用表单键值对的方式post
@POST //POST请求
@FormUrlEncoded //使用表单的方式
Call login(@Url String url, @FieldMap Map params);
//注意这里将回调的参数类型设置为了User类,就是说我们在后面要自定义一个转化器工厂类,具体见下面代码。
//完整的url,使用包含多个键值对的map传递键值对参数;当然这里可以使用@Field来完成传递,但是要写多个类似@Query
@GET
Call getMessage(@Url String url, @QueryMap Map params);
}
测试activity一个登陆界面:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private EditText user_number_et;
private EditText password_et;
private Button sure_btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
user_number_et = (EditText) findViewById(R.id.user_number_et);
password_et = (EditText) findViewById(R.id.password_et);
sure_btn = (Button) findViewById(R.id.sure_btn);
sure_btn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.sure_btn)
goLogin();
}
private void goLogin() {
String loginName = user_number_et.getText().toString();
String password = password_et.getText().toString();
Map params = new HashMap();
params.put("loginName", loginName);
params.put("password", password);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(UrlConfig.SZ_DEV_OUT)
.addConverterFactory(CustomGsonConverterFactory.create())
.build();
RetrofitHttpService service = retrofit.create(RetrofitHttpService.class);
Call call = service.login(UrlConfig.LOGIN(), params);
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
User user = response.body();
if (user != null){
Intent intent = new Intent(MainActivity.this,NoticeCenterActivity.class);
intent.putExtra("user", user);
MainActivity.this.startActivity(intent);
}
}
@Override
public void onFailure(Call call, Throwable t) {
Log.e("onFailure", t.toString());
}
});
}
}
界面的xml文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.gong.retrofittest.MainActivity">
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="textPersonName"
android:ems="10"
android:layout_centerHorizontal="true"
android:id="@+id/user_number_et" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:ems="10"
android:layout_below="@+id/user_number_et"
android:layout_marginTop="10dp"
android:layout_centerHorizontal="true"
android:id="@+id/password_et" />
<Button
android:text="确定"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/password_et"
android:layout_centerHorizontal="true"
android:layout_marginTop="59dp"
android:id="@+id/sure_btn" />
RelativeLayout>
转换器工厂类converterFactory和转换器converter
public class CustomGsonConverterFactory extends Converter.Factory {
private Gson gson;
private CustomGsonConverterFactory(Gson gson){
this.gson = gson;
};
public static CustomGsonConverterFactory create(){
return new CustomGsonConverterFactory(new Gson());
};
@Override
public Converter, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter> adapter = gson.getAdapter(TypeToken.get(type));
return new CustomGsonRequestConverter<>(gson, adapter);
}
@Override
public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
return new CustomResponseBodyConverter(gson);
}
}
/**
* Created by gxo on 2017/3/29.
* 请求数据转化工具类。
* 这里就是最原始的GsonConverterFactory中的那个RequestConverter
*/
class CustomGsonRequestConverter implements Converter{
private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
private static final Charset UTF_8 = Charset.forName("UTF-8");
private final Gson gson;
private final TypeAdapter adapter;
public CustomGsonRequestConverter(Gson gson, TypeAdapter 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());
}
}
class CustomResponseBodyConverter implements Converter{
private final Gson gson;
public CustomResponseBodyConverter(Gson gson) {
this.gson = gson;
}
@Override
public User convert(ResponseBody value) throws IOException {
try {
String rawStr = value.string();
JSONObject jsonObject = new JSONObject(rawStr);
JSONObject jsonObject2 = jsonObject.getJSONObject("error");
JSONObject jsonObject1 = jsonObject.getJSONObject("result");
User user = GsonUtil.GsonToBean(jsonObject1.toString(), User.class);//这里用了GsonUtil工具类和上面的GET请求中的一样,这里不重复粘贴了,
//同时User为我们自定义的类注意里面的变量和返回的json字段要相同,避免解析出错
return user;
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
}
通过代码可以看出其实也很简单,因为代码中有详细的注释所以这里不做赘述
很好理解就是将请求的时候发送的参数的方式改变一下如下:
将接口中的方法变为:
@POST //POST请求
Call login(@Url String url, @Body LoginUser user);
这里的LoginUser为实体类含有loginName,password
俩个参数,注意这里的俩字段要和后台的字段相同,然后将其实例化然后设置好俩个参数传入方法中即可
其他的写法和操作和传表单键值对的方式一样。
情景描述:先登录然后调到上传界面,这里就是将从系统中选取的文件上传到了服务器(这里是图片,别的文件也一样)并实现上传进度的监听;
这里的登录操作和2.3.1中的是一样的所以这里不重复粘贴登录和跳转传递参数的代码。
下面是上传文件涉及的代码:
接口:
public interface RetrofitHttpService {
@GET
@Streaming
Call downloadFile(@Url String url);
@POST
@Multipart
Call uploadFile(@Url String url,@Part("accessToken") RequestBody body, @Part MultipartBody.Part part);
//这里@MultiPart的意思就是允许多个@Part了,我们这里使用了2个@Part,第一个为简单的键值对,第二个我们准备上传个文件,使用了MultipartBody.Part类型。
@POST
@FormUrlEncoded
Call login(@Url String url, @FieldMap Map params);
@GET
Call getMessage(@Url String url, @QueryMap Map params);
}
上传的activity
public class UploadFileActivity extends AppCompatActivity {
private TextView fileNameTv;
private Button choiceBtn;
private ProgressBar uploadPb;
private User user;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_upload_file);
user = (User) getIntent().getSerializableExtra("user");
uploadPb = (ProgressBar) findViewById(R.id.upload_pd);
fileNameTv = (TextView) findViewById(R.id.filename_tv);
choiceBtn = (Button) findViewById(R.id.choice_tv);
choiceBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
/* 开启Pictures画面Type设定为image */
intent.setType("image/*");
/* 使用Intent.ACTION_GET_CONTENT这个Action */
intent.setAction(Intent.ACTION_GET_CONTENT);
UploadFileActivity.this.startActivityForResult(intent,1);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK){
switch (requestCode){
case 1:
try {
Uri uri = data.getData();
//获取文件的路径
String path = FileOpera.getImageAbsolutePath(this, uri);
if (path != null && !path.equals("")) {
fileNameTv.setText(path);
uploadFile(path);
}
} catch (Exception e) {
e.printStackTrace();
}
break;
}
}
}
private void uploadFile(String path) {
File file = new File(path);
String name = file.getName();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(UrlConfig.SZ_DEV_OUT)
.addConverterFactory(GsonConverterFactory.create())
.build();
//这里的ConverterFactory是没作用的,随意的写一个默认的GsonConverterFactory就好,做占位符用
RetrofitHttpService service = retrofit.create(RetrofitHttpService.class);
String aT = user.getToken();
RequestBody resquestBodyAt = RequestBody.create(null,aT);
RequestBody photoRequestBody = RequestBody.create(MediaType.parse("application/otcet-stream"), file);
//通过该行代码将RequestBody转换成特定的FileRequestBody
FileRequestBody body = new FileRequestBody(photoRequestBody, retrofitCallback);
MultipartBody.Part photo = MultipartBody.Part.createFormData("file", name, body);
Call call = service.uploadFile(UrlConfig.ATTACH_FILE_UPLOAD(), resquestBodyAt, photo);
call.enqueue(retrofitCallback);
}
private RetrofitCallback retrofitCallback = new RetrofitCallback() {
@Override
public void onSuccess(Call call, Response response) {
uploadPb.setVisibility(View.INVISIBLE);
}
@Override
public void onFailure(Call call, Throwable t) {
}
@Override
public void onLoading(long total, long progress) {
super.onLoading(total, progress);
uploadPb.setMax(100);
int nowPg = (int) ((progress/total)*100);
uploadPb.setProgress(nowPg);
}
};
}
为了实现上传进度而自己做的特殊的callback类
public abstract class RetrofitCallback<T> implements Callback<T> {
@Override
public void onResponse(Call call, Response response) {
if(response.isSuccessful()) {
onSuccess(call, response);
} else {
onFailure(call, new Throwable(response.message()));
}
}
public abstract void onSuccess(Call call, Response response);
public void onLoading(long total, long progress) {
}
}
/**
* Created by gxo on 2017/4/1.
* 扩展OkHttp的请求体,实现上传时的进度提示
*/
public class FileRequestBody<T> extends RequestBody{
/**
* 实际请求体
*/
private RequestBody requestBody;
/**
* 上传回调接口
*/
private RetrofitCallback callback;
/**
* 包装完成的BufferedSink
*/
private BufferedSink bufferedSink;
public FileRequestBody(RequestBody requestBody, RetrofitCallback callback) {
super();
this.requestBody = requestBody;
this.callback = callback;
}
@Override
public long contentLength() throws IOException {
return requestBody.contentLength();
}
@Override
public MediaType contentType() {
return requestBody.contentType();
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
if (bufferedSink == null) {
//包装
bufferedSink = Okio.buffer(sink(sink));
}
//写入
requestBody.writeTo(bufferedSink);
//必须调用flush,否则最后一部分数据可能不会被写入
bufferedSink.flush();
}
/**
* 写入,回调进度接口
* @param sink Sink
* @return Sink
*/
private Sink sink(Sink sink) {
return new ForwardingSink(sink) {
//当前写入字节数
long bytesWritten = 0L;
//总字节长度,避免多次调用contentLength()方法
long contentLength = 0L;
@Override
public void write(Buffer source, long byteCount) throws IOException {
super.write(source, byteCount);
if (contentLength == 0) {
//获得contentLength的值,后续不再调用
contentLength = contentLength();
}
//增加当前写入的字节数
bytesWritten += byteCount;
//回调
callback.onLoading(contentLength, bytesWritten);
}
};
}
}
布局文件:
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_upload_file"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.gong.retrofittest.UploadFileActivity">
"文件名称"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="92dp"
android:id="@+id/filename_tv" />
public interface RetrofitHttpService {
@GET
@Streaming //流的注释
Call downloadFile(@Url String url);//完整的url
@POST
@Multipart
Call uploadFile(@Url String url,@Part("accessToken") RequestBody body, @Part MultipartBody.Part part);
@POST
@FormUrlEncoded
Call login(@Url String url, @FieldMap Map params);
@GET
Call getMessage(@Url String url, @QueryMap Map params);
}
public class DownloadFileActivity extends AppCompatActivity {
private Button downloadBtn;
private ProgressBar showPb;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_download_file);
downloadBtn = (Button) findViewById(R.id.download_btn);
showPb = (ProgressBar) findViewById(R.id.show_pb);
downloadBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
downloadFile();
}
});
}
private void downloadFile() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://sw.bos.baidu.com")
.addConverterFactory(ResponseBodyConverterFactory.create())
.build();
RetrofitHttpService service = retrofit.create(RetrofitHttpService.class);
Call call = service.downloadFile("http://sw.bos.baidu.com/sw-search-sp/software/c07cde08ce4/Photoshop_CS6.exe");
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
final ResponseBody body = response.body();
Log.e("ResponseBody",body.toString());
new Thread(new Runnable() {
@Override
public void run() {
String path = createSavePath();
WriteFileUtil.writeFile(body,path,callback);
}
}).start();
}
@Override
public void onFailure(Call call, Throwable t) {
Log.e("Throwable",t.toString());
}
});
}
private String createSavePath() {
return Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "PS.exe";
}
private DownloadCallback callback = new DownloadCallback() {
@Override
public void onSuccess(String savePath) {
Log.e("savePath",savePath);
}
@Override
public void onError(Throwable error) {
}
@Override
public void onProgress(float progress) {
Log.e("progress",""+progress);
showPb.setMax(100);
showPb.setProgress((int)progress);
}
};
}
下载后写入文件的操作类
public class WriteFileUtil {
public static Handler mHandler = new Handler(Looper.getMainLooper());
public static void writeFile(ResponseBody body, String path, final DownloadCallback callback) {
File futureStudioIconFile = new File(path);
try {
if (!futureStudioIconFile.exists()) {
futureStudioIconFile.createNewFile();
}
} catch (IOException e) {
e.printStackTrace();
}
InputStream inputStream = null;
OutputStream outputStream = null;
final ProgressInfo progressInfo = new ProgressInfo();
try {
byte[] fileReader = new byte[4096];
progressInfo.total = body.contentLength();
progressInfo.read = 0;
inputStream = body.byteStream();
outputStream = new FileOutputStream(futureStudioIconFile);
while (true) {
int read = inputStream.read(fileReader);
if (read == -1) {
break;
}
outputStream.write(fileReader, 0, read);
progressInfo.read += read;
mHandler.post(new Runnable() {
@Override
public void run() {
callback.onProgress(((float) progressInfo.read / progressInfo.total) * 100);
}
});
}
callback.onSuccess(path);
outputStream.flush();
} catch (IOException e) {
callback.onError(e);
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
callback.onError(e);
}
}
}
static class ProgressInfo {
public long read = 0;
public long total = 0;
}
}
/**
* Created by gxo on 2017/4/1.
*/
public interface DownloadCallback {
void onSuccess(String savePath);
void onError(Throwable error);
void onProgress(float progress);
}
基本的用法就到这里了,简单总结一下
@GET;@POST最为常用同时配合上@Multipart或者@Streaming或者@Body等来实现参数或者文件的上传,下载
很灵活有最简单的@Query,@QueryMap;@Field,@FieldMap来实现GET和POST携带参数的请求也有通过@Body来实现较复杂的文件的上传
RequestConverter和ResponseConverter的定制实现,在上面的例子中已经写过。
说明:我们这里的源码解析只讲流程不去硬扣每个细节,因为这样足够让你领略他的思想。至于想要模仿着写出这样的代码那只能去模仿着写了需要自己的处处留意和不断改进自己的代码风格和写法。
既然是流程式的分析就从最外面提供给的api来层层深入,最终解决我中的疑问:
a.通过注释接口中的方法和数据是怎样运行和被使用的
b.转化器工厂类和转化器(Request和response)如何工作的如何被调用的
c.数据是如何被返回并回调的
我们以2.3中登录接口为例子分析:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(UrlConfig.SZ_DEV_OUT)
.addConverterFactory(CustomGsonConverterFactory.create())
.build();
RetrofitHttpService service = retrofit.create(RetrofitHttpService.class);
Call<User> call = service.login(UrlConfig.LOGIN(), params);
call.enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
User user = response.body();
}
@Override
public void onFailure(Call<User> call, Throwable t) {
}
});
主要做了如下几件事情:
1.首先看到通过Retrofit内部的建造类来将各个参数(baseUrl,转化器工厂类等)传入,最后再build()产生Retrofit的实例
2.通过retrofit.create(RetrofitHttpService.class)产生了接口的实例
3.通过调用相应的方法产生了调度的任务队列call
4.队列任务开始执行传入回调接口完成回调
先看源码如下:
public static final class Builder {
private final Platform platform;
private okhttp3.Call.Factory callFactory;
private HttpUrl baseUrl;
private final List converterFactories = new ArrayList<>();
private final List adapterFactories = new ArrayList<>();
private Executor callbackExecutor;
private boolean validateEagerly;
Builder(Platform platform) {
this.platform = platform;
converterFactories.add(new BuiltInConverters());
}
public Builder() {
this(Platform.get());
}
Builder(Retrofit retrofit) {
platform = Platform.get();
callFactory = retrofit.callFactory;
baseUrl = retrofit.baseUrl;
converterFactories.addAll(retrofit.converterFactories);
adapterFactories.addAll(retrofit.adapterFactories);
// Remove the default, platform-aware call adapter added by build().
adapterFactories.remove(adapterFactories.size() - 1);
callbackExecutor = retrofit.callbackExecutor;
validateEagerly = retrofit.validateEagerly;
}
public Builder client(OkHttpClient client) {
return callFactory(checkNotNull(client, "client == null"));
}
public Builder callFactory(okhttp3.Call.Factory factory) {
this.callFactory = checkNotNull(factory, "factory == null");
return this;
}
public Builder baseUrl(String baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
HttpUrl httpUrl = HttpUrl.parse(baseUrl);
if (httpUrl == null) {
throw new IllegalArgumentException("Illegal URL: " + baseUrl);
}
return baseUrl(httpUrl);
}
public Builder baseUrl(HttpUrl baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
List pathSegments = baseUrl.pathSegments();
if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
}
this.baseUrl = baseUrl;
return this;
}
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
adapterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
public Builder callbackExecutor(Executor executor) {
this.callbackExecutor = checkNotNull(executor, "executor == null");
return this;
}
public Builder validateEagerly(boolean validateEagerly) {
this.validateEagerly = validateEagerly;
return this;
}
public Retrofit build() {
//可知这个参数为必传
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
//可知callFactory为非必传
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
//可知callbackExecutor为非必传
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the default Call adapter.
List adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
}
分析:
步骤1建造者模式产生Retrofit实例,流程如下:
new Retrofit.Builder():
实例化建造器类,完成平台的确定这里是Android,同时将并且把内置的转换器工厂(BuiltInConverters)加添到工厂集合中,它的主要作用就是当使用多种Converters的时候能够正确的引导并找到可以消耗该类型的转化器。
baseUrl():
加入参数baseURl,这里注意判断了非空,可知这个参数为必传
addConverterFactory(Converter.Factory factory)
将转换器工厂传入,为请求和返回数据的转化做准备
client(OkHttpClient client):
由此可知我们可以自行配置okhttpClient,这里可以扩展的就很多了比如使用https,log等等。熟悉okhttp的肯定能脑补出。不熟悉的也没问题,在用到具体的问题时候再进行扩展也是没问题的。
addCallAdapterFactory():
该方法主要是针对Call转换了,比如对Rxjava的支持,从返回的call对象转化为Observable对象,其实不用管它。
callbackExecutor(Executor executor):
该方法从名字上看可以得知应该是回调执行者,也就是Call对象从网络服务获取数据之后转换到UI主线程中,(熟悉okhttp的知道它的回调也是要同样的一个操作的从子线程到主线程的一个转换)。这个想一想大概是用来将回调传递到UI线程了,当然这里设计的比较巧妙,利用platform对象,对平台进行判断,判断主要是利用Class.forName(“”)进行查找,
具体代码已经被放到文末,如果是Android平台,会自定义一个Executor对象,并且利用Looper.getMainLooper()实例化一个handler对象,
在Executor内部通过handler.post(runnable),具体的代码如下:
static class Android extends Platform {
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
@Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
return new ExecutorCallAdapterFactory(callbackExecutor);
}
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
}
最后再build,建造一个包含了各种参数的Retrofit实例。
public T create(final Class service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
//判断是不是Object中的方法,是就按照其调用
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
//下面三行是关键
ServiceMethod
先看下可知其中最重要的是动态代理,Java中已经提供了非常简单的API帮助我们来实现动态代理。
现在我们自己来实现一个,如下:
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textProxy();
}
private void textProxy() {
TestIf testif = (TestIf) Proxy.newProxyInstance(TestIf.class.getClassLoader(), new Class[]{TestIf.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
Log.e("MethodName",name);
String a = (String) args[0];
String b = (String) args[1];
Log.e("a",a);
Log.e("b",b);
GET get = method.getAnnotation(GET.class);
String annotation = get.value();
Log.e("annotation",annotation);
return null;
}
});
testif.test("测试a","测试b");
}
public interface TestIf{
@GET("/gong/name")
void test(String a, String b);
}
}
运行的Log如下:
04-06 14:23:21.485 9767-9767/com.gong.retrofittest E/MethodName: test
04-06 14:23:21.485 9767-9767/com.gong.retrofittest E/a: 测试a
04-06 14:23:21.485 9767-9767/com.gong.retrofittest E/b: 测试b
04-06 14:23:21.486 9767-9767/com.gong.retrofittest E/annotation: /gong/name
可以看到我们通过Proxy.newProxyInstance产生的代理类,当调用接口的任何方法时,都会调用InvocationHandler#invoke方法,在这个方法中可以拿到传入的参数,注解等。retrofit也是在invoke方法里面,拿到所有的参数,注解信息然后就可以去构造RequestBody,再去构建Request,得到Call对象封装后返回。
我们从Retrofit的create中也看到了其实现的思路也是动态代理,只不过他会对要得到的方法,注释等等进行进一步的封装。
比较重要的有三行分别来分析:
ServiceMethod
ServiceMethod, ?> loadServiceMethod(Method method) {
ServiceMethod, ?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder<>(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
ServiceMethod中的构建:
public ServiceMethod build() {
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
if (responseType == Response.class || responseType == okhttp3.Response.class) {
throw methodError("'"
+ Utils.getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
responseConverter = createResponseConverter();
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
if (httpMethod == null) {
throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}
if (!hasBody) {
if (isMultipart) {
throw methodError(
"Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
}
if (isFormEncoded) {
throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
+ "request body (e.g., @POST).");
}
}
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
Type parameterType = parameterTypes[p];
if (Utils.hasUnresolvableType(parameterType)) {
throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
parameterType);
}
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
if (parameterAnnotations == null) {
throw parameterError(p, "No Retrofit annotation found.");
}
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
if (relativeUrl == null && !gotUrl) {
throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
}
if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
throw methodError("Non-body HTTP method cannot contain @Body.");
}
if (isFormEncoded && !gotField) {
throw methodError("Form-encoded method must contain at least one @Field.");
}
if (isMultipart && !gotPart) {
throw methodError("Multipart method must contain at least one @Part.");
}
return new ServiceMethod<>(this);
}
private CallAdapter createCallAdapter() {
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(
"Method return type must not include a type variable or wildcard: %s", returnType);
}
if (returnType == void.class) {
throw methodError("Service methods cannot return void.");
}
Annotation[] annotations = method.getAnnotations();
try {
//noinspection unchecked
return (CallAdapter) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(e, "Unable to create call adapter for %s", returnType);
}
}
直接看build方法,首先拿到这个callAdapter最终拿到的是我们在构建retrofit里面时adapterFactories时添加的,即为:new ExecutorCallbackCall<>(callbackExecutor, call),该ExecutorCallbackCall唯一做的事情就是将原本call的回调转发至UI线程。
接下来通过callAdapter.responseType()返回的是我们方法的实际类型,例如:Call,则返回User类型,然后对该类型进行判断。不能是Response.class 或okhttp3.Response.class不然会抛出异常。
接下来是createResponseConverter拿到responseConverter对象,其当然也是根据我们构建retrofit时,addConverterFactory添加的
ConverterFactory对象来寻找一个合适的返回,寻找的依据主要看该converter能否处理你编写方法的返回值类型,默认实现为BuiltInConverters,仅仅支持返回值的实际类型为ResponseBody和Void,也就说明了默认情况下,是不支持Call这类类型的。
接下来就是对注解进行解析了,主要是对方法上的注解进行解析,那么可以拿到httpMethod以及初步的url(包含占位符)。
后面是对方法中参数中的注解进行解析,这一步会拿到很多的ParameterHandler对象,该对象在toRequest()构造Request的时候调用
其apply方法。
没有逐行的分析因为意义不大,重要的是理解流程和思想,只要知道ServiceMethod主要用于将我们接口中的方法转化为一个Request对象,于是根据我们的接口返回值确定了responseConverter,解析我们方法上的注解拿到初步的url,解析我们参数上的注解拿到构建RequestBody所需的各种信息,最终调用toRequest的方法完成Request的构建。
第二行OkHttpCall okHttpCall的实例化
代码如下只是赋值操作:
OkHttpCall(ServiceMethod serviceMethod, Object[] args) {
this.serviceMethod = serviceMethod;
this.args = args;
}
第三行return serviceMethod.callAdapter.adapt(okHttpCall);我们已经确定这个callAdapter是ExecutorCallAdapterFactory.get()对应代码为:
final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
final Executor callbackExecutor;
ExecutorCallAdapterFactory(Executor callbackExecutor) {
this.callbackExecutor = callbackExecutor;
}
@Override
public CallAdapter, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
final Type responseType = Utils.getCallResponseType(returnType);
return new CallAdapter>>() {
@Override public Type responseType() {
return responseType;
}
@Override public Call adapt(Call call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
};
}
可以看到adapt返回的是ExecutorCallbackCall对象
static final class ExecutorCallbackCall implements Call {
final Executor callbackExecutor;
final Call delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override public void enqueue(final Callback callback) {
if (callback == null) throw new NullPointerException("callback == null");
delegate.enqueue(new Callback() {
@Override public void onResponse(Call call, final Response response) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
if (delegate.isCanceled()) {
// Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallbackCall.this, response);
}
}
});
}
@Override public void onFailure(Call call, final Throwable t) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
callback.onFailure(ExecutorCallbackCall.this, t);
}
});
}
});
}
}
}
可以看出ExecutorCallbackCall仅仅是对Call对象进行封装,类似装饰者模式,只不过将其执行时的回调通过callbackExecutor进行回调到UI线程中去了。在执行enqueue的时候做了真正的操作,并进行了回调直到Ui线程中。
在3.2.2我们已经拿到了经过封装的ExecutorCallbackCall类型的call对象,实际上就是我们实际在写代码时拿到的call对象,那么我们一般会执行enqueue方法,看看源码是怎么做的,首先是ExecutorCallbackCall.enqueue方法,代码在3.2.2,可以看到除了将onResponse和onFailure回调到UI线程,主要的操作还是delegate完成的,这个delegate实际上就是OkHttpCall对象,我们看它的enqueue方法:
final class OkHttpCall implements Call {
OkHttpCall(ServiceMethod serviceMethod, Object[] args) {
this.serviceMethod = serviceMethod;
this.args = args;
}
@Override public void enqueue(final Callback callback) {
if (callback == null) throw new NullPointerException("callback == null");
okhttp3.Call call;
Throwable failure;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
call = rawCall = createRawCall();
} catch (Throwable t) {
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
throws IOException {
Response response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
callSuccess(response);
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callSuccess(Response response) {
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
}
从中我们得出并没有什么太复杂的东西,调用的就是okhttp中的东西补充一点call的创建:
private okhttp3.Call createRawCall() throws IOException {
Request request = serviceMethod.toRequest(args);
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
可以看到,通过serviceMethod.toRequest完成对request的构建,通过request去构造call对象,然后返回.
下面是请求成功之后对返回的数据的解析,这里我们可以看到我们之前传入的转化器工厂有了作用,将数据转化为我们想要的数据
Response parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
// Remove the body's source (the only stateful object) so we can pass the response along.
rawResponse = rawResponse.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
int code = rawResponse.code();
if (code < 200 || code >= 300) {
try {
// Buffer the entire body to avoid future I/O.
ResponseBody bufferedBody = Utils.buffer(rawBody);
return Response.error(bufferedBody, rawResponse);
} finally {
rawBody.close();
}
}
if (code == 204 || code == 205) {
rawBody.close();
return Response.success(null, rawResponse);
}
//转化为我们想要的回调数据
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
T body = serviceMethod.toResponse(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
// If the underlying source threw an exception, propagate that rather than indicating it was
// a runtime exception.
catchingBody.throwIfCaught();
throw e;
}
}
关键在于 serviceMethod.toResponse(catchingBody);
进入serviceMethod中我们看看如下:
/** Builds a method return value from an HTTP response body. */
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
responseConverter就是我们转化器工厂类中返回的ResponseConverter。
首先构造retrofit,几个核心的参数呢,主要就是baseurl,callFactory(默认okhttpclient),converterFactories,adapterFactories,excallbackExecutor。然后通过create方法拿到接口的实现类,这里利用Java的Proxy类完成动态代理的相关代理在invoke方法内部,拿到我们所声明的注解以及实参等,构造ServiceMethod,ServiceMethod中解析了大量的信息,最痛可以通过toRequest构造出okhttp3.Request对象。有了okhttp3.Request对象就可以很自然的构建出okhttp3.call,最后calladapter对Call进行装饰返回。拿到Call就可以执行enqueue或者execute方法了
我们先看一下源码:
public interface Converter {
T convert(F value) throws IOException;
abstract class Factory {
public Converter responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return null;
}
public Converter, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return null;
}
public Converter, String> stringConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return null;
}
}
}
发现这个工厂类是个abstract型的但是里面的方法却是可以选择实现的,平时我们重写的就是上面的两个(一个是将我们的请求的数据转化为
RequestBody一个是将返回的数据ResponseBody转化为我们想要的数据)。
在上面的使用过程中已经有很多的自定义的转化器工厂类了,同时伴随着的是转化器类,具体的实现在上面已经给出啊了。
这里提醒大家基本我们的RequestConverter就直接使用GsonConverterFactory中的GsonRequestBodyConverter,对于返回的数据我们还是要定制一下的但是我个人比较偏好于将原始的字符串返回,并自己做解析,再在外面封装一层回调。对于不是json字符串的如文件下载的情况就只能在converter中处理了。
因为Retrofit算是框架,具体的很多的功能还是要通过配置okhttp来完成的,比如log拦截器,缓存,超时重连,设置公共参数,cookie,请求头等。因为这一部分应该算是okhttp的东西,所以这里就不写了,好多的功能都要通过okhttp来配置。了解okhttp的人就很好接受如果没了解过推荐看看一下的文章:
http://blog.csdn.net/lmj623565791/article/details/49734867/鸿洋的,并且还封装了一个自己的okhttp工具。