Retrofit源码分析(一)

金三银四速速临近,准备分析一下用了一段时日的Retrofit,本文分析Retrofit2.1.0版本,2.2.0最近新发布,据说支持RxJava2
(°∀°)ノ
Ps:对所用的框架分析也是面试重要一环哦,知其然而知其所然是考察程序员潜力的重要一环(๑•̀ㅂ•́)و✧

Retrofit源码分析(二)

介绍

Retrofit:大名鼎鼎的Square公司旗下的开源框架,是OkHttp的封装版,完成了高度的解耦,非常的好用,尤其配合是RESTful风格的服务端。

基本用法

//定义一个全局单例(推荐)
Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("yourAPI/")
                .client(new OkHttpClient())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
//定义一个接口,里面存放方法
public interface API{
//注意这个Call不是OkHttp3的Call
@GET("users/search/{keyword}")
Call<List<UserBean>> searchUserList(@Path("keyword") String keyword);
}
API api= retrofit.create(API.class);
api.searchUserList("张三").省略

入口

在我们构建了一个Retrofit实例后,调用了create方法,调用API接口中定义的方法后直接可以发起网络请求,这是怎么实现的呢?那么,请听下回分解。让我们进去瞧瞧。

public <T> T create(final Class<T> service) {
        //判断了传入的service必须为interface,且不能实现其他接口
        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 {
                        //如果调用的方法是Object的
                        if (method.getDeclaringClass() == Object.class) {
                            return method.invoke(this, args);
                        }
                        //默认进不来这个if
                        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);
                    }
                });
    }

可以看到这里运用了Java的动态代理技术,关于动态代理的详细介绍,这篇写的很详细。
我们先来看执行细节的第一句

第一句

ServiceMethod serviceMethod = loadServiceMethod(method);

看起来像是把我们在API中定义的方法searchUserList封装成了一个对象

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;
    }

果然,这里还运用了建造者模式和缓存来设计,API中的同一个方法只会构建一次,值得一提的是ServiceMethod的原注释

Adapts an invocation of an interface method into an HTTP call

意思是一个接口方法对应一个Http请求,及接下来让我们来看看ServiceMethod的构造器,都带上了注释,看官暂时不明白也没关系,后面会一一讲解清楚。反正这博文也没人看(;¬_¬)

ServiceMethod(Builder<T> builder) {
        //负责创建请求
        this.callFactory = builder.retrofit.callFactory();
        //负责将请求适配成API中的Call或者RxJava的Observable
        this.callAdapter = builder.callAdapter;
        //请求的baseUrl
        this.baseUrl = builder.retrofit.baseUrl();
        //响应体的解析器,上文设置了Gson来解析成
        this.responseConverter = builder.responseConverter;
        //请求的方式,POST,GET。。。
        this.httpMethod = builder.httpMethod;
        //请求的相对路径,API中定义的路径
        this.relativeUrl = builder.relativeUrl;
        //请求报文头
        this.headers = builder.headers;
        //请求报文类型
        this.contentType = builder.contentType;
        //是否带有请求体
        this.hasBody = builder.hasBody;
        //是否表单请求
        this.isFormEncoded = builder.isFormEncoded;
        //是否是Multipart请求,一般用于传输文件
        this.isMultipart = builder.isMultipart;
        //数组,记录请求中方法的参数,@Path,@Query等等
        this.parameterHandlers = builder.parameterHandlers;
  }

我们前文在API中定义的一个方法searchUserList的生辰八字(包括方法的注解信息)就被封装到了ServiceMethod中了。

第二句

OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);

如果说第一句是乘客准备好了身份证,那么第二句代码就是购票上车的具体操作。
我们可以看到OkHttpCall的内部是实现了retrofit2包下的Call<T>,主要是一个同步方法execute,一个异步方法enqueue

    @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
                    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();
                }
            }
        });
    }
    @Override 
    public Response<T> execute() throws IOException {
        okhttp3.Call call;

        synchronized (this) {
            if (executed) throw new IllegalStateException("Already executed.");
            executed = true;

            if (creationFailure != null) {
                if (creationFailure instanceof IOException) {
                    throw (IOException) creationFailure;
                } else {
                    throw (RuntimeException) creationFailure;
                }
            }

            call = rawCall;
            if (call == null) {
                try {
                    //创建一个Call
                    call = rawCall = createRawCall();
                } catch (IOException | RuntimeException e) {
                    creationFailure = e;
                    throw e;
                }
            }
        }

        if (canceled) {
            call.cancel();
        }
        //将Response转换
        return parseResponse(call.execute());
    }

细心地读者可能都发现了,在这2个方法中都调用了2个方法
一个是

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;
  }

这个方法完成了如下几步操作
1.将serviceMethod(乘客身份信息)+args(乘客选择的列车座位)生成了一个okhttp3.Request(列车票)
2.serviceMethod.callFactory.newCall(request)上车
3.将call返回,通知上级乘客已经就位
toRequest(args)无非就是将参数构建成一个okhttp3请求,略过~~这里注意,将request转换成okhttp3.Call是通过callFactory来完成的,callFactoryokhttp3.Call下的Factory接口,默认也就是自家的OkHttpClient,也可以通过new Retrofit.Builder().client(yourClient)来改变。
还有一个是

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) {
    //Http错误码的情况
      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) {
    //204-205时服务端不返回实体内容
      return Response.success(null, rawResponse);
    }

    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
      //将从okhttp3来的response通过转换再解析成数据实体T
      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


这个方法内部也是调用接口来实现的

T toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
  }

接口的初始化这里

new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.build();

也就是说看官不喜欢Gson也可以选用Jackson以及自定义,在项目中一般都要自定义,这里先挖个坑,(`・ω・´)
那么我们花了不少的篇幅来讲解乘客购票上车这个动作,毕竟上车了不是还会有验票吗 手动滑稽

第三句

return serviceMethod.callAdapter.adapt(okHttpCall);

接下来我们来看动态代理的最后一句,也就是司机开车,哦不,发起请求
这里我们又见到了老熟人serviceMethod,这里也是通过callAdapter这个接口来发起请求的。

//我们也可以添加对RxJava的支持,安利一波
Retrofit retrofit = new Retrofit.Builder()
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();

由于在Retrofit中支持的adapterFactories是一个List类型,并且默认会在List中添加一个默认的ExecutorCallAdapterFactory,这里我们先分析它的源码

final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
  final Executor callbackExecutor;

  ExecutorCallAdapterFactory(Executor callbackExecutor) {
    //传入一个回调 调度器
    this.callbackExecutor = callbackExecutor;
  }

  @Override
  public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    final Type responseType = Utils.getCallResponseType(returnType);
    return new CallAdapter<Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public <R> Call<R> adapt(Call<R> call) {
        //返回调度器的回调结果
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }

  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);
            }
          });
        }
      });
    }

    @Override public boolean isExecuted() {
      return delegate.isExecuted();
    }

    @Override public Response<T> execute() throws IOException {
      return delegate.execute();
    }

    @Override public void cancel() {
      delegate.cancel();
    }

    @Override public boolean isCanceled() {
      return delegate.isCanceled();
    }

    @SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone.
    @Override public Call<T> clone() {
      return new ExecutorCallbackCall<>(callbackExecutor, delegate.clone());
    }

    @Override public Request request() {
      return delegate.request();
    }
  }
}

短短100行代码看官可能看的有点找不到北


Retrofit.Builder中我们执行最后的build()时,Retrofit已经为我们准备了默认的调度器,也就是Handler,也就是说callbackExecutor.execute(Runnable)这个方法就相当于Handler post一个任务到主线程,那么剩下的也就是纸老虎了,细心的同学可能注意到了,ExecutorCallbackCall其实就是一个装饰者模式。那么我们就简单的分析完了Retrofit的一部分源码。

总结


笔者总结了一张精简的本文阉割版,如有错误,敬请指正。
对RxJavaAdapter的分析和其他的补充就放到下一篇了(。・ω・。)
Retrofit源码分析(二)

“你们是世上的盐。盐若失了味,怎能叫它再咸呢?以后无用,不过丢在外面,被人践踏了。你们是世上的光。城造在山上是不能隐藏的。人点灯,不放在斗底下,是放在灯台上,就照亮一家的人。你们的光也当这样照在人前,叫他们看见你们的好行为,便将荣耀归给你们在天上的父。” (马太福音 5:13-16 和合本)

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

推荐阅读更多精彩内容