zoukankan      html  css  js  c++  java
  • Android进阶必学retrofit源码解析

    源码的分析将从基本的使用方法入手,分析retrofit的实现方案,以及其中涉及到的一些有趣的技巧。

    ‘Android技术交流群653583088,欢迎大家加入交流,畅谈!本群有免费学习资料视频’并且免费分享retrofit源码解析视频

    简单使用

    定义HTTP API

    
    public interface GitHubService {
      @GET("users/{user}/repos")
      Call<List<Repo>> listRepos(@Path("user") String user);
    }

    创建Retrofit并生成API的实现

    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://api.github.com/")
        .build();
    GitHubService service = retrofit.create(GitHubService.class);

    调用API方法,生成Call

    Call<List<Repo>> repos = service.listRepos("octocat");
    

    Retrofit的创建

    retrofit实例的创建,使用了builder模式,从下面的源码中可以看出。

    public static final class Builder {
    	Builder(Platform platform) {
    		this.platform = platform;
    		converterFactories.add(new BuiltInConverters());
    	}
    	public Builder() {
    		// Platform.get()方法可以用于判断当前的环境
    		this(Platform.get());
    	}
    	public Builder baseUrl(String baseUrl) {
          checkNotNull(baseUrl, "baseUrl == null");
          HttpUrl httpUrl = HttpUrl.parse(baseUrl);
          if (httpUrl == null) {
            throw new IllegalArgumentException("Illegal URL: " + baseUrl);
          }
          return baseUrl(httpUrl);
        }
    	
    	public Retrofit build() {
          if (baseUrl == null) {
            throw new IllegalStateException("Base URL required.");
          }
          okhttp3.Call.Factory callFactory = this.callFactory;
          if (callFactory == null) {
            callFactory = new OkHttpClient();// 新建Client,留到之后newCall什么的
          }
          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);
        }
    }

    retrofit.create

    好玩的地方开始了,我们先来看看这个方法。

    public <T> T create(final Class<T> service) {
      Utils.validateServiceInterface(service);
      if (validateEagerly) {
        eagerlyValidateMethods(service);
      }
      // 动态代理,啦啦啦
      return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
          new InvocationHandler() {
            // platform 可以分辨出你是在android,还是java8,又或者别的
            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.
              // 这里的invoke,Object方法都走这里,比如equals、toString、hashCode什么的
              if (method.getDeclaringClass() == Object.class) {
                return method.invoke(this, args);
              }
              // java8默认方法,1.8的新特性
              if (platform.isDefaultMethod(method)) {
                return platform.invokeDefaultMethod(method, service, proxy, args);
              }
              // 这里是核心代码了
              ServiceMethod<Object, Object> serviceMethod =
                  (ServiceMethod<Object, Object>) loadServiceMethod(method);
              OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
              return serviceMethod.callAdapter.adapt(okHttpCall);
            }
          });
    }

    可以看出创建API使用了动态代理,根据接口动态生成的代理类,将接口的都转发给了负责连接代理类和委托类的InvocationHandler实例,接口方法也都通过其invoke方法来处理。
    invoke方法中,首先会通过Platform.get()方法判断出当前代码的执行环境,之后会先把Object和Java8的默认方法进行一个处理,也是在进行后续处理之前进行去噪。其中的关键代码其实就是最后三句,这也是这篇文章将要分析的。

    创建ServiceMethod

    erviceMethod<?, ?> loadServiceMethod(Method method) {
      // 从缓存里面取出,如果有的话,直接返回好了
      ServiceMethod<?, ?> result = serviceMethodCache.get(method);
      if (result != null) return result;
      synchronized (serviceMethodCache) {
        result = serviceMethodCache.get(method);
        if (result == null) {
          // 为null的话,解析方法的注解和返回类型、参数的注解he参数类型,新建一个ServiceMethod
          result = new ServiceMethod.Builder<>(this, method).build();// ->
          // 新建的ServiceMethod加到缓存列表里面
          serviceMethodCache.put(method, result);
        }
      }
      return result;
    }

    注解的解析

    CallAdapterConverter等到后面再分析,这里先看看parseMethodAnnotation(annotation),功能和其名字一样,其对方法注解进行了解析。

    /**
     * 解析方法注解,呜啦啦
     * 通过判断注解类型来解析
     * @param annotation
     */
    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);
      } 
      // 其他的一些方法注解的解析
      ...
    }
    private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
      if (this.httpMethod != null) {// 已经赋值过了
        throw methodError("Only one HTTP method is allowed. Found: %s and %s.",
            this.httpMethod, httpMethod);
      }
      this.httpMethod = httpMethod;
      this.hasBody = hasBody;
      // value为设置注解方法时候,设置的值,官方例子中的users/{user}/repos or user
      if (value.isEmpty()) {
        return;
      }
      // 查询条件的一些判断
        ...
      this.relativeUrl = value;
      this.relativeUrlParamNames = parsePathParameters(value);
    }
    `

    在解析注解时,先通过instanceof判断出注解的类型,之后调用parseHttpMethodAndPath方法解析注解参数值,并设置httpMethod、relativeUrl、relativeUrlParamNames等属性。

    上面说了API中方法注解的解析,现在来看看方法参数注解的解析,这是通过调用parseParameterAnnotation方法生成ParameterHandler实例来实现的,代码比较多,这里挑选@Query来看看。

    else if (annotation instanceof Query) {
    Query query = (Query) annotation;
    String name = query.value();
    boolean encoded = query.encoded();
    Class<?> rawParameterType = Utils.getRawType(type);// 返回基础的类
    gotQuery = true;
    // 可以迭代,Collection
    if (Iterable.class.isAssignableFrom(rawParameterType)) {
      if (!(type instanceof ParameterizedType)) {
    	throw parameterError(p, rawParameterType.getSimpleName()
    		+ " must include generic type (e.g., "
    		+ rawParameterType.getSimpleName()
    		+ "<String>)");
      }
      ParameterizedType parameterizedType = (ParameterizedType) type;
      Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);// 返回基本类型
      Converter<?, String> converter =
    	  retrofit.stringConverter(iterableType, annotations);
      return new ParameterHandler.Query<>(name, converter, encoded).iterable();
    } else if (rawParameterType.isArray()) {// Array
      Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());// 如果是基本类型,自动装箱
      Converter<?, String> converter =
    	  retrofit.stringConverter(arrayComponentType, annotations);
      return new ParameterHandler.Query<>(name, converter, encoded).array();
    } else {// Other
      Converter<?, String> converter =
    	  retrofit.stringConverter(type, annotations);
      return new ParameterHandler.Query<>(name, converter, encoded);
    }

    在@Query中,将分成Collection、array、other三种情况处理参数,之后根据这些参数,调用ParameterHandler中的Query静态类,创建出一个ParameterHandler实例。这样循环直到解析了所有的参数注解,组合成为全局变量parameterHandlers,之后构建请求时会用到

    OkHttpCall

    ServiceMethod创建完成之后,我们来看看下一行代码中的OkHttpCall类,里面的包含了请求的执行和响应处理,我们来看看异步请求的做法

    
     
    OkHttpCall(ServiceMethod<T, ?> serviceMethod, Object[] args) {
      this.serviceMethod = serviceMethod;
      this.args = args;
    }
    @Override public void enqueue(final Callback<T> callback) {
    checkNotNull(callback, "callback == null");
    okhttp3.Call call;
    Throwable failure;
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;
      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
    	try {
    	  call = rawCall = createRawCall();// 创建OkHttp3.Call
    	} catch (Throwable t) {
    	  failure = creationFailure = t;
    	}
      }
    }
    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }
    if (canceled) {
      call.cancel();
    }
    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);
      }
      @Override public void onFailure(okhttp3.Call call, IOException e) {
    	try {
    	  callback.onFailure(OkHttpCall.this, e);
    	} catch (Throwable t) {
    	  t.printStackTrace();
    	}
      }
      private void callFailure(Throwable e) {
    	try {
    	  callback.onFailure(OkHttpCall.this, e);
    	} catch (Throwable t) {
    	  t.printStackTrace();
    	}
      }
      private void callSuccess(Response<T> response) {
    	try {
    	  callback.onResponse(OkHttpCall.this, response);
    	} catch (Throwable t) {
    	  t.printStackTrace();
    	}
      }
    });
    }
    private okhttp3.Call createRawCall() throws IOException {
      Request request = serviceMethod.toRequest(args);// 根据ParameterHandler组装Request.Builder,生成Request
      okhttp3.Call call = serviceMethod.callFactory.newCall(request);// Retrofit中创建的new OkHttpClient().newCall(request)
      ...
      return call;
    }

    CallAdapter

    现在来看看enqueue传入的参数callback,这个参数可能和很多人心中想的并不一样,它并不是用户在使用时传入的那个Callback对象。那么他是从哪里来的呢?不知道你还记不记得我之前在Retrofit.Builder.build()方法中提到过一句代码Platform.get()。在不使用addCallAdapterFactory的情况下。将会使用Platform的一种内部类,在Android环境下将会使用到Android类(这其实是个策略模式)

    static class Android extends Platform {
      @Override public Executor defaultCallbackExecutor() {
        return new MainThreadExecutor();
      }
      @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
        return new ExecutorCallAdapterFactory(callbackExecutor);
      }
      static class MainThreadExecutor implements Executor {
    	// Looper.getMainLooper()就是为嘛响应会在主线程的原因
        private final Handler handler = new Handler(Looper.getMainLooper());
        @Override public void execute(Runnable r) {
          handler.post(r);
        }
      }
    }

    上面的代码先稍微放一下,我们继续看retrofit.Bulider.build,其中有几句比较关键的代码

    callFactory = new OkHttpClient();
    callbackExecutor = platform.defaultCallbackExecutor();
    adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
    

    结合Android类中的代码可以看出,其最后生成了ExecutorCallAdapterFactory类。虽然看到了CallAdapter.Factory,但是到底是哪里执行了enqueue方法呢?现在我们来看看retrofit.create的最后一句代码serviceMethod.callAdapter.adapt(okHttpCall)
    Converter

    现在回到OkhttpCall.enqueue方法中,在其中还有一句重要的代码没有看,那就是response = parseResponse(rawResponse);,我们来看看这其中做了什么。

    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 th
      rawResponse = rawResponse.newBuilder()
          .body(new NoContentResponseBody(rawBody.contentType(), rawBody.conte
          .build();
      ...
      ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
      try {
        T body = serviceMethod.toResponse(catchingBody);// 解析body,比如Gson解析
        return Response.success(body, rawResponse);
      } catch (RuntimeException e) {
        // If the underlying source threw an exception, propagate that rather 
        // a runtime exception.
        catchingBody.throwIfCaught();
        throw e;
      }
    }
    ### ServiceMethod
    R toResponse(ResponseBody body) throws IOException {
      return responseConverter.convert(body);
    }

    可以看出parseResponse最终调用了Converter.convert方法。这里以常用的GsonConverterFactory为例。

    # GsonConverterFactory
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
        Retrofit retrofit) {
      TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
      return new GsonResponseBodyConverter<>(gson, adapter);
    }
    # GsonResponseBodyConverter
    final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
      private final Gson gson;
      private final TypeAdapter<T> adapter;
      GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
        this.gson = gson;
        this.adapter = adapter;
      }
      @Override public T convert(ResponseBody value) throws IOException {
        JsonReader jsonReader = gson.newJsonReader(value.charStream());
        try {
          return adapter.read(jsonReader);
        } finally {
          value.close();
        }
      }
    }

    responseBodyConverter方法中用到的type参数就是之前我在CallAdapter中提到的responseType方法的返回值。生成adapter方法,用于convert方法使用。OkHttpCall在这之后的代码就比较简单了,通过回调将转换后得响应数据发送出去即可

    本文分析了Retrofit的执行流程,其实包含了Retrofit、ServiceMethod、OkHttpCall、CallAdapter、Converter等方面。Retrofit的代码相对是比较少,也比较容易理解的,不过却是很好的架构实例。

    ‘Android技术交流群653583088,欢迎大家加入交流,畅谈!本群有免费学习资料视频’并且免费分享retrofit源码解析视频

  • 相关阅读:
    STL(五)list
    WinCE进程ID获取窗口句柄
    【转】.obj, .lib, .dll, .exe的关系
    《Windows核心编程》——线程
    【转】windows结束线程方式
    VS2005调试多进程
    【转】VC中常用的宏
    【转】C++标准库简介
    WinCE下用STL的奇怪问题
    STL(三)string
  • 原文地址:https://www.cnblogs.com/876013676ch/p/10187272.html
Copyright © 2011-2022 走看看