Retrofit的使用和源码解析
来源:互联网 发布:双敏网络精灵 编辑:程序博客网 时间:2024/05/16 02:19
一.Retrofit的概况
本文写作的目的是为自己以后温故知新而留的笔记,文章中所有的代码均是我逐行测试过的,读者可以将代码直接copy到自己的项目中直接使用。
1.1概述
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的面纱,由浅极深。没什么难理解的东西,对于初期接触的你和使用了但是不了解内部精髓的你相信会有一些帮助。
1.2配置使用环境
下面都是在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
1.3本文提纲
先来理清文章的脉络:
a.GET请求
b.POST请求
c.文件的上传(单文件,监听上传的进度功能)
d.文件的下载(下载进度监听功能实现)
e.自定义ConverterFactory,自定义Converter
f.最后是源码的分析
1.4特别说明
1.4.1由于文章中使用的是公司的正式接口所以在文章正式发布的时候会将接口去掉(但是接口的作用我会说清楚),但是文中的代码都是作者亲自调试并能正常运行的。如果第一次使用为了快速集成可以直接将代码拷贝过去,并将自己想要定制的部分进行细致的封装。
1.4.2文章中MainActivity是个登录界面使用的是post请求,因为接下来的几个接口可能会用到登录成功之后返回的token(登录有效的标示)所以这这个页面将登录设为首页。同时token也说明从这里来传到不同的页面,提供使用。
二.基本用法
2.1GET请求
retrofit在使用的过程中,需要定义一个接口对象,我们首先演示一个最简单的get请求,代码如下所示:
//注释接口public interface RetrofitHttpService { @GET//个人喜欢不将baseUrl后面的部分放在注解方法后面作为值,所以我这里只是写方法名,当然要在方法中加上@Url String url Call<String> getMessage(@Url String url, @QueryMap Map<String,String> params); //定义了回调参数的类型为String //方法的名称getMessage,参数为拼接完好的url和?后面的参数(放在集合map中并用@QueryMap进行注解) //个人比较懒其实还可以将参数一个个传入并使用@Query作为注解,那么就会写好几个@Query String param作为参数并用“,”(逗号)隔开,那么在增加和删除参数的时候就会要更改接口带来麻烦}//具体演示功能Activitypublic 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<String,String> params = new HashMap<String, String>(); 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<String> call = service.getMessage(“你自己拼接好的完整的Url”, params);//生成请求队列并加入 call.enqueue(new Callback<String>() {//执行请求操作 @Override public void onResponse(Call<String> call, Response<String> 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<NoticeMessage> 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<String> 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<ResponseBody, ?> 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<T> adapter; public CustomGsonRequestConverter(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()); }}/** * 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> T GsonToBean(String gsonString, Class<T> cls) { T t = null; if (gson != null) { t = gson.fromJson(gsonString, cls); } return t; } /** * json转成list * 解决泛型问题 */ public static <T> List<T> jsonToList(String json, Class<T> cls) { List<T> list = new ArrayList<T>(); 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 <T> List<Map<String, T>> GsonToListMaps(String gsonString) { List<Map<String, T>> list = null; if (gson != null) { list = gson.fromJson(gsonString, new TypeToken<List<Map<String, T>>>() { }.getType()); } return list; } /** * json转成map的 */ public static <T> Map<String, T> GsonToMaps(String gsonString) { Map<String, T> map = null; if (gson != null) { map = gson.fromJson(gsonString, new TypeToken<Map<String, T>>() { }.getType()); } return map; }}
说明:
上来就搞得好像很复杂但是很简单,
1.首先自己写个接口,注明什么请求,方法中注明要请求的url,参数
2.Retrofit实例的构造
3.获得接口实例并获得请求队列将请求任务加入其中
4.执行请求,并完成异步回调
上面的代码中所用的注意点和各个类,方法的作用已经写的很清楚了,仔细看很容易。不会的先只记住功能下面都会详细分析。
2.2api概况
相信大家对Retrofit有了些许的了解,那么接下来我们先来总体罗列一下Retrofit中所有的功能和注意点
常用的请求类型
@GET,@POST,@PUT,@DELETE
但是我相信绝大部分时候只要@GET,@POST足以
addConverterFactory中转化器工厂类(很简单,现在你不用完全了解,后面有详细分析,并让你有能力自己写一个)这里可以自定义属于自己的工厂类,实现高度的定制,利于扩展。但是我个人认为不需要过多的写这个类,而是应该回调原始的json字符串,最后再封装一层,获得最终想要的实体类。(这里只是考虑返回普通json数据的情况,如果是下载文件等特殊情况直接做io操作并返回自己想要的信息即可)
2.3POST请求
2.3.1表单的方式传递键值对@FormUrlEncoded
先看代码:交代一下情景,简单的一个登陆界面
接口如下,有注释:
public interface RetrofitHttpService { @GET @Streaming Call<ResponseBody> downloadFile(@Url String url); @POST @Multipart Call<String> uploadFile(@Url String url,@Part("accessToken") RequestBody body, @Part MultipartBody.Part part); //使用表单键值对的方式post @POST //POST请求 @FormUrlEncoded //使用表单的方式 Call<User> login(@Url String url, @FieldMap Map<String, String> params); //注意这里将回调的参数类型设置为了User类,就是说我们在后面要自定义一个转化器工厂类,具体见下面代码。 //完整的url,使用包含多个键值对的map传递键值对参数;当然这里可以使用@Field来完成传递,但是要写多个类似@Query @GET Call<String> getMessage(@Url String url, @QueryMap Map<String,String> 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<String, String> params = new HashMap<String, String>(); 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<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(); if (user != null){ Intent intent = new Intent(MainActivity.this,NoticeCenterActivity.class); intent.putExtra("user", user); MainActivity.this.startActivity(intent); } } @Override public void onFailure(Call<User> call, Throwable t) { Log.e("onFailure", t.toString()); } }); }}
界面的xml文件
<?xml version="1.0" encoding="utf-8"?><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<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { return new CustomResponseBodyConverter(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<T> adapter; public CustomGsonRequestConverter(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()); }} class CustomResponseBodyConverter implements Converter<ResponseBody,User>{ 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; }}
通过代码可以看出其实也很简单,因为代码中有详细的注释所以这里不做赘述
2.3.2POST请求体的方式向服务器传入json字符串@Body
很好理解就是将请求的时候发送的参数的方式改变一下如下:
将接口中的方法变为:
@POST //POST请求
Call login(@Url String url, @Body LoginUser user);
这里的LoginUser为实体类含有loginName,password
俩个参数,注意这里的俩字段要和后台的字段相同,然后将其实例化然后设置好俩个参数传入方法中即可
其他的写法和操作和传表单键值对的方式一样。
2.4文件的上传@Multipart(篇幅问题只写单文件上传,多文件也很简单百度就ok了)
情景描述:先登录然后调到上传界面,这里就是将从系统中选取的文件上传到了服务器(这里是图片,别的文件也一样)并实现上传进度的监听;
这里的登录操作和2.3.1中的是一样的所以这里不重复粘贴登录和跳转传递参数的代码。
下面是上传文件涉及的代码:
接口:
public interface RetrofitHttpService { @GET @Streaming Call<ResponseBody> downloadFile(@Url String url); @POST @Multipart Call<String> uploadFile(@Url String url,@Part("accessToken") RequestBody body, @Part MultipartBody.Part part); //这里@MultiPart的意思就是允许多个@Part了,我们这里使用了2个@Part,第一个为简单的键值对,第二个我们准备上传个文件,使用了MultipartBody.Part类型。 @POST @FormUrlEncoded Call<User> login(@Url String url, @FieldMap Map<String, String> params); @GET Call<String> getMessage(@Url String url, @QueryMap Map<String,String> params);}
上传的activitypublic 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<String> call = service.uploadFile(UrlConfig.ATTACH_FILE_UPLOAD(), resquestBodyAt, photo); call.enqueue(retrofitCallback); } private RetrofitCallback retrofitCallback = new RetrofitCallback<String>() { @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<T> call, Response<T> 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<T> callback; /** * 包装完成的BufferedSink */ private BufferedSink bufferedSink; public FileRequestBody(RequestBody requestBody, RetrofitCallback<T> 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); } }; }}布局文件:<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="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"> <TextView android:text="文件名称" 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" /> <Button android:text="选择文件" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/filename_tv" android:layout_centerHorizontal="true" android:layout_marginTop="73dp" android:id="@+id/choice_tv" /> <ProgressBar style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="10dp" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="50dp" android:id="@+id/upload_pd" /></RelativeLayout>
2.5文件下载带进度的监听的(这里演示用的是一个ps软件的下载)
public interface RetrofitHttpService { @GET @Streaming //流的注释 Call<ResponseBody> downloadFile(@Url String url);//完整的url @POST @Multipart Call<String> uploadFile(@Url String url,@Part("accessToken") RequestBody body, @Part MultipartBody.Part part); @POST @FormUrlEncoded Call<User> login(@Url String url, @FieldMap Map<String, String> params); @GET Call<String> getMessage(@Url String url, @QueryMap Map<String,String> 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<ResponseBody> call = service.downloadFile("http://sw.bos.baidu.com/sw-search-sp/software/c07cde08ce4/Photoshop_CS6.exe"); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> 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<ResponseBody> 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);}
2.6总结
基本的用法就到这里了,简单总结一下
2.6.1对方法的注释
@GET;@POST最为常用同时配合上@Multipart或者@Streaming或者@Body等来实现参数或者文件的上传,下载
2.6.2方法中参数的注释
很灵活有最简单的@Query,@QueryMap;@Field,@FieldMap来实现GET和POST携带参数的请求也有通过@Body来实现较复杂的文件的上传
2.6.3ConverterFactory的定制化实现
RequestConverter和ResponseConverter的定制实现,在上面的例子中已经写过。
三.源码解析
说明:我们这里的源码解析只讲流程不去硬扣每个细节,因为这样足够让你领略他的思想。至于想要模仿着写出这样的代码那只能去模仿着写了需要自己的处处留意和不断改进自己的代码风格和写法。
3.1流程分析
既然是流程式的分析就从最外面提供给的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.队列任务开始执行传入回调接口完成回调
3.2具体的分析
3.2.1Retrofit中的Builder建造器主要是build方法
先看源码如下:
public static final class Builder { private final Platform platform; private okhttp3.Call.Factory callFactory; private HttpUrl baseUrl; private final List<Converter.Factory> converterFactories = new ArrayList<>(); private final List<CallAdapter.Factory> 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<String> 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<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories); adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); // Make a defensive copy of the converters. List<Converter.Factory> 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实例。
3.2.2 通过create方法产生了接口的实例,我们来看一下:
public <T> T create(final Class<T> 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<Object, Object> serviceMethod =(ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.callAdapter.adapt(okHttpCall); } });}
先看下可知其中最重要的是动态代理,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: test04-06 14:23:21.485 9767-9767/com.gong.retrofittest E/a: 测试a04-06 14:23:21.485 9767-9767/com.gong.retrofittest E/b: 测试b04-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<T, R> 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<T, R>) 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<T, ?> 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<Object, Call<?>>() { @Override public Type responseType() { return responseType; } @Override public Call<Object> adapt(Call<Object> call) { return new ExecutorCallbackCall<>(callbackExecutor, call); } }; }
可以看到adapt返回的是ExecutorCallbackCall对象
static final class ExecutorCallbackCall<T> implements Call<T> { final Executor callbackExecutor; final Call<T> delegate; ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) { this.callbackExecutor = callbackExecutor; this.delegate = delegate; } @Override public void enqueue(final Callback<T> callback) { if (callback == null) throw new NullPointerException("callback == null"); delegate.enqueue(new Callback<T>() { @Override public void onResponse(Call<T> call, final Response<T> 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<T> 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.3真正的执行者OkHttpCall
在3.2.2我们已经拿到了经过封装的ExecutorCallbackCall类型的call对象,实际上就是我们实际在写代码时拿到的call对象,那么我们一般会执行enqueue方法,看看源码是怎么做的,首先是ExecutorCallbackCall.enqueue方法,代码在3.2.2,可以看到除了将onResponse和onFailure回调到UI线程,主要的操作还是delegate完成的,这个delegate实际上就是OkHttpCall对象,我们看它的enqueue方法:
final class OkHttpCall<T> implements Call<T> { OkHttpCall(ServiceMethod<T, ?> serviceMethod, Object[] args) { this.serviceMethod = serviceMethod; this.args = args; } @Override public void enqueue(final Callback<T> 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<T> 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<T> 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<T> 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。
3.2.4总结
首先构造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方法了
四 其他
4.1 自定义Converter.Factory
我们先看一下源码:
public interface Converter<F, T> { T convert(F value) throws IOException; abstract class Factory { public Converter<ResponseBody, ?> 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中处理了。
4.2 关于okhttpClient的配置
因为Retrofit算是框架,具体的很多的功能还是要通过配置okhttp来完成的,比如log拦截器,缓存,超时重连,设置公共参数,cookie,请求头等。因为这一部分应该算是okhttp的东西,所以这里就不写了,好多的功能都要通过okhttp来配置。了解okhttp的人就很好接受如果没了解过推荐看看一下的文章:
http://blog.csdn.net/lmj623565791/article/details/49734867/鸿洋的,并且还封装了一个自己的okhttp工具。
- Retrofit的使用和源码解析
- 【Retrofit】Retrofit源码解析
- Retrofit源码解析---addConverterFactory和addCallAdapterFactory区别
- Android 网络框架Retrofit的使用和解析
- Retrofit源码解析
- Retrofit源码解析
- Retrofit源码解析
- Retrofit源码解析
- Retrofit源码解析
- Retrofit源码解析
- Android Retrofit源码解析
- Retrofit 源码解析
- Retrofit源码解析
- Retrofit源码解析:RxJavaCallAdapterFactory
- Retrofit源码解析---初始化
- Retrofit 源码解析
- Retrofit源码解析
- Retrofit源码解析
- Redux-form嵌套到实际项目开发案例八
- 解决 No resource found that matches the given name (at 'layout_above' with value '@id/button3').
- MaterialProgressDrawable v4包下圆形进度条
- 实现多个控件之间画线连接并自由移动(线随控件动)
- 前端面试题——JS合集
- Retrofit的使用和源码解析
- Map按照值降序排列
- visual studio属性管理器(property manager)上各项的含义
- Android intent 有关flag标志的理解
- php导出txt文件
- JS中变量声明及作用域详解
- 相对布局设置位置
- Android打开相机
- 工具类,操作字符串、日期、图像、IO等