Retrofit2 笔记

来源:互联网 发布:mmd动作数据怎么做 编辑:程序博客网 时间:2024/06/05 08:01

Retrofit是针对于Android/Java的、基于okHttp的、一种轻量级且安全的、并使用注解方式的网络请求框架。

Retrofit的设计非常插件化而且轻量级,真的是非常高内聚而且低耦合

Retrofit的优势

首先,Retrofit使用注解方式,大大简化了我们的URL拼写形式,而且注解含义一目了然,简单易懂;
其次,Retrofit使用简单,结构层次分明,每一步都能清晰的表达出之所以要使用的寓意;
再者,Retrofit支持同步和异步执行,使得请求变得异常简单,只要调用enqueue/execute即可完成;
最后,Retrofit更大自由度的支持我们自定义的业务逻辑,如自定义Converters。

从 Retrofit 的创建方法可以看出,使用的是 Builder 模式

Retrofit 中有如下的几个关键变量:(取自:https://segmentfault.com/a/1190000006767113)

//用于缓存解析出来的方法  private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();  //请求网络的OKHttp的工厂,默认是 OkHttpClient  private final okhttp3.Call.Factory callFactory;  //baseurl  private final HttpUrl baseUrl;  //请求网络得到的response的转换器的集合 默认会加入 BuiltInConverters       private final List<Converter.Factory> converterFactories;  //把Call对象转换成其它类型  private final List<CallAdapter.Factory> adapterFactories;  //用于执行回调 Android中默认是 MainThreadExecutor  private final Executor callbackExecutor;  //是否需要立即解析接口中的方法  private final boolean validateEagerly;

再看一下Retrofit 中的内部类 Builder 的 builder 方法:

public Retrofit build() {  if (baseUrl == null) {    throw new IllegalStateException("Base URL required.");  }  okhttp3.Call.Factory callFactory = this.callFactory;  if (callFactory == null) {    //默认创建一个 OkHttpClient    callFactory = new OkHttpClient();  }  Executor callbackExecutor = this.callbackExecutor;  if (callbackExecutor == null) {     //Android 中返回的是 MainThreadExecutor    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);}

loadServiceMethod: 先到缓存中找,缓存中没有再去创建。这里创建了 ServiceMethod 对象。ServiceMethod 用于把接口方法的调用转换成一个 HTTP 请求。其实,在 ServiceMethod 中,会解析接口中方法的注解、参数等,它还有个 toRequest 方法,用于生成一个 Request 对象。这个 Request 对象就是 OkHttp 中的 Request,代表了一条网络请求(Retrofit 实际上把真正请求网络的操作交给了 OkHttp 执行)。

ServiceMethod loadServiceMethod(Method method) {    ServiceMethod result;    synchronized (serviceMethodCache) {      result = serviceMethodCache.get(method);      if (result == null) {        result = new ServiceMethod.Builder(this, method).build();        serviceMethodCache.put(method, result);      }    }    return result;}

OkHttpCall继承于 Call 接口。Call 是Retrofit 的基础接口,代表发送网络请求与响应调用,它包含下面几个接口方法:

Response<T> execute() throws IOException; //同步执行请求void enqueue(Callback<T> callback); //异步执行请求,callback 用于回调boolean isExecuted(); //是否执行过void cancel(); //取消请求boolean isCanceled(); //是否取消了Call<T> clone(); //克隆一条请求Request request(); //获取原始的request

OkHttpCall 是 Call 的一个实现类,它里面封装了 OkHttp 中的原生 Call,在这个类里面实现了 execute 以及 enqueue 等方法,其实是调用了 OkHttp 中原生 Call 的对应方法。

接下来把 OkHttpCall 传给 serviceMethod.callAdapter 对象,
那么 CallAdapter 是干嘛的呢?上面调用了adapt 方法,它是为了把一个 Call 转换成另一种类型,比如当 Retrofit 和 RxJava 结合使用的时候,接口中方法可以返回 Observable,这里相当于适配器模式。默认情况下得到的是一个 Call 对象,它是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);        }      });    }  });}

在 enqueue 方法中,调用了 OkHttpCall 的 enqueue,所以这里相当于静态的代理模式。OkHttpCall 中的 enqueue 其实又调用了原生的 OkHttp 中的 enqueue,这里才真正发出了网络请求,部分代码如下:

@Override public void enqueue(final Callback<T> callback) {    if (callback == null) throw new NullPointerException("callback == null");    //真正请求网络的 call    okhttp3.Call call;    Throwable failure;    synchronized (this) {      if (executed) throw new IllegalStateException("Already executed.");      executed = true;      //省略了部分发代码      ...      call = rawCall;      //enqueue 异步执行    call.enqueue(new okhttp3.Callback() {      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)          throws IOException {        Response<T> response;        try {        //解析数据 会用到 conveterFactory,把 response 转换为对应 Java 类型          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 获取数据后,解析数据并回调callback响应的方法,一次网络请求便完成了。

Retrofit对象create方法返回Proxy.newProxyInstance动态代理

Java动态代理就是给了程序员一种可能:当你要调用某个Class的方法前或后,插入你想要执行的代码:
比如你要执行某个操作前,你必须要判断这个用户是否登录,或者你在付款前,你需要判断这个人的账户中存在这么多钱(见http://www.jianshu.com/p/c1a3a881a144描述)。

/** Create an implementation of the API defined by the {@code service} interface. */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);        }        //为了兼容 Java8 平台,Android 中不会执行        if (platform.isDefaultMethod(method)) {          return platform.invokeDefaultMethod(method, service, proxy, args);        }        //下面是重点,解析方法        ServiceMethod serviceMethod = loadServiceMethod(method);        OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);        return serviceMethod.callAdapter.adapt(okHttpCall);      }});//取自:https://segmentfault.com/a/1190000006767113

为什么要使用动态代理?

Call<ZhuanLanAuthor> call = api.getAuthor("qinchao");

create方法返回的api对象其实是一个动态代理对象,并不是一个真正的Api接口的implements产生的对象,当api对象调用getAuthor方法时会被动态代理拦截,然后调用Proxy.newProxyInstance方法中的InvocationHandler对象,它的invoke方法会传入3个参数:

  • Object proxy: 代理对象,不关心这个
  • Method method:调用的方法,就是getAuthor方法  
  • Object…args:方法的参数,就是”qinchao”

而Retrofit关心的就是method和它的参数args,接下去Retrofit就会用Java反射获取到getAuthor方法的注解信息,配合args参数,创建一个ServiceMethod对象

ServiceMethod就像是一个中央处理器,传入Retrofit对象和Method对象,调用各个接口和解析器,最终生成一个Request,包含api 的域名、path、http请求方法、请求头、是否有body、是否是multipart等等。最后返回一个Call对象,Retrofit2中Call接口的默认实现是OkHttpCall,它默认使用OkHttp3作为底层http请求client.

使用Java动态代理的目的就要拦截被调用的Java方法,然后解析这个Java方法的注解,最后生成Request由OkHttp发送

OkHttpCall就是调用ServiceMethod获得一个可以执行的Request对象,然后等到Http请求返回后,再将response body传入ServiceMethod中,ServiceMethod就可以调用Converter接口将response body转成一个Java对象.

Retrofit会对解析过的请求进行缓存,就在Map

Retrofit的接口设计及其4个接口

Retrofit的设计非常插件化而且轻量级,真的是非常高内聚而且低耦合,这个和它的接口设计有关。Retrofit中定义了4个接口:

Callback<T> 请求数据返回的接口Converter<F, T> 将HTTP返回的数据解析成Java对象Call<T> 发送一个HTTP请求,默认的实现是OkHttpCall<T>CallAdapter<T> 将Call对象转换成另一个对象,属性只有responseType一个,还有一个<R> T adapt(Call<R> call)方法,这个接口的实现类也只有一个DefaultCallAdapter

Call是Retrofit中重要的一个概念,代表被封装成单个请求/响应的交互行为

通过调用Retrofit2的execute(同步)或者enqueue(异步)方法,发送请求到网络服务器,并返回一个响应(Response)。

  • 独立的请求和响应模块
  • 从响应处理分离出请求创建
  • 每个实例只能使用一次。
  • Call可以被克隆。
  • 支持同步和异步方法。
  • 能够被取消。

好的代码就是依赖接口而不是实现

Retrofit 整个框架的代码不算太多,还是比较易读的。主要就是通过动态代理的方式把 Java 接口中的注解形式请求解析为响应的网络请求,然后交给 OkHttp 去执行。并且可以适配不同的 CallAdapter,可以方便与 RxJava 结合使用。

Retrofit非常巧妙的用注解来描述一个HTTP请求,将一个HTTP请求抽象成一个Java接口,然后用了Java动态代理的方式,动态的将这个接口的注解“翻译”成一个HTTP请求,最后再执行这个HTTP请求

Retrofit的功能非常多的依赖Java反射,代码中其实还有很多细节,比如异常的捕获、抛出和处理,大量的Factory设计模式

Retrofit中接口设计的恰到好处,在你创建Retrofit对象时,让你有更多更灵活的方式去处理你的需求,比如使用不同的Converter、使用不同的CallAdapter,这也就提供了你使用RxJava来调用Retrofit的可能

什么是好的代码?像Picasso和Retrofit这样的就是好的代码,扩展性强、低耦合、插件化


Retrofit源码的组成:

  • 一个retrofit2.http包,里面全部是定义HTTP请求的注解,比如GET、POST、PUT、DELETE、Headers、Path、Query等等
  • 余下的retrofit2包中十几个类和接口就是全部retrofit的代码了,代码真的很少,很简单,因为retrofit把网络请求这部分功能全部交给了okHttp了

以下描述粘贴自:
Retrofit2使用简介,Rabtman,7月 16, 2016

固定参数查询

@GET("tasks?pageNo=1")Call<PageDataDTO<HistoryTaskDTO>> getHistoryTasks();// 方法调用//service.getHistoryTasks();// 请求头// GET http://.../tasks?pageNo=1 HTTP/1.1

动态参数(Query)

@GET("tasks")Call<PageDataDTO<HistoryTaskDTO>> getHistoryTasks(@Query("pageNo") int no);// 方法调用//service.getHistoryTasks(2);// 请求头// GET http://.../tasks?pageNo=2 HTTP/1.1

动态参数(QueryMap)

@GET("tasks")Call<PageDataDTO<HistoryTaskDTO>> getHistoryTasks(@QueryMap Map<String,String> map);//方法调用HashMap<String,String> params = new HashMap<>();params.put("pageNo", "1");params.put("pageSize", "10");service.getHistoryTasks(params);// 请求头// GET http://.../tasks?pageNo=1&pageSize=10 HTTP/1.1

固定头

@GET("tasks")@Headers("Accept-Encoding: application/json")Call<PageDataDTO<HistoryTaskDTO>> getHistoryTask();// 方法调用//service.getHistoryTask();// 请求头// GET http://.../tasks HTTP/1.1// Accept-Encoding: application/json

动态头

@GET("tasks")Call<PageDataDTO<HistoryTaskDTO>> getHistoryTask(@Header("Location") String location);// 方法调用//service.getHistoryTask("china");// 请求头// GET http://.../tasks HTTP/1.1// Location: china

路径替换

@GET("{token}/account/total/fetch")Call<ResponseBody<String>> getTotal(@Path("token") String token);//方法调用//service.getTotal("fd88b2ff");// 请求头// GET http://.../fd88b2ff/account/total/fetch HTTP/1.1POST请求(无BODY)//POST请求无BODY@POST("auth")Call<UserDTO> userLogin();

POST请求(带BODY,)

//POST请求带BODY,其中的body对象将被Retrofit实例指定的转换器转换@POST("auth")Call<UserDTO> userLogin(@Body User user);POST请求(表单传参,Field)@FormUrlEncoded@POST("auth")Call<UserDTO> userLogin( @Field("username") String username);

POST请求(表单传参,FieldMap)

@FormUrlEncoded@POST("auth")Call<UserDTO> userLogin(@FieldMap Map<String, String> params);//方法调用HashMap<String,String> params = new HashMap<>();        params.put("username", "15222222222");        params.put("code", "123456");service.userLogin(params);

动态URL

@FormUrlEncoded Call<UserDTO> getUserInfo(@Url String url);

参考链接:
Android 网络框架之Retrofit2使用详解及从源码中解析原理
What is the best library to make HTTP calls from Java/Android?
Comparison of Android networking libraries: OkHTTP, Retrofit, and Volley [closed]
Retrofit 2.0: The biggest update yet on the best HTTP Client Library for Android

再回头看,有如下几篇源码解析的好文章,list如下:
Retrofit2 源码解析 白瓦力 2015.12.13
Android Retrofit源码解析 segmentfault 然则 2016年08月30日发布
Retrofit2 源码解析 Dec 13 2015
Android 网络框架之Retrofit2使用详解及从源码中解析原理
使用Retrofit2.0+OkHttp3.0实现缓存处理, 2016-7-29
Android网络框架源码分析二—Retrofit,2016-1-3(包含与Volley对比)
开源项目分析android-open-project-analysis,包含流程图和类图
拆轮子系列:拆 Retrofit Posted by Piasy on June 25, 2016