Retrofit2源码阅读

声明:本文章基于retrofit:2.3.0

一.Retrofit2中类的介绍

1.retrofit2/http包下的注解 Retrofit2大都是用于构造请求的, 这些注解可以分为三类 :"Http请求方法类","标记类","参数类",具体会在中描述

2.Call:(接口)类似于OKHttp的Call就是对一个请求的封装,我们一般在接口文件中定义的方法其返回值就是Call<T> 类型.

3.CallAdapter:(接口其中有一个抽象内部类Factory):用于对Call进行操作,包装的类,Retrofit2提供了很多CallAdapter在中会有描述

4.DefaultCallAdapterFactory:是对CallAdapter.Factory的实现

5.Converter:(接口其中有一个抽象内部类Factory)用于转换数据,如将请求网络返回的Response转换成我们需要的javaBean,Retrofit2提供了很多Converter在中会有描述.当然不仅仅是针对返回的数据,还能用于一般备注解的参数的转化例如@Body标识的对象做一些操作

6.BuiltInConverters:(实现了Converter.Factory) 是Retrofit2中Converter的默认实现

7.Platform:Android,java8等平台信息的封装,可获取平台信息,并且每个平台都有对应的回调接口,比如Android平台就会具有回调到主线程的接口.

8.ServiceMethod:从接口方法到Http请求(OKHttp的Call)的转换器,也是使用的构造器模式.

二.对最简单的Retrofit2访问网络的代码进行源码阅读

    //配置Retrofit,并获取Retrofit实例
    Retrofit.Builder builder = new Retrofit.Builder();
    builder .addConverterFactory(GsonConverterFactory.create());
    builder .baseUrl("http://www.ccholterserver.com:44444/");
    Retrofit retrofit =builder .build();
    //获取接口实例
    RetrofitDemo retrofitDemo = retrofit.create(RetrofitDemo.class);
    Call<ResponseBody> call = retrofitDemo.getImage();
    //执行请求
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            ResponseBody body = response.body();
            try {
                Log.e("MainActivity", body.string());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {}
    });

1.在执行第一句代码创建Retrofit.Builder的时候

Retrofit.Builder builder = new Retrofit.Builder();

系统首先会调用Platform.get()判断当前的平台是什么,

public Builder() {
  this(Platform.get());
}

然后对平台信息进行记录,并为Retrofit2添加一个默认的ConverterFactory(转换器)

Builder(Platform platform) {
  this.platform = platform;
  converterFactories.add(new BuiltInConverters());
}

在Retrofit中有一个存储ConverterFactory的集合

private final List<Converter.Factory> converterFactories = new ArrayList<>();

2.第二句第三句代码是对Retrofit.Builder进行配置

 builder .addConverterFactory(GsonConverterFactory.create());
 builder .baseUrl("http://www.ccholterserver.com:44444/");

在执行 builder .addConverterFactory(GsonConverterFactory.create());的时候也就是给converterFactories 集合中再添加了一个ConverterFactory.

/** Add converter factory for serialization and deserialization of objects. */
public Builder addConverterFactory(Converter.Factory factory) {
  converterFactories.add(checkNotNull(factory, "factory == null"));
  return this;
}

在执行builder .baseUrl("http://www.ccholterserver.com:44444/");这句代码的时候首先会对baseUrl进行检查,如果没有问题则转换为HttpUrl然后进行记录到Builder中

public Builder baseUrl(String baseUrl) {
  //检查baseUrl 是否为null
  checkNotNull(baseUrl, "baseUrl == null");
  //将String型的baseUrl转换成HttpUrl
  HttpUrl httpUrl = HttpUrl.parse(baseUrl);
  if (httpUrl == null) {
    throw new IllegalArgumentException("Illegal URL: " + baseUrl);
  }
  return baseUrl(httpUrl);
}

public Builder baseUrl(HttpUrl baseUrl) {
  //检查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);
  }
  //对baseUrl 进行记录
  this.baseUrl = baseUrl;
  return this;
}

3.第四句代码** Retrofit retrofit =builder .build();**执行了builder.build()方法,这个就是创建一个Retrofit然后将builder记录的数据传递给Retrofit.

public Retrofit build() {
  //如果baseUrl 为空 就抛异常
  if (baseUrl == null) {
    throw new IllegalStateException("Base URL required.");
  }

  //如果用户没有设置OkHttpClient的话,Retrofit会自己创建一个        
  //OkHttpClient对象
  okhttp3.Call.Factory callFactory = this.callFactory;
  if (callFactory == null) {
    callFactory = new OkHttpClient();
  }
  
  //如果用户没有设置callbackExecutor,那么Retrofit就会将该平台默认    
  //的Executor设置给自己
  Executor callbackExecutor = this.callbackExecutor;
  if (callbackExecutor == null) {
    callbackExecutor = platform.defaultCallbackExecutor();
  }

  // 将用户设置的CallAdapter.Factory进行拷贝并添加该平台默认的
  //CallAdapter.Factory
  List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
  adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

  // 将用户设置的Converter.Factory进行拷贝,默认的Converter.Factory
  //已经在创建Builder的时候设置了
  List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

  //创建一个Retrofit实例,并返回
  return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
      callbackExecutor, validateEagerly);
}


//在创建Retrofit实例的时候其实也没有做其他的事情,只是将从Builder中传
//递过来的参数进行了记录

Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl, List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,
  @Nullable Executor callbackExecutor, boolean validateEagerly) {
this.callFactory = callFactory;
this.baseUrl = baseUrl;
this.converterFactories = unmodifiableList(converterFactories); // Defensive copy at call site.
this.adapterFactories = unmodifiableList(adapterFactories); // Defensive copy at call site.
this.callbackExecutor = callbackExecutor;
this.validateEagerly = validateEagerly;
}

4.下面执行的第五局代码** RetrofitDemo retrofitDemo = retrofit.create(RetrofitDemo.class);**就是通过Retrofit实例将我们写url的接口文件进行实例化.其实里面是通过动态代理来对接口进行实例化,然后在调用接口中方法的时候都会先调用代理中InvocationHandler中的invoke()方法.

 public <T> T create(final Class<T> service) {
//对传入的interface进行检查
Utils.validateServiceInterface(service);
//如果我们设置了validateEagerly为true,那么Retrofit就会提前检查传入  
//Interface中的方法
if (validateEagerly) {
  eagerlyValidateMethods(service);
}
//返回传入interface的实例
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, @Nullable Object[] args)
          throws Throwable {
        //如果传入的method是object的中定义的方法那就直接执行
        if (method.getDeclaringClass() == Object.class) {
          return method.invoke(this, args);
        }
        //isDefaultMethod()这个方法其实只是对java8平台上的有效,Android平台上直接返回的是false
        if (platform.isDefaultMethod(method)) {
          return platform.invokeDefaultMethod(method, service, proxy, args);
        }
        
        //将接口中定义的方法封装成ServiceMethod,其实也就是获取到接口中方法的注解,和参数来进行转换
        ServiceMethod<Object, Object> serviceMethod =
            (ServiceMethod<Object, Object>) loadServiceMethod(method);
      
        //将ServiceMethod再次封装成OkHttpCall(实现了Retrofit的Call接口)
        OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
        //返回一个Retrofit的Call对象(就是ExecutorCallAdapterFactory中的内部类ExecutorCallbackCall)
        return serviceMethod.callAdapter.adapt(okHttpCall);
      }
    });

}

5.下面执行第六句代码Call<ResponseBody> call = retrofitDemo.getImage();这个流程就是执行InvocationHandler的invoke方法 在上面4 中已经有注释了,在这里我们来看看** return serviceMethod.callAdapter.adapt(okHttpCall);**这句代码中callAdapter道理是如何确定为 ExecutorCallAdapterFactory,最后返回的结果也就是ExecutorCallAdapterFactory中的内部类ExecutorCallbackCall

首先执行serviceMethod.callAdapter这句代码,那么我们就来看看这个callAdapter成员变量在哪里确定的

在ServiceMethod的Builder.build()方法中找到了

 public ServiceMethod build() {
   //获取到callAdapter 
  callAdapter = createCallAdapter();
  responseType = callAdapter.responseType();
  ...
 }

然后执行了createCallAdapter()方法,在这里调用了Retrofit的callAdapter()方法

private CallAdapter<T, R> createCallAdapter() {
  ...
  Annotation[] annotations = method.getAnnotations();
  try {
    //获取callAdapter
    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);
  }
}

下面就到了Retrofit的callAdapter方法中

public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
return nextCallAdapter(null, returnType, annotations);
}

它又调用了自己的nextCallAdapter方法,就在这里返回了CallAdapter

public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
  Annotation[] annotations) {
checkNotNull(returnType, "returnType == null");
checkNotNull(annotations, "annotations == null");

int start = adapterFactories.indexOf(skipPast) + 1;

for (int i = start, count = adapterFactories.size(); i < count; i++) {

  //遍历存CallAdapter.Factory的集合,然后找到adapter.Factory,然后调用get()方法返回//遍历存CallAdapter.Factory的集合,然后找到adapter.Factory
  CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
  if (adapter != null) {
    return adapter;
  }
}
...
}

到目前为止我们只给adapterFactories这个集合中转入了一个CallAdapter.Factory,那就是在创建Retrofit的时候调用adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));这句代码向adapterFactories中添加了CallAdapter.Factory

我们已经知道了这里的platform是Android 所以只需要看看Android中的defaultCallAdapterFactory就可以了

@Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
  if (callbackExecutor == null) throw new AssertionError();
  return new ExecutorCallAdapterFactory(callbackExecutor);
}

到现在我们终于知道那个serviceMethod.callAdapter就是ExecutorCallAdapterFactory了

6.最后执行了第七句代码** call.enqueue(new Callback<ResponseBody>() {...}** 首先我们通过 4,5已经知道了这个call是ExecutorCallAdapterFactory中的内部类ExecutorCallbackCall,所以我们直接来看ExecutorCallbackCall的enqueue()方法.首先会调用callbackExecutor的enqueue(),这里的callbackExecutor就是OKHttpCall,OKHttpCall的enqueue()方法中首先会创造一个OKHttp的Call然后执行Call.enqueue方法执行请求,最后使用Converter对请求回来的Response进行转换

@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "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进行转换
      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();
    }
  }
});
}




 Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
......
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
  //调用ServiceMethod的toResponse的方法使用Converter进行转换
  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;
}
}


//调用Converter的convert方法转换
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}

在这里我们可以明显的看出来Retrofit中使用callbackExecutor将线程从子线程切换到了主线程,使onResponse()和onFailure()运行在了主线程,这里的callbackExecutor就是Android中的MainThreadExecutor,它在Retrofit的Builder.build方法中实例化,具体可以看 3

@Override public void enqueue(final Callback<T> callback) {
  checkNotNull(callback, "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);
        }
      });
    }
  });
}

Retrofit2到OKHttp的调用流程图

Retrofit2调用流程图.png

三.Retrofit2中注解分类及用法

1.HTTP请求方法:下表中列举的方法除了Http都是Http中的请求方法,而Http可以代替其他所有
image.png
2.标记类
image.png
3.参数类
image.png

四.Retrofit2中提供的Converter

Converter Gradle依赖
Gson com.squareup.retrofit2:converter-gson:2.0.2
Jackson com.squareup.retrofit2:converter-jackson:2.0.2
Moshi com.squareup.retrofit2:converter-moshi:2.0.2
Protobuf com.squareup.retrofit2:converter-protobuf:2.0.2
Wire com.squareup.retrofit2:converter-wire:2.0.2
Simple XML com.squareup.retrofit2:converter-simplexml:2.0.2
Scalars com.squareup.retrofit2:converter-scalars:2.0.2

五.Retrofit2中提供的CallAdapter

CallAdapter Gradle依赖
guava com.squareup.retrofit2:adapter-guava:2.0.2
Java8 com.squareup.retrofit2:adapter-java8:2.0.2
rxjava com.squareup.retrofit2:adapter-rxjava:2.0.2

六.值得学习的代码

1.获取当前是那个平台(Platform类),如下是关键代码

private static Platform findPlatform() {
  try {
    Class.forName("android.os.Build");
    if (Build.VERSION.SDK_INT != 0) {
      return new Android();
     }
  } catch (ClassNotFoundException ignored) {
  }
  try {
    Class.forName("java.util.Optional");
    return new Java8();
  } catch (ClassNotFoundException ignored) {
  }
  return new Platform();
}

七.总结及问题

1.Retrofit2和OKHttp的关系
  Retrofit2其实就是对OKHttp进行了封装,底层请求网络完全就是OKHttp,不过他将请求回来的结果发送到了主线程.

2.Retrofit2中的baseUrl可以不以""结尾,单看其他的博客说有的版本是必须要加上""的所以我们最好加上,避免版本兼容问题

3.Retrofit2中的baseUrl不能为null,或者为空字符串(或者错误的url).当为null的话会报

throw new NullPointerException("baseUrl == null");

如果为空字符串(或者错误的url) 的话回报(但是也有可能控制台什么错误都不会输出,但是debug的话确实走到这句代码,我就遇到这样的问题)

 throw new IllegalArgumentException("Illegal URL: " + baseUrl);

4.Retrofit2中Path和Query的区别
Path是用于url中路径的设置(Url中"?"之前是路径)
Query 适用于Url中参数的设置(Url中"?"之后是参数)
下面举例

  //正确的写法
 @GET("users")
 Call<List<User>> getUsersBySort(@Query("sortby") String sort);
  //错误的写法
 @GET("users?sortby={sortby}")
 Call<List<User>> getUsersBySort(@Path("sortby") String sort);

5.自定义Converter.Fectory需要注意的

  1. responseBodyConverter 主要是对应@Body注解,完成ResponseBody到实际的返回类型的转化,这个类型对应Call<XXX>里面的泛型XXX,其实@Part等注解也会需要responseBodyConverter,只不过我们的参数类型都是RequestBody,由默认的converter处理了。
    2.requestBodyConverter 完成对象到RequestBody的构造。
    3.一定要注意,检查type如果不是自己能处理的类型,记得return null (因为可以添加多个,你不能处理return null ,还会去遍历后面的converter).

八.参考文章

http://www.jianshu.com/p/308f3c54abdd
http://blog.csdn.net/lmj623565791/article/details/51304204

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容