美文网首页
retrofit原理

retrofit原理

作者: 慕尼黑凌晨四点 | 来源:发表于2021-03-01 15:30 被阅读0次

    前言:Retrofit是基于OkHttp封装的一个网络请求框架,既然是OkHttp的上层框架,当然是解决了OkHttp在开发中的一些痛点。具体的来说:

    1. 使用过程中接口配置繁琐,OkHttp中每发起一个请求都要新建一个Request,当要配置复杂请求(body,请求头,参数)
      时尤其复杂。
    2. 需要用户拿到responseBody后自己手动解析,解析工作应该是可以封装的。
    3. 无法适配自动进行线程切换。
    4. 嵌套网络请求会陷入“回调陷阱”

    Retrofit基本使用

    依赖:

    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'//数据解析(可选)
    implementation 'com.squareup.retrofit2:adapter-rxjava:2.9.0'//RxJava适配器(可选)
    

    构建接口Service:

    public interface  ApiService {
        @GET("users/{users}")
        Call<UserBean> getUserData(@Path("users") String users);
    }
    

    使用:

    static Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://gitee.com/api/v5/")
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build();
    
    final ApiService service = retrofit.create(ApiService.class);
    
    final Call<UserBean> result = service.getUserData("caoyangim");
    
    result.enqueue(new Callback<UserBean>() {
        public void onResponse(Call<UserBean> call, Response<UserBean> response) {
            if (response.isSuccessful()){
                System.out.println("success:"+response.body());
            }
        }
        public void onFailure(Call<UserBean> call, Throwable throwable) {
            System.out.println("ERROR:"+throwable.toString());
        }
    });
    

    注解

    • get
    • post
    • put
    • delete
    • patch
    • head
    • options
    • http 通用注解,可以替换以上所有注解,其拥有method,path,hasbody三个属性

    源码解析

    构建Retrofit网络实例对象

    在创建retrofit网络实例对象的时候用到了build建造者模式【一般 配置参数大于5个 & 参数可选 就要用build模式了】,代码如下:

    public Retrofit build() {
        if (baseUrl == null) {
          throw new IllegalStateException("Base URL required.");
        }
    
        okhttp3.Call.Factory callFactory = this.callFactory;
        //默认只支持OKHttpClient,不支持httpUrlConnection和httpClient
        //不过你也可以自己构建一个HTTPClient,传给callFactory
        if (callFactory == null) {
          callFactory = new OkHttpClient();
        }
        
    
        //为线程切换准备了一个execute,可以点进去看,这个Executor在Android平台下其实就是Handler
        Executor callbackExecutor = this.callbackExecutor;
        if (callbackExecutor == null) {
          callbackExecutor = platform.defaultCallbackExecutor();
          /**
           * 返回的excutor就是这个:
           
            static final class MainThreadExecutor implements Executor {
                private final Handler handler = new Handler(Looper.getMainLooper());
                @Override
                public void execute(Runnable r) {handler.post(r);}
            }
            
          **/
            
        }
    
        //网络请求适配工厂的集合,构造时传入的RxJavaCallAdapter会传到这里面来
        List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
        callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
    
        //数据转换工厂的集合,传入的GsonAdapter到这里来
        List<Converter.Factory> converterFactories =
            new ArrayList<>(
                1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
        
        ...
    
        return new Retrofit(
            callFactory,
            baseUrl,
            unmodifiableList(converterFactories),
            unmodifiableList(callAdapterFactories),
            callbackExecutor,
            validateEagerly);
      }
    }
    

    TIP:Retrofit在这里提供了一个统一的入口,所有需要的东西都通过build模式往里面加就行了,用的时候也是用retrofit.create()用,所以这里也用到了“外观(门面)模式”

    retrofit.create()

    public <T> T create(final Class<T> service) {
      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.
                  if (method.getDeclaringClass() == Object.class) {
                    return method.invoke(this, args);
                  }
                  args = args != null ? args : emptyArgs;
                  return platform.isDefaultMethod(method)
                      ? platform.invokeDefaultMethod(method, service, proxy, args)
                      : loadServiceMethod(method).invoke(args);
                }
              });
    }
    

    这里面用到了动态代理的技术,你传入一个带有获取数据方法的接口,这个方法帮你动态的生成一个代理类继承该接口,这样一来所有的获取数据的方法都走这里的invoke函数,就可以拦截调用函数的执行,从而将网络接口的参数配置归一化。

    即你每次发起网络请求看似是走的自己定义的接口,实际上它是会走到这个方法中来的。

    然后往下一般会走loadServiceMethod(method)方法,此方法的作用是解析传入的这个方法,主要是通过解析它的注解获取请求路径、传递参数等属性,返回的是一个ServiceMethod对象。

    loadServiceMethod

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

    从这个方法可以看出有个缓存机制,解决了每次都使用反射带来的效率问题。

    ServiceMethod/HttpServiceMethod

    image-20210221151029435.png

    里面还有几个属性:

    abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
        ...
      private final RequestFactory requestFactory;
      private final okhttp3.Call.Factory callFactory;
      private final Converter<ResponseBody, ResponseT> responseConverter;
    }
    
    image-20210221151159987.png

    retrofit构建了所有的网络请求共同的一些属性,而serviceMethod对应着某个单独的网络请求,其内部属性更加具体化。

    请求数据-getUserData

    因为用到了动态代理模式,所有不管你接口中写了什么自定义的请求方法,他都会被转到动态代理的类中来:即上面说到的loadServiceMethod():

    return platform.isDefaultMethod(method)
                        ? platform.invokeDefaultMethod(method, service, proxy, args)
                        : loadServiceMethod(method).invoke(args);
    

    loadServiceMethod中会走到parseAnnotations中,返回了一个ServiceMethod对象:

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

    返回值的类型根据callAdapter来定,一般可以用defalultCallAdapter,RxJavaCallAdapter 或者也可以自定义callAdapter,只需重写adapt方法即可:收到的是call,根据自己的需求返回即可。

    enqueue

    enqueue最终是调用到了OkHttpCall.enqueue方法中来,在这里面实际上是用到了okhttp的相关方法,比如createRawCall():

    private okhttp3.Call createRawCall() throws IOException {
      okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
      if (call == null) {
        throw new NullPointerException("Call.Factory returned null.");
      }
      return call;
    }
    

    返回的是okHttp3中的call,然后用这个call:

    call.enqueue(
        new okhttp3.Callback() {
          @Override
          public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
            Response<T> response;
            try {
              response = parseResponse(rawResponse);
            } catch (Throwable e) {
              throwIfFatal(e);
              callFailure(e);
              return;
            }
    
            try {
              callback.onResponse(OkHttpCall.this, response);
            } catch (Throwable t) {
              throwIfFatal(t);
              t.printStackTrace(); // TODO this is not great
            }
          }
    
          @Override
          public void onFailure(okhttp3.Call call, IOException e) {
            callFailure(e);
          }
    
          private void callFailure(Throwable e) {
            try {
              callback.onFailure(OkHttpCall.this, e);
            } catch (Throwable t) {
              throwIfFatal(t);
              t.printStackTrace(); // TODO this is not great
            }
          }
        });
    

    这些都是okhttp3的用法,被封装到了retrofit中。

    再在返回结果的时候用到callbackExecutor.execute来切换了线程,executor实际上是:

    static final class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());
    
      @Override
      public void execute(Runnable r) {
        handler.post(r);
      }
    }
    

    设计模式

    retrofit中用到了门面(外观)模式,建造者模式,代理模式(动态代理)等设计模式的思想。

    retrofit中你可以通过很多组件式的方式使用,比如callAdapter的选择上,可以选择java8的,可以选择RxJava/RxJava2的,scala的方式来编码;获取数据后数据转换方面(convert),可以使用gson ,jackson,moshi甚至xml的方式,都可以自己根据需求选择。这些可以自定义的来组合使用,即外观模式。

    建造者模式和代理模式分别在业务代码和源码中可以体现出来。

    相关文章

      网友评论

          本文标题:retrofit原理

          本文链接:https://www.haomeiwen.com/subject/ovsafltx.html