Retrofit原理浅析

一、概述

Retrofit是一个网络加载框架,底层是基于OkHttp封装的,api接口定义和api接口使用解耦(参考3),并可以通过注解构建不同的请求方式和参数,还可以支持数据解析转换(gson)以及线程调度异步请求(RxJava)的优点。

二、简单示例

这个示例是作为下面原理分析的示例,比较简单的GET请求。如果要具体了解详细的使用,可以参考鸿洋_的文章学习参考2。示例中的api使用的是鸿洋_的玩安卓网站的开放api。

  • 添加权限和依赖
###AndroidManifest.xml###
<uses-permission android:name="android.permission.INTERNET" />

###build.gradle###
//    implementation 'com.squareup.retrofit2:retrofit:2.5.0'
//    implementation 'com.squareup.retrofit2:converter-gson:2.5.0' // 用Gson解析json
    implementation 'com.squareup.retrofit2:retrofit:2.0.2'
    implementation 'com.squareup.retrofit2:converter-gson:2.0.2'
  • 创建一个api接口文件,可通过注解的方式构建不同的请求
public interface ApiService {
    @GET("hotkey/json")
    Call<HotkeyList> getHotkey();//获取热搜词
}
  • 创建各请求返回结果对应的数据类. 搜索热词Hotkey数据类
public class HotkeyList {
    /*
    {"data":[{"id":6,"link":"","name":"面试","order":1,"visible":1},
    {"id":9,"link":"","name":"Studio3","order":1,"visible":1},
    {"id":5,"link":"","name":"动画","order":2,"visible":1},
    {"id":1,"link":"","name":"自定义View","order":3,"visible":1},
    {"id":2,"link":"","name":"性能优化 速度","order":4,"visible":1},
    {"id":3,"link":"","name":"gradle","order":5,"visible":1},
    {"id":4,"link":"","name":"Camera 相机","order":6,"visible":1},
    {"id":7,"link":"","name":"代码混淆 安全","order":7,"visible":1},
    {"id":8,"link":"","name":"逆向 加固","order":8,"visible":1}],
    "errorCode":0,
    "errorMsg":""}
    */
    
    private int errorCode;
    private String errorMsg;
    private List<DataBean> data;

    public int getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(int errorCode) {
        this.errorCode = errorCode;
    }

    public String getErrorMsg() {
        return errorMsg;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }

    public List<DataBean> getData() {
        return data;
    }

    public void setData(List<DataBean> data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "BannerList{" +
                "errorCode=" + errorCode +
                ", errorMsg='" + errorMsg + '\'' +
                ", data=" + data +
                '}';
    }

    public static class DataBean {
        /*{"id":6,"link":"","name":"面试","order":1,"visible":1}*/
        private int id;
        private String link;
        private String name;
        private int order;
        private int isVisible;

        @Override
        public String toString() {
            return "DataBean{" +
                    "link='" + link + '\'' +
                    ", id=" + id +
                    ", imagePath='" + name + '\'' +
                    ", isVisible=" + isVisible +
                    ", order=" + order+"}";
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getLink() {
            return link;
        }

        public void setLink(String link) {
            this.link = link;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getOrder() {
            return order;
        }

        public void setOrder(int order) {
            this.order = order;
        }

        public int getIsVisible() {
            return isVisible;
        }

        public void setIsVisible(int isVisible) {
            this.isVisible = isVisible;
        }
    }

}
  • 最重要的一步,构建Retrofit去请求网络数据。
//1、创建retrofit
Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://www.wanandroid.com")
        .addConverterFactory(GsonConverterFactory.create())
        .build();        
        
//2、用retrofit构建出对应的接口实例对象,里面用了代理模式
ApiService apiService = retrofit.create(ApiService.class);

//3、获取适配转换Call对象,其实返回的为是Call的衍生类OkHttpCall
Call<BannerList> call = apiService.getBanner();

//4、调用call.enqueue方法获取数据,最终调用的是okhttp的enqueue方法
call.enqueue(new Callback<BannerList>() {
    @Override
    public void onResponse(Call<BannerList> call, Response<BannerList> response) {
        Log.d("response", response.body().toString());
    }

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

三、原理浅析

上面的示例中,Retrofit核心步骤分为四步:

不过在分析之前先说明2个比较重要的类,后面会用到
serviceMethod:里面通过解析注解,请求参数等等,并通过toRequest提供request给OkHtppCall以以便请求网络
OkHtppCall:底层是OkHttp实现,OkHtppCall通过serviceMethod提供的request获取请求后,将结果通过toResponse传给serviceMethod做数据转换(如gson).

  • build:创建retrofit,能配置url,rxjava,gson等。
    通过构建器模式,在build方法中,通过创建了OkHttpClient对象,以及转换器集合和调用适配器集合等生成Retrofit对象。
public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // Make a defensive copy of the adapters and add the default Call adapter.
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }
  • create:用retrofit加工出对应的接口实例对象,一个代理对象。
    通过代理模式的动态代理,在create中,根据传入的接口对象,动态创建接口代理实例对象。
  public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    //动态代理,可拦截service内的方法method
    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 {
            // 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);
            }
            ServiceMethod serviceMethod = loadServiceMethod(method);
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }
  • call:调用对象(接口)中的方法,获取适配转换Call(/ExecutorCallbackCall/HttpCall)对象。
    当调用其请求方法(getHotkey)时,会被代理拦截,并将方法method转为serviceMethod,并传递给新创建的OkHttpCall对象,并通过ServiceMethod中的adapt返回Call(ExecutorCallAdapterFactory,DefaultCallAdapterFactory)
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
       new InvocationHandler() {
		......
		ServiceMethod serviceMethod = loadServiceMethod(method);
		OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
		return serviceMethod.callAdapter.adapt(okHttpCall);
		 ......
 }

那么ServiceMethod是干嘛的?serviceMethod,里面通过解析注解,请求参数等等通过toRequest提供request给OkHtppCall以以便请求网络。具体我们先看下ServiceMethod的构造函数,对其有个大概印象。
在ServiceMethod的构造参中,可以发现包含,如baseUrl,httpMethod,hasBody ,headers,isFormEncoded 等请求相关的信息。

  ServiceMethod(Builder<T> builder) {
    this.callFactory = builder.retrofit.callFactory();
    this.callAdapter = builder.callAdapter;
    this.baseUrl = builder.retrofit.baseUrl();
    this.responseConverter = builder.responseConverter;
    this.httpMethod = builder.httpMethod;
    this.relativeUrl = builder.relativeUrl;
    this.headers = builder.headers;
    this.contentType = builder.contentType;
    this.hasBody = builder.hasBody;
    this.isFormEncoded = builder.isFormEncoded;
    this.isMultipart = builder.isMultipart;
    this.parameterHandlers = builder.parameterHandlers;
  }

上面拦截代码中,首先loadServiceMethod方法中,通过构建模式,创建了一个ServiceMethod对象。并存入缓存serviceMethodCache中,防止多次创建

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

在build方法中,创建了一个调用适配器callAdapter对象,并且包含解析注解、判断请求方式、请求参数解析等等操作。此外如果hasBody==false并且isMultipart或者isFormEncoded为true,那么将会抛出methodError的错误,这也验证了之前的结论:如果定义的java接口参数为空,那么@FormUrlEncoded以及@Multipart需要去掉。最后build()方法会返回一个ServiceMethod。

public ServiceMethod build() {
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
      if (responseType == Response.class || responseType == okhttp3.Response.class) {
        throw methodError("'"
            + Utils.getRawType(responseType).getName()
            + "' is not a valid response body type. Did you mean ResponseBody?");
      }
      responseConverter = createResponseConverter();

      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }

      if (httpMethod == null) {
        throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
      }

      if (!hasBody) {
        if (isMultipart) {
          throw methodError(
              "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
        }
        if (isFormEncoded) {
          throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
              + "request body (e.g., @POST).");
        }
      }

      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p++) {
        Type parameterType = parameterTypes[p];
        if (Utils.hasUnresolvableType(parameterType)) {
          throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
              parameterType);
        }

        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
        if (parameterAnnotations == null) {
          throw parameterError(p, "No Retrofit annotation found.");
        }

        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }

      if (relativeUrl == null && !gotUrl) {
        throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
      }
      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
        throw methodError("Non-body HTTP method cannot contain @Body.");
      }
      if (isFormEncoded && !gotField) {
        throw methodError("Form-encoded method must contain at least one @Field.");
      }
      if (isMultipart && !gotPart) {
        throw methodError("Multipart method must contain at least one @Part.");
      }

      return new ServiceMethod<>(this);
    }

再看看createCallAdapter()方法,

   private CallAdapter<?> createCallAdapter() {
      Type returnType = method.getGenericReturnType();
      if (Utils.hasUnresolvableType(returnType)) {
        throw methodError(
            "Method return type must not include a type variable or wildcard: %s", returnType);
      }
      if (returnType == void.class) {
        throw methodError("Service methods cannot return void.");
      }
      Annotation[] annotations = method.getAnnotations();
      try {
        return 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);
      }
    }

上面的代码主要是将获取的methode的类型和注解,传入Retrofit的callAdapter(returnType, annotations)中,实际调用的是nextCallAdapter方法,nextCallAdapter通过传入的参数循环从adapterFactories集合里面取出第一个不为null的CallAdapter,CallAdapter可以通过DefaultCallAdapterFactory和ExecutorCallAdapterFactory工厂模式生成。Retrofit初始化build时默认创建了一个ExecutorCallAdapterFactory。

 public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
  }
  
  public CallAdapter<?> nextCallAdapter(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<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }
    .......
  }

loadServiceMethod分析完了,那么接下来return serviceMethod.callAdapter.adapt(okHttpCall)返回的为什么是okHttpCall/ExecutorCallbackCall呢?由上分析serviceMethod创建的callAdapter其实是ExecutorCallAdapterFactory工厂类返回的Call的衍射类ExecutorCallbackCall。DefaultCallAdapterFactory中adapt返回的是参数自身,即OkhttpCall

@Override public <R> Call<R> adapt(Call<R> call) {
   return new ExecutorCallbackCall<>(callbackExecutor, call);
 }
  • enqueue:调用okhttp中的enquenue方法,异步请求,onResponse和onFailure处于ui线程
    通过ExecutorCallbackCall调用的enquenue,最终调用的还是传入的OKhttpCall的对象delegate的enqueue方法。
delegate.enqueue()

所以不管是通过ExecutorCallbackCall还是okHttpCall,最终调用的都是okHttpCall中的enqueue方法

@Override public void enqueue(final Callback<T> callback) {
    if (callback == null) throw new NullPointerException("callback == null");

    okhttp3.Call call;
  ......

      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          failure = creationFailure = t;
        }
      }
    }
  
  ......
  
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }
        callSuccess(response);
      }
   ......
  }

上面代码主要可分三部分说明。第一部分,生成一个带请求信息Request的Okhttp的Call对象

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

在createRawCall,调用serviceMethod中的toRequest方法去生成请求信息。

  Request toRequest(Object... args) throws IOException {
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
        contentType, hasBody, isFormEncoded, isMultipart);

    @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

    int argumentCount = args != null ? args.length : 0;
    if (argumentCount != handlers.length) {
      throw new IllegalArgumentException("Argument count (" + argumentCount
          + ") doesn't match expected count (" + handlers.length + ")");
    }

    for (int p = 0; p < argumentCount; p++) {
      handlers[p].apply(requestBuilder, args[p]);
    }

    return requestBuilder.build();
  }

第二部分,调用okhttp的enqueue请求

call.enqueue(new okhttp3.Callback() {......}

第三部分,解析转换返回的数据

Response<T> response;
 try {
   response = parseResponse(rawResponse);
 } catch (Throwable e) {
   callFailure(e);
   return;
 }
 callSuccess(response);

在parseResponse中,将结果解析,并且调用serviceMethod.toResponse去交给转换器转换为对应的数据类。

 Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
  T body = serviceMethod.toResponse(catchingBody);
}

####ServiceMethod####
  /** Builds a method return value from an HTTP response body. */
  T toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
  }

到此,整个Retrofit就分析完了,Retrofit不仅使用注解的方式,让网络请求更简单方便,而且因为其使用了大量的设计模式去封装,所以解耦也是非常棒的。

四、总结

整个流程,简单复习下:

  • build:创建retrofit,能配置url,rxjava, gson变换器等。

  • create: 用retrofit加工出对应的接口实例对象,一个代理对象。

  • 调用对象(接口)中的方法,获取适配转换Call(ExecutorCallbackCall/OkhttpCall)对象。
    当调用接口中的方法时,会被代理对象拦截,调用到代理中的方法解析invoke,并将方法method转为serviceMethod,并传递给新创建的OkHttpCall对象,并通过adapt(ExecutorCallAdapterFactory,DefaultCallAdapterFactory)返回OkHttpCall]或衍生类(ExecutorCallbackCall,OkhttpCall).

serviceMethod:里面通过解析注解,请求参数等等通过toRequest提供request给OkHtppCall以以便请求网络
OkHtppCall:底层是OkHttp实现,OkHtppCall通过serviceMethod提供的request获取请求后,将结果通过toResponse传给serviceMethod做数据转换(如gson).

  • call.enqueue,调用okhttp中的enquenue方法,异步请求,同serviceMethod提供的toResponse解析数据。onResponse和onFailure处于ui线程。

PS:上面是更具retrofit:2.0.2源码做的分析。retrofit:2.5.0 的话中,serviceMethod被中的变量和方法,被httpserviceMethod,RequestFactory等用设计模式再次封装了,解耦更好

参考:
1.网络加载框架 - Retrofit
2.Retrofit2 完全解析 探索与okhttp之间的关系
3.Retrofit分析-漂亮的解耦套路

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值