Retrofit2框架学习

一、Retrofit概述

Retrofit是一款开源网络框架,由Square公司推出的,目前最新版本是Retrofit2,源码地址 https://github.com/square/retrofit

Retrofit的推出,主要是为了对Square公司推出的另一款网络请求框架OKHttp做封装。(Retrofit并不直接发起网络请求,网络请求仍然是由OkHttp发起的。)

Retrofit2+OkHttp3的使用,已经成为Android最主流的网络请求框架,此外还可以再配合RxJava、Dagger2等,使用起来非常强大。

本文简要分析Retrofit2的源码,及记录自己的一些理解。
Retrofit2源码量很少,所以阅读起来还是稍容易的,只要对动态代理,Java注解等有一定基础。

看下它的源码类:

Retrofit2框架学习_第1张图片Retrofit2框架学习_第2张图片

上面就是Retrofit2的所有源码类了,其中“http”包下所有的都是注解类,占了一大半,挑一两个看看即可。重点看下Retrofit处理网络请求流程相关的类。

还是先从如何使用说起:

二、Retrofit2的使用

如果不对Retrofit2做封装,以发起登录请求为例,
假设登录请求的地址为http://192.168.1.1/user/login,参数包括账号和密码,那么得这么使用:

1.首先创建网络请求接口
创建一个Interface,按Retrofit习惯,后缀为Service。这里就叫LoginService吧:

//LoginService.java

public interface LoginService {
    //登录
    @POST("user/login")
    Call<UserBean> login(@Field("account") String account, @Field("pwd") String pwd);
    
    //注册
    @POST("user/register")
    Call<UserBean> register(@Field("account") String account, @Field("pwd") String pwd);
}

这里@POST注解表示这是post请求,@Field注解表示这个参数将会放到请求body里。
UserBean是我们自定义的表示用户信息的Java bean类,包括用户名,ID等信息,这里就不贴代码了。登录成功后返回的数据将会解析为UserBean类型。

2.创建Retrofit对象

Retrofit retrofit = new Retrofit.Builder()
	//设置请求通用地址,必选
	.baseUrl("http://192.168.1.1/")
	//添加解析器,可选。如果不添加,则结果只能解析为ResponseBody类型,需自己解析为UserBean。
	.addConverterFactory(GsonConverterFactory.create())
	//添加Retrofit对Rxjava的支持,可选
	.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
	//配置处理请求的client,必选
	.client(getOkHttpInstance())
	.build();

3.得到Call对象(Call代表网络请求)

Call<UserBean> call = retrofit.create(LoginService.class).login("Jack", "123456");

4.发起网络请求,处理响应结果

call.enqueue(new Callback<UserBean>() {
            @Override
            public void onResponse(Call<UserBean> call, Response<UserBean> response) {
                //响应成功
                UserBean user = response.body();
            }

            @Override
            public void onFailure(Call<UserBean> call, Throwable t) {
				//响应失败
            }
        });

三、Retrofit2流程解析

1.创建网络请求接口
Retrofit2使用流程的第1步不细讲了,是准备工作。

2.创建Retrofit对象
创建Retrofit对象,用到了建造者模式,baseUrl必须传,另外可以按需添加Json解析器,添加RxJava的适配器等。

Retrofit的builder的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);
}

3.得到Call对象(Call代表网络请求)
重点是这第三步。看了使用过程我们可能会有疑问,LoginService只是个Interface,一般都是要有这个Interface的实现类,才能创建对象的。但是使用Retrofit2,我们不用自己实现,它帮我们实现了LoginService对象。而且用这个LoginService对象调用了登录方法,得到了Call对象。

Retrofit2是怎么实例化LoginService的呢?
我们从Retrofit.java的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);
        }
      });
}

从上面的代码可看到,用到了动态代理来处理请求。

对于代理,可以理解为创建一个中间者去访问目标,这个中间者就称为代理。这样做的好处就是,我们可以在代理访问目标前后,插入我们自定义的处理,比如用来插入日志打印,判断拦截等等。

如果是静态代理的方式,需要我们为每一个目标对象创建一个代理对象。
而动态代理的方式,则可以用同一个代理去处理不同的目标对象。也就是要代理的目标对象是动态确定的。

对于动态代理,这里就不过多分析了。要知道的是,Proxy.newProxyInstance()方法,传入一个Interface,返回的就是该Interface的实例对象。这就是LoginService对象的创建由来。

而Proxy.newProxyInstance()方法还传入了一个new InvocationHandler()对象,这个对象是用来干嘛的呢? 它的作用就是,当我们调用目标对象的方法的时候,都会通过这个 InvocationHandler()的invoke()方法来调用。

因此当我们调用LoginService的login()方法的时候,实际上是通过这个InvocationHandler的invoke()方法来处理的。

这个invoke()方法,一般都会走到最后几句:

ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);

分为三行,第一行:
Retrofit 的 loadServiceMethod()方法:

ServiceMethod serviceMethod = loadServiceMethod(method);
  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;
  }

这里传入的method,在本例中就是login()这个Method。

可见ServiceMethod对象,同样是使用建造者模式创建的。ServiceMethod类用于描述我们创建的Interface里的方法的各个属性,注解属性,json转换器等。

看下这个简化后的 ServiceMethod.Builder#build()方法:

public ServiceMethod build() {
  callAdapter = createCallAdapter();
  responseType = callAdapter.responseType();
  
  XXX省略中间一些处理
  
  responseConverter = createResponseConverter();
  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation);
  }

  XXX省略中间一些处理
  return new ServiceMethod<>(this);
}

这里的parseMethodAnnotation(annotation)就是解析方法用到的各个注解的过程,这里就不看了。

而createCallAdapter()方法会遍历adapterFactories列表,通过逐个判断其方法的返回值,注解等,找个符合目标的那个adapter,这里也不细看了。

adapterFactories都会包含哪些adapterFactory呢?

可以点这里回去看看Retrofit的构建方法中,adapterFactories默认会添加defaultCallAdapterFactory对象。另外如果我们创建Retrofit对象的时候,如果传入了别的adapterFactory比如RxJavaCallAdapterFactory以支持Rxjava,那么也会添加到adapterFactories里。

另外,ServiceMethod的build()方法里还会确定一个responseConverter,和得到adapter的过程类似,会遍历converterFactories,找到合适的Converter。因为我们创建Retrofit对象的时候,添加了GsonConverterFactory,所以这里最终确定的responseConverter肯定是GsonResponseBodyConverter。这里就不细看了。

第二行,得到OkHttpCall对象。

OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);

注意这里的OkHttpCall类是Retrofit的类,不是OkHttp的。

第三行,对网络请求okHttpCall 进行处理。

return serviceMethod.callAdapter.adapt(okHttpCall);

这里serviceMethod 的 callAdapter,上面已经说过,是ServiceMethod建造时,build()方法里调用createCallAdapter()生成的。

看到这,可以点这里返回到动态代理相关的invoke()方法理一下思路。

接着看,得到adapter之后,会调用adapter.adapt(okHttpCall)。

这里假设我们用的是默认的adapter,没有使用RxJava,则看下DefaultCallAdapterFactory 类:

final class DefaultCallAdapterFactory extends CallAdapter.Factory {
  static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();

  @Override
  public CallAdapter<?> 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 call;
      }
    };
  }
}

到这里,再理一下那三行代码的作用:
就是这三行:

ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);

简而言之,其作用就是

解析所调用的请求方法(本例中是login方法)的注解等属性,找到能处理这个请求的adapter,json解析器等,并且得到OkHttpCall 对象。

4.发起网络请求,处理响应结果
这一步是通过Call.enqueue()方法把call加入请求队列的,看下enqueue()方法:

@Override public void enqueue(final Callback<T> callback) {
  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 {
        //创建OkHttp的call对象
        call = rawCall = createRawCall();
      } catch (Throwable t) {
        failure = creationFailure = t;
      }
    }
  }

  xxx省略一些检查

  call.enqueue(new okhttp3.Callback() {
    @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
        throws IOException {
      Response<T> response;
      try {
        //解析成目标Bean类
        response = parseResponse(rawResponse);
      } catch (Throwable e) {
        callFailure(e);
        return;
      }
      callSuccess(response);
    }

    xxx省略callSuccess()等方法的定义
  });
}

从OkHttpCall的enqueue(final Callback callback)方法可见,其内部是调用了OkHttp的Call类的enqueue()来处理请求的。这也就是本文开头说的Retrofit不会自己处理网络请求,而是交给OkHttp来处理。而OkHttp返回请求结果后,会由Retrofit的json解析器解析成目标Bean类,然后传入Retrofit的callback里,交给用户去处理

看下Retrofit是如何解析响应数据为Bean的:

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();
	  
  xxx省略对rawResponse.code()的检查

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

ServiceMethod的toResponse()方法:

T toResponse(ResponseBody body) throws IOException {
  return responseConverter.convert(body);
}

之前说到,responseConverter是在Retrofit的loadServiceMethod(Method method)里,建造者模式生成ServiceMethod对象的时候,遍历converterFactories得到的。可以点这里回顾一下。
本例中这里得到的responseConverter肯定是GsonResponseBodyConverter。

下面是GsonResponseBodyConverter的convert()方法:

  @Override public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      return adapter.read(jsonReader);
    } finally {
      value.close();
    }
  }

通过convert()方法,就把ResponseBody转换成目标Bean类型了。

这也是Retrofit的一个方便之处,一般我们处理网络请求数据的时候,解析得到的json字符串为为Java Bean这一步,通常由我们手动完成。
而使用Retrofit的话,在定义请求接口的时候需指定Java Bean的类型。Retrofit会将这步结果解析的步骤自动完成,最终回调里传入的是解析好的Java Bean了。

你可能感兴趣的:(Android)