Retrofit2简单使用与分析(未完)

Retrofit2出自大名鼎鼎的Square公司,使我们以Java接口及注解的形式来完成Http请求,其简洁明了结构清晰的写法,让人爱不释手。之前也有过使用Retrofit2,当时也是只处于会用的形式,很多地方只是一知半解,今天呢,再来回顾一下,探究一下具体的使用与原理。

简单示例

​ 根据官网文档说明

  1. 我们先创建一个Java接口
public interface GitHubService {
  @GET("users/{user}/repos") //get后面括号这一部分表示具体的路径地址
  Call<List<Repo>> listRepos(@Path("user") String user);
}
  1. 创建Retrofit的实例
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/") //网络请求的baseUrl
    .build();
  1. 行网络请求,获得返回数据
//通过retrofit创建GitHubService的动态代理
GitHubService service = retrofit.create(GitHubService.class);
//通过代理调用相应方法
Call<List<Repo>> repos = service.listRepos("octocat");//进行请求并返回数据

​ 通过以上3步,我们就可以进行一次Get请求,这是怎么实现的呢?稍后我们来一步步的进行探究。

使用详解

在上述示例中,我们进行了一个简单的get请求,其中用到了2个注解

@Get就表示请求类型为get请求,@path表示的请求路径中的占位符

例子中的最终请求链接为 https://api.github.com/users/octocat/repos,这个是怎么得到的呢 ?

在第2步创建retrofit的实例中,我们可以看到这是使用了一个建造者模式来完成retrofit实例的创建。

就先看一下build方法吧,简单说就是把所有配置信息整合起来,完成retrofit实例的构建:

  public Retrofit build() {
      if (baseUrl == null) { //1. 必须设置baseUrl 要不直接报错
        throw new IllegalStateException("Base URL required.");
      }
      //2.设置callFactory,如果没有设置的话默认使用okhttpclient
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }
      //用来线程任务 ,android默认在主线种执行
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }
      
      // 添加默认的Call适配器 用于请求
      // Make a defensive copy of the adapters and add the default Call adapter.
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
      
      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories = new ArrayList<>(
          1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

      //添加数据转换器,用于将网络请求返回的结果转换成我们需要的类型 用于处理返回数据
      // Add the built-in converter factory first. This prevents overriding its behavior but also
      // ensures correct behavior when using converters that consume all types.
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
      converterFactories.addAll(platform.defaultConverterFactories());

      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }
  }
注解类型的分类

这里我们可以根据API文档来查看,发现Retrofit仅仅有数十个类

(图一)retrofit2包中的类或接口
(图二)http请求相关的注解类

通过上以2图可以发现,图一(左图)是具体实现相关的类,图二(右图)全是http请求相关的注解类。

这里我们先对这些注解进行一个分类,根据这些注解类的功能及描述,可以分为三大类如下表所示

  • 请求方法的类型:
GET POST
PUT 
PATCH
DELETE
OPTIONS
HEAD //以上这7个分别对应http中的网络请求方法
HTTP //可以使用此方法来替代以上7个方法
  • 标记类型
FormUrlEncoder 
Multipart 
Streaming 
  • 请求参数类型
Body、
Field、FieldMap、
Header、Headers、HeaderMap、
Part、PartMap、
Path、
Query、QueryMap、QueryName、
Tag、
Url

想想整个请求过程大概是这样:

  1. 定义api接口类,使用注解定义请求方法及所需要的参数信息
  2. 创建retrofit的实例对象,进行一个基本公共信息配置,也可以提前
  3. 通过retrofit实例,创建api接口类的动态代理类
    • 解析api接口类中的所有方法及注解信息
    • 根据注解信息,构造出真实的请求call信息
  4. 通过生成的代理类,调用相应的请求方法获得返回数据

解释:

其中第一环节主要是根据注解定义请求方法与参数,这里主要涉及到retrofit中注解的运用

第二环节主要是配置公共信息,这里用的最多的就是addConverterFactory()与addCallAdapterFactory()针对返回数据和请求进行转换适配,常用的 就是gson、rxjava

第三环节是重中之重,这个环节搞明白了retrofit基本上也就搞通了:

   public <T> T create(final Class<T> service) {
 //验证接口类(主要是验证service是否是接口及接口 其类型参数类型是否支持 以及是否提前获取ServiceMethod)
        validateServiceInterface(service);
        return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service},
                new InvocationHandler() {
                    private final Platform platform = Platform.get();
                    private final Object[] emptyArgs = new Object[0];

                    @Override
                    public @Nullable
                    Object invoke(Object proxy, Method method,
                                  @Nullable 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);
                }
                //关键方法        
                return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
                    }
                });
    }
//通过接口类中的Method,解析上面的注解信息,获取转换之后的ServiceMethod
//每一个方法 都对应一个 servicemethod
ServiceMethod<?> loadServiceMethod(Method method) { 
        //先从缓存中取
        ServiceMethod<?> result = serviceMethodCache.get(method);
        if (result != null) return result;
        synchronized (serviceMethodCache) {
            result = serviceMethodCache.get(method);
            if (result == null) {
                //未获取的话 通过retrofit实例 和 method  来完成注解解析
                result = ServiceMethod.parseAnnotations(this, method);
                //缓存起来
                serviceMethodCache.put(method, result);
            }
        }
        return result;
    }
 static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
        //解析method中的 方法注解信息 构建请求信息RequestFactory
        RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method); //⑴

        Type returnType = method.getGenericReturnType(); //获取方法对应的返回类型
        if (Utils.hasUnresolvableType(returnType)) {
            throw methodError(method,
   "Method return type must not include a type variable or wildcard: %s", returnType);
        }
        if (returnType == void.class) {
            throw methodError(method, "Service methods cannot return void.");
        }
        //根据retrofit,method,requestFactory 获取servicemethod ,
     //这个里面已经对对请求 和返回类型进行了适配
        return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);//⑵
    }
解析注解构建请求信息(方法上面的注解和参数里面的注解)

先看一下第一处·RequestFactory.parseAnnotations(retrofit, method);

 static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
        return new Builder(retrofit, method).build();
    }

又是一个建造者模式,再具体看一下,Builder方法的具体信息和build方法

  Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations(); //1.获取方法上的注解信息
      this.parameterTypes = method.getGenericParameterTypes();//2.获取方法上面的返回参数类型
      this.parameterAnnotationsArray = method.getParameterAnnotations();//3.获取方法上面的参数注解信息
  }
 RequestFactory build() {
     //遍历方法中的所有注解
     for (Annotation annotation : methodAnnotations) {
         parseMethodAnnotation(annotation);
     }
      ...省略
     int parameterCount = parameterAnnotationsArray.length; //参数注解的长度
     parameterHandlers = new ParameterHandler<?>[parameterCount];
     for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
         parameterHandlers[p] =
             parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);//解析参数注解信息 并返回一个处理器 
     }
 ...省略
     

     return new RequestFactory(this);
 }

这里面又暴露出来2个关键方法parseMethodAnnotationparseParameter

parseMethodAnnotation 实际就是解析方法上面的注解信息,比如使用的哪种请求方式,请求路径,header信息等

 private void parseMethodAnnotation(Annotation annotation) {
     if (annotation instanceof DELETE) {
         parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
     } else if (annotation instanceof GET) {
         parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
     } else if (annotation instanceof HEAD) {
         parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
     } else if (annotation instanceof PATCH) {
         parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
     } else if (annotation instanceof POST) {
         parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
     } else if (annotation instanceof PUT) {
         parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
     } else if (annotation instanceof OPTIONS) {
         parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);

     } else if (annotation instanceof HTTP) {
         HTTP http = (HTTP) annotation;
         parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());

     } else if (annotation instanceof retrofit2.http.Headers) {
         String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
         if (headersToParse.length == 0) {
             throw methodError(method, "@Headers annotation is empty.");
         }
         headers = parseHeaders(headersToParse); //解析header注解信息
     } else if (annotation instanceof Multipart) { //multipart 与 formUrlencoded 互斥
         if (isFormEncoded) {
             throw methodError(method, "Only one encoding annotation is allowed.");
         }
         isMultipart = true;
     } else if (annotation instanceof FormUrlEncoded) {
         if (isMultipart) {
             throw methodError(method, "Only one encoding annotation is allowed.");
         }
         isFormEncoded = true;
     }
 }

parseHttpMethodAndPath方法主要就是

  • 获取请求方式,
  • 是否有请求体,
  • 以及检查注解值(请求url)中是否带有?,以及问号后面是否有key-value
    • 如果问号后面带有类似?key=value的情况抛错,使用@query
  • 获取请求url(使用的是相对路径)
  • 以及url中是否有使用{}括起来的路径占位符

parseParameter 主要就是解析method中的参数注解信息

private @Nullable
ParameterHandler<?> parseParameter(
        int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
    ParameterHandler<?> result = null;
    if (annotations != null) {
        for (Annotation annotation : annotations) {
            ParameterHandler<?> annotationAction =
                    parseParameterAnnotation(p, parameterType, annotations, annotation);
                    //解析方法中的参数注解信息 获得相应参数处理器 比如@path @query ...
            if (annotationAction == null) {
                continue;
            }

            if (result != null) {
                throw parameterError(method, p,
                        "Multiple Retrofit annotations found, only one allowed.");
            }

            result = annotationAction;
        }
    }

    return result;
}
parsePathParamters

RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method); //⑴

第(1)处的讲解主要就在上面了,下面看关键的核心方法(2)

HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);//⑵

根据构建好的请求信息及方法来适配真正的请求和请求返回数据的相应转换
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
    Retrofit retrofit, Method method, RequestFactory requestFactory) {
    boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
    boolean continuationWantsResponse = false;
    boolean continuationBodyNullable = false;

    //方法上面的注解
    Annotation[] annotations = method.getAnnotations();
    Type adapterType;
    if (isKotlinSuspendFunction) { //暂时不看kotlin
        Type[] parameterTypes = method.getGenericParameterTypes();
        Type responseType = Utils.getParameterLowerBound(0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
        if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
            // Unwrap the actual body type from Response<T>.
            responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
            continuationWantsResponse = true;
        } else {
            // TODO figure out if type is nullable or not
            // Metadata metadata = method.getDeclaringClass().getAnnotation(Metadata.class)
            // Find the entry for method
            // Determine if return type is nullable or not
        }

        adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
        annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
    } else {
        adapterType = method.getGenericReturnType();
    }

    //根据注解和返回类型 构建callAdapter
    CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method, adapterType, annotations); //(1)

    Type responseType = callAdapter.responseType();
    if (responseType == okhttp3.Response.class) {
        throw methodError(method, "'"
                          + getRawType(responseType).getName()
                          + "' is not a valid response body type. Did you mean ResponseBody?");
    }
    if (responseType == Response.class) {
        throw methodError(method, "Response must include generic type (e.g., Response<String>)");
    }
    // TODO support Unit for Kotlin?
    if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
        throw methodError(method, "HEAD method must use Void as response type.");
    }

    Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(retrofit, method, responseType); //(2)

    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if (!isKotlinSuspendFunction) {
        return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    } else if (continuationWantsResponse) {
        //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
        return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForResponse<>(requestFactory,
                                                                                callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
    } else {
        //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
        return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForBody<>(requestFactory,   callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter, continuationBodyNullable);
    } //(3)
}

上述方法主要就是构建ServiceMethod,里面最主要的就是进行请求Call的适配和返回数据类型转换的适配

代码(1)处CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method, adapterType, annotations);就是根据方法返回类型和注解信息构建CallAdapter,

代码(2)处Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(retrofit, method, responseType);就是根据返回Call的返回数据和方法来进行数据转换,以转换成我们所需要的类型.

代码(3)处 就是根据前而2处构建HttpServiceMethod,最后在代码调用之处 调用 生成代理类的代理方法完成请求.

  • 跟踪createCallAdapter会发现最终代码执行到了nextCallAdapter方法,这里会根据构建的retrofit实例中的callAdapterFactories与返回类型、注解信息 找到合适的适配器
   public CallAdapter<?, ?> nextCallAdapter(
    @Nullable CallAdapter.Factory skipPast, Type returnType,  Annotation[] annotations) {
        Objects.requireNonNull(returnType, "returnType == null");
        Objects.requireNonNull(annotations, "annotations == null");

        int start = callAdapterFactories.indexOf(skipPast) + 1;
        for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
          CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
            if (adapter != null) {
                return adapter;
            }
        }
        ...省略 异常
        throw new IllegalArgumentException(builder.toString());
    }
  • 同理跟踪代码createResponseConverter分发现代码最终执行到了nextResponseBodyConverter方法,这里会将callAdapter的返回数据和方法所需要的返回类型及注解信息,找到合适的数据转换器
public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
            @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
        Objects.requireNonNull(type, "type == null");
        Objects.requireNonNull(annotations, "annotations == null");

        int start = converterFactories.indexOf(skipPast) + 1;
        for (int i = start, count = converterFactories.size(); i < count; i++) {
            Converter<ResponseBody, ?> converter =
               converterFactories.get(i).responseBodyConverter(type, annotations, this);
            if (converter != null) {
                //noinspection unchecked
                return (Converter<ResponseBody, T>) converter;
            }
        }
        ... 省略
        throw new IllegalArgumentException(builder.toString());
    }

第四环节就是直接调用代理类的代理方法获得返回数据再进行处理了

loadServiceMethod(method).invoke(args != null ? args : emptyArgs);

调用方法时,会执行代理类(HttpServiceMethod)的invoke方法,

@Override
final @Nullable    ReturnT invoke(Object[] args) {
     Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args);
}

这是一份很详细的 Retrofit 2.0 使用教程(含实例讲解)

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