背景

Android 6.0(SDK 23)推出后,Apache的HttpClient被弃用。之前一些常用的框架如xUtilsandroid-async-http都需要依赖HttpClient,现在用起来都不方便了。幸好有Retrofit补上这个漏,而且还被得更好。
Retrofit是Square公司出口的一个网络请求框架,本质上是对OkHttp的包装,非常适合Android平台使用。目前最新版本是2.0 beta2,所以下面所说的Retrofit都是指Retrofit 2.0。

使用样例

在看源码之前,我们先熟悉一下Retrofit的使用方式。下面介绍两种使用Retrofit的方式,分别是Call方式和RxJava方式。都是基于Gradle。在项目中创建项目之后,

  • Call方式

在项目的build.gradle文件中添加Retrofit的依赖,

compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'

创建实体类和一个接口,如下

public class GithubUser {
    public String login;
    public String avatar_url;
    public String company;
}

public interface GithubApi {
    @GET("users/{user}")
    Call<GithubUser> getUserInfoByName(@Path("user") String name);
}

初创化Retrofit对象,以及实例化GithubApi

Retrofit mRetrofit = new Retrofit.Builder()
        // 这里有一个坑
        // 如果项目不是部署在域名根目录下,则必须要以“/”结尾,否则出错
        // 在1.9以及之前,url是不能以“/”结尾的。
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create(gson))
    .client(mClient)
    .build();

GithubApi api = mRetrofit.create(GithubApi.class);

然后就可以请求数据了

api.getUserInfoByName("atanl").enqueue(new Callback<GithubUser>() {
      @Override
            public void onResponse(Response<GithubUser> response, Retrofit retrofit) {
        Toast.makeText(getApplicationContext(), response.body().login, Toast.LENGTH_SHORT).show();                  }
      @Override
      public void onFailure(Throwable t) {
          t.printStackTrace();
      }        
});

上面代码涉及了两个步骤,获取到一个Call实例,然后调用它的enqueue方法。其实在前一个步骤http请求是还没有发送的,只有调用的enqueue方法之后才开始请求。这和java基础多线程编程中的Call或者Future其实是一个概念。关于Call下面会有更多说明。

  • 使用RxJava配合的方式

和RxJava配合使用时,本质是对Call这种方式的一个包装,就是把Call实例转变成Observable对象。等下做更多的说明,先看一下样例代码。ps,如果不知道RxJava是什么鬼的童鞋,可以参考给 Android 开发者的 RxJava 详解

在上面的build.gradle的基础上再添加下面依赖,

compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2'
compile 'io.reactivex:rxandroid:1.0.1'

在生成Retrofit实例的时候,添加一个RxJava的CallAdapterFactory。如下,

Retrofit mRetrofit = new Retrofit.Builder()    
        .baseUrl("https://api.github.com/")
        .addConverterFactory(GsonConverterFactory.create(gson))
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .client(mClient)
        .build();

向GithubApi接口添加一个方法。如下,

public interface GithubApi {
    @GET("users/{user}")
    Call<GithubUser> getUserInfoByName(@Path("user") String name);
    
    GET("users/{user}")
    Observable<GithubUser> getUserInfoObservable(@Path("user") String name);
}

接下下来就是使用这个接口获取数据了,

api.getUserInfoObservable("atanl")    
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(user -> {
                Toast.make(getApplicationContext(), user.login + "", Toast.LENGHT_SHORT).show();
        });

上面代码中通过getUserInfoObservable获取得到的Observable其实就是通过RxJavaCallAdapterFactory对Call的一个转换。

Retrofit create方法的源码跟踪

看过上面的样例,不管是直接通过Call方式还是和RxJava配合使用,一个必经的步骤应是通过Retrofit的create方法来实例化一个接口实例(如GithubApi)。那么我们就一直看一下create方法是怎么工作的。

public <T> T create(final Class<T> service) {
        ... 这里省略了若干行代码
        
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service}, new InvocationHandler() { // 留意这个代理实例
    
        ... 这里省略了若干行代码
        
        @Override 
        public Object invoke(Object proxy, Method method, Object... args) throws Throwable {
                ... 这里省略了若干行代码
            return loadMethodHandler(method).invoke(args);
        }        
    });
}

上面代码说白了就是通过Java的动态代理机制,实例化一个实现了GithubApi的的代理客户端(即Proxy对象),然后调用GithubApi的方法时,操作会被代理到一个InvocationHandler中。如上面代码,对GithubApi中方法的访问其实都是代理到了最后一行代码

return loadMethodHandler(method).invoke(args);

很简单,意思就是去某个地方获取一个Handler出来,然后调用它来产生结果,然后作为接口方法的返回值。那么我们继续跟进loadMethodHandler,看一下load出来的MethodHandler是长什么样子的。

MethodHandler<?> loadMethodHandler(Method method) {
    MethodHandler<?> handler;
    synchronized (methodHandlerCache) {
          handler = methodHandlerCache.get(method);
          if (handler == null) {
                  handler = MethodHandler.create(this, method); // 这是是关键
                  methodHandlerCache.put(method, handler);
          }
    }
    return handler;
}

这个方法中可以看出,Retrofit会先在缓存中找下是不是已经有现在的MethodHandler实例可以获取,如果有就直接拿出来用。否则,create一个。我们先不用管它的缓存,只看create。所以继续,跟进create方法中,

static MethodHandler<?> create(Retrofit retrofit, Method method) {
    CallAdapter<Object> callAdapter = (CallAdapter<Object>) createCallAdapter(method, retrofit);
    Type responseType = callAdapter.responseType();
    Converter<ResponseBody, Object> responseConverter = (Converter<ResponseBody, Object>) createResponseConverter(method, retrofit, responseType);
    RequestFactory requestFactory = RequestFactoryParser.parse(method, responseType, retrofit);
    return new MethodHandler<>(retrofit, requestFactory, callAdapter, responseConverter);
}

这里有五行代码,每行都做了一件事。
第一,创建一个CallAdapter,这里是重点,等下详述。
第二,获取到接口(GithubApi)方法的返回值的泛型类型(两个方法都是GithubUser)。
第三,实例化一个数据结果的转换器,把Http请求所得的response转换成POJO。
第四,实例化一个请求工厂类,用于把带有@GET,@POST等注解的接口方法生成对的Http请求实例。
第五,返回我们想要的MethodHandler对象。

当我们拿到MethodHandler对象之后,就可以调用它的invoke方法来生成结果,并且接口方法的返回值。那么我们再看MethodHandler的invoke方法,

Object invoke(Object... args) {
    return callAdapter.adapt(new OkHttpCall<>(retrofit, requestFactory, responseConverter, args)); // 记住这个OkHttpCall类
}

只是很简单的调用之前第一步获取到的CallAdapter实例的adapt方法。方法名很朴实,也很应景,意思就是适配,把一个Call实例(即OkHttpCall对象)适配成一个Call实例(这种情况下,其实适配器就是直接返回Call实例,没有做什么转换),或者一个Observale实例(这种情况下,下面细说)。

这里涉及到两个关键点,一个是CallAdapater,一个是OkHttpCall。后者我们等下再说,先说CallAdapter。

CallAdapter只是一个接口,不能直接实例化。在Retrofit的体系中,所有的CallAdapter实例都是通过相应的CallAdapterFactory来产生的,如上面提到的RxCallAdapterFactory。

我们先回到MethodHandler.create方法的第一行代码,即创建一个CallAdapter。跟进代码,看一下CallAdapterFactory是怎么生产出一个CallAdapter来。

private static CallAdapter<?> createCallAdapter(Method method, Retrofit retrofit) {
    Type returnType = method.getGenericReturnType();
    ... // 这里省略了若干行代码。
    Annotation[] annotations = method.getAnnotations();
    try {
          return retrofit.callAdapter(returnType, annotations);
    } catch (RuntimeException e) {
           ... // 这里省略了若干行代码
    }
}

////// 
public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
}

//////
public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations){
        ... // 这里省略了若干行代码。
    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;
          }
    }
    ... // 这里省略了若干行代码。
}

从上面代码最后的循环中可以看出,CallAdapter其实是通过遍历各个AdapterFactory来,然后根据返回接口方法的返回类型(如上面GithubApi的第二个方法的返回类型是Observable)来决定由哪个Factory来实例化CallAdapter。如果是返回类型是Observable,那么就由RxCallAdapterFactory(在创建Retrofit时向其中注册)来代劳。

RxCallAdapterFactory源码跟踪

RxCallAdapterFactory实现了CallAdapter.Factory接口,所以我们从get方法切入,

@Overridepublic CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    ... // 这里省略若干行代码
    CallAdapter<Observable<?>> callAdapter = getCallAdapter(returnType);
    ... // 这里省略若干行代码
    return callAdapter;
}

private CallAdapter<Observable<?>> getCallAdapter(Type returnType) {
    Type observableType = Utils.getSingleParameterUpperBound((ParameterizedType) returnType);
    ... // 这里省略若干行代码
    return new SimpleCallAdapter(observableType);
}

上面代码中,我们可以看到,RxCallAdapterFactory最后产生的CallAdapter其实就是一个SimpleCallAdapter实例。它实现了CallAdapter接口,所以我们也来看一下他的adapt方法。

@Override 
public <R> Observable<R> adapt(Call<R> call) {
    return Observable.create(new CallOnSubscribe<>(call))
          .flatMap(new Func1<Response<R>, Observable<R>>(){
            @Override 
            public Observable<R> call(Response<R> response) {
                if (response.isSuccess()) {
                    return Observable.just(response.body());
                }
                return Observable.error(new HttpException(response));
            });
}

上面代码涉及到了一个CallOnSubscribe类,它是Observable.OnSubscribe接口的一个实现。所以我们来看下它的call方法。

@Override 
public void call(final Subscriber<? super Response<T>> subscriber) {
    ... // 这里省略若干行代码
    try {
        Response<T> response = call.execute();
        if (!subscriber.isUnsubscribed()) {
            subscriber.onNext(response);
        }
    } catch (Throwable t) {
        Exceptions.throwIfFatal(t);
        if (!subscriber.isUnsubscribed()) {
            subscriber.onError(t);
        }        
        return;
    }
    if (!subscriber.isUnsubscribed()) {
        subscriber.onCompleted();
    }
}

上面代码意思很明显,调用Call的execute方法访问网络数据,并且把结果转化为Observable的数据源。

好,到这里RxCallAdapterFactory的工作原理已经知道了,可能有童鞋会对Call接口还有疑问。没有问题,下面就来看一个Call的实现类——OkHttpCall。

OkHttpCall类的源码跟踪

之前有说过Retrofit其实就是对OkHttp的一个包装。空穴来风,从这个OkHttpCall类就可以原因。OkHttpCall类是Call接口一个实现,采取了委派模式,调用它的execute方法或者enqueue方法其实都是委派到com.squareup.okhttp.Call实例来实现,然后再把结果包装成POJO形式的Response。如execute方法的源码,

public Response<T> execute() throws IOException {
    synchronized (this) {
        ... // 这里省略若干行代码
        com.squareup.okhttp.Call rawCall = createRawCall();
        ... // 这里省略若干行代码
        return parseResponse(rawCall.execute());
}

private Response<T> parseResponse(com.squareup.okhttp.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();
    ... // 这里省略若干行代码
    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
          T body = responseConverter.convert(catchingBody);
          return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
          ... // 这里省略若干行代码
    }}

另外,Call接口的enqueue方法其实和execute是差不多的,只不过是多线程方式运行。源码自己去看,要跟踪到OkHttp的Call中,我懒得写了。

没有了。

打完收工。

声明

By 啪嗒科技林泳坛
©啪嗒科技 本文只可引用,不允许转载,只能在发表于padakeji.com相关域名。