Retrofit架构和源码解析和基本用法代码

篇章目标要点

Retrofit是目前最为流程的网络访问框架,其内部集成了OkHttp框架。Retrofit用法非常简单,本文目的是探究一下其工作原理,通过源码了解其是解析注解参数的过程,以及处理网络返回信息的过程。计划在下一篇文章阐述其如何整合OkHttp进行网络请求的过程,以及内部的拦截器的工作原理。

Retrofit源码

可以在Gitee上获取Retrofit源码,便于在本地阅读

git clone https://gitee.com/mirrors/retrofit.git

Retrofit基本用法

1.添加依赖

//retrofit网络框架依赖
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'

2.定义请求任务接口

根据自己的任务类型制定相应的请求任务接口,在这里需要先弄清楚Base Url基础网络链接,Path路径,Query请求参数这几个概念的关系,我随便找了一个百度图片的url链接来加深对这几个概念的认识。

访问百度图片的示例
https://image.baidu.com/search/detail?ct=503316480&z=0&ipn=false&word=%E6%9D%A8%E5%B9%82&step_word=&hs=0&pn=2&spn=0&di=9280&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&istype=2&ie=utf-8&oe=utf-8&in=&cl=2&lm=-1&st=-1&cs=3359357441%2C1831176160&os=2057570920%2C3873201257&simid=4259213350%2C801558043&adpicid=0&lpn=0&ln=1872&fr=&fmq=1631953164671_R&fm=index&ic=0&s=undefined&hd=undefined&latest=undefined&copyright=undefined&se=&sme=&tab=0&width=&height=&face=undefined&ist=&jit=&cg=star&bdtype=11&oriquery=&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%3A%2F%2Fwx4.sinaimg.cn%2Fmw690%2F007lbnyvly1gudwovi41tj60jd0o4jsl02.jpg%26refer%3Dhttp%3A%2F%2Fwx4.sinaimg.cn%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Djpeg%3Fsec%3D1634545171%26t%3Db00bb646d0f38c13d770fa18d056c682&fromurl=ippr_z2C%24qAzdH3FAzdH3Fojtk5_z%26e3Bv54AzdH3Fm0dcl9canlAzdH3FKxPH7BccI&gsm=3&rpstart=0&rpnum=0&islist=&querylist=&nojc=undefined

关于Base Url基础网络链接,Path路径,Query请求参数这3个概念的关系如下

术语内容特征
Base Urlhttps://image.baidu.com/search/请求链接中头部的除Path以外的链接
Pathdetail请求链接中“?”前的参数
Queryz=0 表示Query的key为”z”,value为”0”1.“?”后的参数,每一组参数都是key,value同时出现;2.两组参数之间使用“&”连接
有了上述的概念之后,我们开始定义请求接口,示例如下
/**
 * 请求百度图片api
 * */
public interface RetrofitService {

    @GET("index")
    Call<Object> getPicture(@Query("word") String word);//请求图片
}

可以根据需要定义拦截器,一般考虑在拦截器修正请求链接,用以增加全部网络请求场景的共同参数,常见的用法有添加时间戳,添加令牌token,添加序列号等,参照以下示例

public class PictureInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        //添加新的请求参数
        HttpUrl.Builder builder = request.url().newBuilder();
        String query = request.url().query();
        builder.removeAllQueryParameters("word");
        builder.addQueryParameter("tn","baiduimage");
        builder.addQueryParameter("ipn","r");
        builder.addQueryParameter("ct","201326592");
        builder.addQueryParameter("cl","2");
        builder.addQueryParameter("lm","-1");
        builder.addQueryParameter("st","-1");
        builder.addQueryParameter("sf","1");
        builder.addQueryParameter("fmq","");
        builder.addQueryParameter("pv","");
        builder.addQueryParameter("ic","0");
        builder.addQueryParameter("nc","1");
        builder.addQueryParameter("z","");
        builder.addQueryParameter("se","1");
        builder.addQueryParameter("showtab","0");
        builder.addQueryParameter("fb","0");
        builder.addQueryParameter("width","");
        builder.addQueryParameter("height","");
        builder.addQueryParameter("face","0");
        builder.addQueryParameter("istype","2");
        builder.addQueryParameter("ie","utf-8");
        builder.addQueryParameter("fm","index");
        builder.addQueryParameter("pos","history");
        builder.addQueryParameter(query.substring(0,query.indexOf("=")),query.substring(query.indexOf("=") , query.length()-1));
        //重构请求数据
        HttpUrl newUrl = builder.build();
        Request newQuest = request.newBuilder()
                .url(newUrl)
                .method(request.method() , request.body())
                .build();
        return chain.proceed(newQuest);
    }
}

接下来就是构建请求实体方法

private final static String baseUrl = "https://image.baidu.com/search/";
//构建请求实体
private Retrofit getEntity(){
    PictureInterceptor interceptor = new PictureInterceptor();
    OkHttpClient client = new OkHttpClient.Builder()
            .addInterceptor(interceptor)
            .build();

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .build();
    return retrofit;
}

定义了一个基于关键词请求图片的方法

//基于关键词搜索图片
public void getPicture(String keyWords){
    Retrofit retrofit = getEntity();
    RetrofitService retrofitService = retrofit.create(RetrofitService.class);
    Call<Object> call = retrofitService.getPicture(keyWords);
    call.enqueue(new Callback<Object>() {
        @Override
        public void onResponse(Call<Object> call, Response<Object> response) {
            Log.d(TAG,"response body = "+response.body());
        }

        @Override
        public void onFailure(Call<Object> call, Throwable t) {
            Log.d(TAG,"error");
        }
    });
}

以上即是Retrofit基本用法的简要说明示例

网络请求任务流程

RetrofitService retrofitService = retrofit.create(RetrofitService.class)

在网络请求任务流程中,如上述示例,在创建了RetrofitService对象之后,在Retrofit类中create方法会生成RetrofitService动态代理对象。随后会通过loadServiceMethod方法解析RetrofitService方法内容,调用ServiceMethod类的方法parseAnnotations来解析注解,最终实现注解解析的是HttpServiceMethod类中的parseAnnotations方法。

//使用动态代理,由service的ClassLoader对生成的代理对象加载
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.
        if (method.getDeclaringClass() == Object.class) {
          return method.invoke(this, args);
        }
        if (platform.isDefaultMethod(method)) {
          return platform.invokeDefaultMethod(method, service, proxy, args);
        }
	//1.基于invoke执行网络请求
        return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
      }
    });

HttpServiceMethod类中的createCallAdapter方法用于获取网络请求任务

private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
    Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
  try {
    //noinspection unchecked
    //获取Call请求任务
    return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
  } catch (RuntimeException e) { // Wide exception range because factories are user code.
    throw methodError(method, e, "Unable to create call adapter for %s", returnType);
  }
}

随后该请求会走到Retrofit中,来获取下一个网络请求任务

//获取下一个网络请求任务
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());
}

最终是在DefaultCallAdapterFactory类中get()方法创建网络执行器

//创建网络请求执行器
@Override
public @Nullable CallAdapter<?, ?> get(
    Type returnType, Annotation[] annotations, Retrofit retrofit) {
  ……
  final Executor executor =
      Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
          ? null
          : callbackExecutor;

  return new CallAdapter<Object, Call<?>>() {
    @Override
    public Type responseType() {
      return responseType;
    }

    @Override
    public Call<Object> adapt(Call<Object> call) {
      return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
    }
  };
}

通过ExecutorCallbackCall类实现网络请求的执行方法

@Override
public void enqueue(final Callback<T> callback) {
  Objects.requireNonNull(callback, "callback == null");

  delegate.enqueue(
      new Callback<T>() {
        @Override
        public void onResponse(Call<T> call, final Response<T> response) {
          callbackExecutor.execute(
              () -> {
                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(() -> callback.onFailure(ExecutorCallbackCall.this, t));
        }
      });
}

网络请求任务流程用流程图概括如下
在这里插入图片描述

处理网络响应流程

这里的行为是处理网络请求得到的Response.基本流程是Retrofit创建了动态代理对象后,会调用HttpServiceMethod中的invoke方法基于反射原理调用相应方法。然后在OkHttpCall对象中execute()方法,最后是在parseResponse方法中解析Response,当中会对Response Code进行判断,比如Code值小于200或者大于等于300时认为是错误响应,会走error通知;Code值为204或205时认为是成功响应,会走success通知。
OkHttpCall中parseResponse方法源码如下

//4.解析网络响应,预处理不同code的情况
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();
  //该范围code认为响应错误
  if (code < 200 || code >= 300) {
    try {
      // Buffer the entire body to avoid future I/O.
      ResponseBody bufferedBody = Utils.buffer(rawBody);
      return Response.error(bufferedBody, rawResponse);
    } finally {
      rawBody.close();
    }
  }
  //该范围code认为响应成功
  if (code == 204 || code == 205) {
    rawBody.close();
    return Response.success(null, rawResponse);
  }

  ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
  try {
    T body = responseConverter.convert(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;
  }
}

处理网络响应的函数调用的基本流程如下
在这里插入图片描述

Retrofit解析注解参数原理

解析网络请求参数流程的发起是在Retrofit中创建了动态代理对象后,通过调用loadServiceMethod(Method method)方法请求解析RretrofitService方法内容。然后在ServiceMethod类中调用RequestFactory对象的parseAnnotations(Retrofit retrofit, Method method)方法,最终将RequestFactory对象解析的方法和参数信息传入HttpServiceMethod类中parseAnnotations方法中用于检查检查RetrofitService接口方法的注解信息,并将注解和方法信息转为网络请求任务

//解析RetrofitService方法内容
ServiceMethod<?> loadServiceMethod(Method method) {
  ServiceMethod<?> result = serviceMethodCache.get(method);
  if (result != null) return result;

  synchronized (serviceMethodCache) {
    result = serviceMethodCache.get(method);
    if (result == null) {
      //解析方法注解信息
      result = ServiceMethod.parseAnnotations(this, method);
      serviceMethodCache.put(method, result);
    }
  }
  return result;
}

RequestFactory基于注解参数类别解析代码如下

//5.基于注解参数类别解析
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);
  } 
	……

  // Get the relative URL path and existing query string, if present.
  //URL路径以?结尾,查找结尾
  int question = value.indexOf('?');
  if (question != -1 && question < value.length() - 1) {
    // Ensure the query string does not have any named parameters.
    String queryParams = value.substring(question + 1);
    Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
    if (queryParamMatcher.find()) {
      throw methodError(
          method,
          "URL query string \"%s\" must not have replace block. "
              + "For dynamic query parameters use @Query.",
          queryParams);
    }
  }

  this.relativeUrl = value;
  this.relativeUrlParamNames = parsePathParameters(value);
}

解析请求参数,并转为网络请求这部分的流程梳理总结如下
在这里插入图片描述

学习心得

Retrofit框架内部调用逻辑复杂,文章重在记录个人学习过程的理解,由于个人能力水平有限,解答过程存在不足,后续会不断完善,也欢迎大家指正交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值