深入理解Retrofit2

(借鉴自Android进阶之光)


我们从3个角度切入

1.Retrofit的创建过程

2.Call的创建过程

3.Call的enqueue方法


1.Retrofit的创建过程

网络接口

public interface LoginAPI {
    @GET("LoginServlet")
    Observable<JSONObject> login(
            @Query("user_name") String userName,
            @Query("user_password") String userPassword);
}
创建Retrofit

new Retrofit.Builder()
        .baseUrl(baseUrl)
        .addConverterFactory(GsonConverterFactory.create())
        .build()

Retrofit是通过建造者模式构建出来的,所以看一下Builder方法做了什么

public Builder() {
  this(Platform.get());
}
再查看Platform.get()方法,最终会调用到下面的findPlatform()方法,这个方法的作用是检测你的运行平台。返回了运行平台之后,再根据不同的运行平台来提供不同的线程池。

class Platform {
  private static final Platform PLATFORM = findPlatform();

  static Platform get() {
    return PLATFORM;
  }

  private static Platform findPlatform() {
    try {
      Class.forName("android.os.Build");
      if (Build.VERSION.SDK_INT != 0) {
        return new Android();
      }
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("java.util.Optional");
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("org.robovm.apple.foundation.NSObject");
      return new IOS();
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform();
  }
看一下build方法

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);
}
最上面验证了一个常识,baseUrl不能为空。

接下来那个callFactory。如果你之前有传进来callFactory,那么就用你传进来的callFactory。如果你没有,就new一个OkHttpClient()对象。所以我们得知了,如果你需要设置OkHttpClient,你只需要传进去一个你设置好的的OkHttpClient即可。就像这样Retrofit.Builder().callFactory(...)

下面的callbackExecutor用处是把回调传递到UI线程!这个你得关注下。
adapterFactories主要用来存储对Call进行转化的对象。
converterFactories主要用来存储转化数据对象,这里的addConverterFactory(GsonConverterFactory.create())就是添加了转化成Gson的支持。


2.Call的创建过程
retrofit.create(LoginAPI.class)
看看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() {
        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);
        }
      });
}
别看这么长,都是长在了return里啊!他这里采用的是动态代理。具体讲静态和动态代理就不讲了,就简单讲下。(代理是为了扩充功能的,比如你原来的方法功能不够, 你又不能修改这个方法,那么用代理就重新写个方法,给他前后加上逻辑。简单来说就是这样。讲多一点,就是能修改也不要修改,你要保证功能的原子性)所以呢,我们执行LoginApi里的login方法,其实等同于执行了这个invoke方法了(这个invoke方法就是扩展功能后的方法了)。我们讲了一串废话,其实只需要重点关注下loadServiceMethod方法即可。

private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();
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;
}
先通过result = serviceMethodCache.get(method);看看这个方法有没有缓存,之前有没有调用过(实际上,map是一个源码中经常用到的缓存器)。如果没有缓存,就创建一个并缓存下来。

看一下这个创建的方法
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);
}
第一行的callAdapter最终会得到我们再Retrofit的build过程中adapterFactroies中添加进去的platform.defaultCallAdapterFactory(callbackExecutor)方法的get方法。defaultCallAdapterFactory指的是ExecutorCallAdapterFactory。

看一下ExecutorCallAdapterFactory的get方法
@Override
public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
  if (getRawType(returnType) != Call.class) {
    return null;
  }
  final Type responseType = Utils.getCallResponseType(returnType);
  return new CallAdapter<Call<?>>() {
    @Override public Type responseType() {
      return responseType;
    }

    @Override public <R> Call<R> adapt(Call<R> call) {
      return new ExecutorCallbackCall<>(callbackExecutor, call);
    }
  };
}
会返回一个CallAdapter对象,这里的responseType方法会返回数据的真实类型。比如我们的LoginApi传入的是JSONObject,那么responseType返回的就是JSONObject类。而下面的adapt方法会创建ExecutorCallbackCall,它会把call的回调转发到UI线程,这样你就可以在回调完成后在UI线程里更新UI了,这个我们之前也提到过。


继续看ServiceMethod的build方法。responseConverter = createResponseConverter();这段代码把GsonConverterFactory(Converter.Factory的子类)添加到converterFactories列表中,表示返回的数据是支持转化成JSON对象的。
然后
for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }
实际上就是遍历一下方法的注解,解析一下方法的注解,看看我们到底是用的啥注解,啥请求(比如GET、POST)。
那么最后一个代码块不用说也是判断如QUERY、PART这种注解了。

最后回头看看Retrofit的create方法。
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
new了一个OkHttpCall的对象,把他传入adapt中。前面提到,adapt会创建ExecutorCallbackCall,并传入OkHttpCall。所以看一下ExecutorCallbackCall的代码。
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
  this.callbackExecutor = callbackExecutor;
  this.delegate = delegate;
}

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

  delegate.enqueue(new Callback<T>() {
    @Override public void onResponse(Call<T> call, final Response<T> response) {
      callbackExecutor.execute(new Runnable() {
        @Override public void run() {
          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(new Runnable() {
        @Override public void run() {
          callback.onFailure(ExecutorCallbackCall.this, t);
        }
      });
    }
  });
}
成功,失败回调都在这里了。所以我们可以得知,ExecutorCallbackCall是对于Call的封装。它通过callbackExecutor把请求回调到UI线程。当我们得到Call对象后会调用它的enqueue方法,其实调用的是ExecutorCallbackCall的enqueue。而delegate.enqueue显示最终调用的是delegate(传入的OkHttpCall)的enqueue方法。


3.Call的enqueue方法
所以根据上面所说,直接看OkHttpCall的enqueue方法即可
这里调用了okhttp3.Call的call的enqueue方法
call.enqueue(new okhttp3.Callback() {
parseResponse这里调用了parseResponse方法
看看这个方法
int code = rawResponse.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();
  }
}

if (code == 204 || code == 205) {
  return Response.success(null, rawResponse);
}

ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
  T body = serviceMethod.toResponse(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;
}
根据返回的不同状态码code值做不同的操作。正常情况会调用到T body = serviceMethod.toResponse(catchingBody);看一下toResponse
/** Builds a method return value from an HTTP response body. */
T toResponse(ResponseBody body) throws IOException {
  return responseConverter.convert(body);
}
这个responseConverter就是之前ServiceMethod的build方法调用的createResponseConverter方法返回的Converter。我们传入的是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);
}
它的responseBodyConverter方法,最终会创建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();
    }
  }
}
这里会把回调的数据转换成JSON格式。

Call的enqueue方法主要做的是用OkHttp来请求网络,把返回的Response进行数据转换并回调给UI线程。所以就是对于OkHttp的封装。

讲的有点乱,最后贴3个图帮助理解。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值