Retrofit源码解析(一)源码流程

只是对网络请求做了一个封装,但是不做具体的网络请求,网络请求由okHttp做的。
讲解版本:

implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'

简单使用

interface IFreeService {
    @GET("api/personalMessage/get/{id}")
    fun updatePersonalMessage(
        @Path("id") personalId: String
    ): Call<ResponseBody>
}

var retrofit = Retrofit.Builder().baseUrl("")
    //设置数据解析器,创建含有Gson对象实例的GsonConverterFactory
    .addConverterFactory(GsonConverterFactory.create())
    .build()

// 将接口转换为实现类
var service = retrofit.create(IFreeService::class.java)
var respo = service.updatePersonalMessage("1")

// 同步请求
val request = respo.request()
// 异步请求
respo.enqueue(object : Callback<ResponseBody>{
    override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
        TODO("Not yet implemented")
    }

    override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
        TODO("Not yet implemented")
    }
})

整体流程

  • Build设计模式初始化Retrofit对象。根据不同平台初始化不同平台的对应继承子类。生成一个回调回UI线程的执行器,可以将执行结果返回至Ui线程内。生成一个网络请求适配器工厂。
  • 通过retrofit.create这个方法生成接口实现类,采用动态代理设计模式,把IFreeService的方法调用集中到了InvocationHandler.invoke,再构建了ServiceMethod,OKHttpCall,返回callAdapter.adapt的结果。使用Converter转化响应数据。
  • 借助Retrofit通过okHttp进行网络请求。通过toRequest方法,将retrofit的Request转换成okHttp的Request。对okHttp请求回来的数据通过Converter进行解析,调用到ServiceMethod#toResponse方法,将okhttp3.Response转化为retrofit2.Response。

核心

CallAdapter

callAdapter是由addCallAdapterFactory方法传入的,不传默认DefaultCallAdapterFactory。
DefaultCallAdapterFactory的adapter方法默认返回call。
作用是将包装对象转化为用户自定义对象。

Converter

Converter是由addConverterFactory方法传入的。
作用是将okhttp3.Response转化为retrofit2.Response。

Retrofit#create

public <T> T create(final Class<T> 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, @Nullable Object[] args)
              throws Throwable {
            ...
            // 创建ServiceMethod对象,ServiceMethod主要负责解析它对应的method的各种参数,调用toRequest为OkHttp提供Request。
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            // OkHttpCall内部封装okhttp3.Call。
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            // 将包装对象转化为用户自定义对象
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
}

请求数据解析

ServiceMethod#Build()

Builder(Retrofit retrofit, Method method) {
  this.retrofit = retrofit;
  // 接口方法
  this.method = method;
  // 接口方法的注解,一般是请求方式
  this.methodAnnotations = method.getAnnotations();
  // 参数类型
  this.parameterTypes = method.getGenericParameterTypes();
  // 参数注解数组
  this.parameterAnnotationsArray = method.getParameterAnnotations();
}

ServiceMethod#build()

public ServiceMethod build() {
  callAdapter = createCallAdapter();
  responseType = callAdapter.responseType();
  ...
  responseConverter = createResponseConverter();

  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation);
  }
  ...
  int parameterCount = parameterAnnotationsArray.length;
  parameterHandlers = new ParameterHandler<?>[parameterCount];
  for (int p = 0; p < parameterCount; p++) {
    Type parameterType = parameterTypes[p];
    ...
    Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
   ...
    parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
  }
  ...
  return new ServiceMethod<>(this);
}

接口注解解析

parseHttpMethodAndPath

private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
  this.httpMethod = httpMethod;
  this.hasBody = hasBody;

  int question = value.indexOf('?');
  if (question != -1 && question < value.length() - 1) {
    String queryParams = value.substring(question + 1);
    Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
  }
  // 省略域名的URL
  this.relativeUrl = value;
  this.relativeUrlParamNames = parsePathParameters(value);
}

参数类型,注解解析

int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
    Type parameterType = parameterTypes[p];
    
    Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
    // 把参数类型,参数注解放在一起解析之后存储到了这个ParameterHandler<T>数组中,中间主要做了多种合法性校验,并根据注解的类型,生成不同的 ParameterHandler<T>子类
    // ParameterHandler抽象类,在apply方法中进行参数替换
    parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}

总结

Retrofit和OkHttp合作流程

Retrofit的OkHttpCall内部封装okhttp3.Call,调用enqueue方法时,内部实际调用的是okHttp的enqueue方法,okHttp使用的Request通过ServiceMethod的toRequest方法创建。回调回来的Response,通过parseResponse方法,使用Converter转化为自定义类型的Response。

Retrofit数据处理流程

交给了callAdapter以及converter去处理,callAdapter负责把okHttpCall转成用户自定义对象,converter负责把服务器返回的数据转成具体的实体类。

参考

Retrofit是如何工作的
Retrofit原理解析最简洁的思路

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