OkHttp+Retrofit使用分析

目前Android的网络请求最流行的应该就是OkHttp + Retrofit,在平时开发的项目中也用到了这一套框架,现在对源码的一些实现进行分析。Retrofit2.0最新版本默认集成OkHttp3.3,此源码分析也基于此版本。

1.网络层架构介绍

OkHttp+Retrofit使用分析_第1张图片
01.png
图源:Piasy
由上图可以看到,Android客户端使用这套框架进行网络请求,基本层次结构分为:Okio进行流操作,处理与服务端的传输信息;OkHttp作为网络请求客户端对请求与响应进行了一层封装;Retrofit在此进行的操作是对每个请求与响应创建的格式化操作,处理请求以及转化接收的响应数据,可以添加不同的Converter进行不同数据结构的转化。
Android系统提供了两种HTTP通信类,HttpURLConnection和HttpClient。由于HttpClient的API太多,难以对它们进行改进容易破坏兼容性,所以Android官方停止了对它的维护以及集成。OkHttp是一个相对成熟的解决方案,据说Android4.4的源码中可以看到HttpURLConnection已经替换成了OkHttp实现。
Android官方对两种请求方式的选择建议

2.使用分析

2.1初始化

2.1.1创建Retrofit、OkHttpClient对象

//创建Retrofit
Retrofit retrofit = new Retrofit.Builder()
        .client(OKHttpFactory.INSTANCE.getOkHttpClient())
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .baseUrl(BASE_URL)
        .build();

//创建OkHttpClient
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder
        //打印日志拦截器
        .addInterceptor(new HttpLogInterceptor())
        //下载进度拦截器
        .addInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Response originalResponse = chain.proceed(chain.request());
                return originalResponse
                        .newBuilder()
                        .body(new FileResponseBody(originalResponse))
                        .build();
            }
        });
okHttpClient = builder.build();

此项目中OkHttpClient创建方式使用的传统builder,官方也提供了一个快捷的创建方式,此方式使用了默认的一些设置:

OkHttpClient client = new OkHttpClient();
//构造方法
public OkHttpClient() {
    this(new Builder());
}
public Builder() {
  dispatcher = new Dispatcher();
  protocols = DEFAULT_PROTOCOLS;
  connectionSpecs = DEFAULT_CONNECTION_SPECS;
  proxySelector = ProxySelector.getDefault();
  cookieJar = CookieJar.NO_COOKIES;
  socketFactory = SocketFactory.getDefault();
  hostnameVerifier = OkHostnameVerifier.INSTANCE;
  certificatePinner = CertificatePinner.DEFAULT;
  proxyAuthenticator = Authenticator.NONE;
  authenticator = Authenticator.NONE;
  connectionPool = new ConnectionPool();
  dns = Dns.SYSTEM;
  followSslRedirects = true;
  followRedirects = true;
  retryOnConnectionFailure = true;
  connectTimeout = 10_000;
  readTimeout = 10_000;
  writeTimeout = 10_000;
}

如果没有特殊需求可以使用这种方式创建OkHttpClient对象

2.1.2定义API获取实例

interface GoogleService {
    /**
     * 谷歌框架请求接口
     * @param param
     * @return
     */
    @FormUrlEncoded
    @POST("api/google/getGoogleApks")
    Observable>> getGoogleFrameApks(
            @Field("param")JSONObject param);

    /**
     * 下载apk
     * @param fileUrl
     * @return
     */
    @GET
    Call downloadFile(@Url String fileUrl);
}
GoogleService googleService = retrofit.create(GoogleService.class);

现在看Retrofit.create()方法的实现

public  T create(final Class 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 {
          ...
          ServiceMethod serviceMethod = loadServiceMethod(method);
          OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
          return serviceMethod.callAdapter.adapt(okHttpCall);
        }
      });
}

这里使用的是动态代理技术,动态生成接口的实现类,并创建代理类,代理把对接口的调用转发给InvocationHandler,这样的好处就是所有对接口方法的调用都会在InvocationHandler的invoke方法实现中进行处理,在此方法中可以进行自定义的数据统计等操作。

2.1.3 调用API方法

Call downloadCall = googleService.downloadFile(apkUrl);
//外部传入回调接口实例
downloadCall.enqueue(callback);

由于Retrofit支持RxJava,将返回值类型设置为Observable,可以更方便对数据进行处理。
示例代码如下:

Observable> observable = googleService.getArticles(jsonObject);
observable
        .observeOn(Schedulers.io())
        .subscribeOn(AndroidSchedulers.mainThread())
        .subscribe(new ProgressSubscriber>() {
            @Override
            public void onError(Throwable e) {
                 ...
            }

            @Override
            public void onNext(List o) {
                 ...
            }
        });

2.2 API执行代码解析

Retrofit请求解析流程图
[图片上传失败...(image-e9d981-1519442347531)]
在Retrofit初始化的creat方法中,以下三行代码执行真正的请求操作:

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

2.2.1 ServiceMethod< T >

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

由此看到每个ServiceMethod对象对应API接口的一个方法,在获取的时候serviceMethodCache实现了缓存,同一个API的同一个方法只会创建一次。
ServiceMethod构造函数的实现:

ServiceMethod(Builder builder) {
    this.callFactory = builder.retrofit.callFactory();
    this.callAdapter = builder.callAdapter;
    this.baseUrl = builder.retrofit.baseUrl();
    this.responseConverter = builder.responseConverter;
    this.httpMethod = builder.httpMethod;
    this.relativeUrl = builder.relativeUrl;
    this.headers = builder.headers;
    this.contentType = builder.contentType;
    this.hasBody = builder.hasBody;
    this.isFormEncoded = builder.isFormEncoded;
    this.isMultipart = builder.isMultipart;
    this.parameterHandlers = builder.parameterHandlers;
  }
  • callFactory

负责创建HTTP请求,返回的是Okhttp3.Call类对象,如Call类说明文档:

A call is a request that has been prepared for execution. 
A call can be canceled. 
As this object represents a single request/response pair (stream), it cannot be executed twice.
一个准备好执行的请求。请求可以取消,由于代表一个单一的请求/响应流,所以不能被执行两次。

builder.retrofit.callFactory();
此处callFactory()在Retrofit的build()方法中可以看到默认返回一个OkHttpClient对象;

    okhttp3.Call.Factory callFactory = this.callFactory;
    if (callFactory == null) {
        callFactory = new OkHttpClient();
    }

初始化Retrofit的时候也可以指定其他的callFactory创建HTTP请求,此处Retrofit默认必须使用OkHttpClient,如需使用其他客户端要修改源码重新编译。

  • callAdapter

    private CallAdapter createCallAdapter() {
            ...      
          Annotation[] annotations = method.getAnnotations();
          try {
            return retrofit.callAdapter(returnType, annotations);
          } catch (RuntimeException e) { // Wide exception range because factories are user code.
            throw methodError(e, "Unable to create call adapter for %s", returnType);
          }
        }
    

    callAdapter也在Retrofit中进行创建,Retrofit类内部遍历adapterFactories列表,工厂提供需要的callAdapter,如果没有工厂能够提供将抛出异常,Retrofit初始化时可以添加不同的工厂,此项目中用到的是RxJavaCallAdapterFactory,使用此工厂API方法即可以返回Observable对象。

  • responseConverter

    private Converter createResponseConverter() {
         Annotation[] annotations = method.getAnnotations();
         try {
           return retrofit.responseBodyConverter(responseType, annotations);
         } catch (RuntimeException e) { // Wide exception range because factories are user code.
           throw methodError(e, "Unable to create converter for %s", responseType);
         }
       }
    

responseConverter的创建和上面两个对象一样也是在Retrofit中创建,由Retrofit初始化时进行添加,主要负责的是进行请求响应的自动化解析工作,此项目中用到的是GsonConverterFactory解析json数据.

Retrofit初始化时converterFactories会默认添加一个BuiltInConverters转换器,该转换器继承自Converter.Factory,Converter.Factory正是对每个HTTP请求参数转换为字符串,
其中提供了requestBodyConverter和stringConverter,@Body 和 @Part 类型的参数使用 requestBodyConverter 进行转换,其他类型的参数使用stringConverter转换。

  • parameterHandlers

    private ParameterHandler parseParameter(
            int p, Type parameterType, Annotation[] annotations) {
          ParameterHandler result = null;
          for (Annotation annotation : annotations) {
            ParameterHandler annotationAction = parseParameterAnnotation(
                p, parameterType, annotations, annotation);
          }
           ...
          return result;
        }
    

parameterHandlers由ServiceMethod的parseParameter创建,API接口方法的每个参数都会生成一个parameterHandler,它负责解析参数使用的注解类型(比如上面用到的@ Field,@Url等).

创建Retrofit实例时,用户根据自己的需求创建不同的工厂,让Retrofit不同模块之间高度解耦,代码更加清晰。

2.2.2 OkHttpCall

OkHttpCall实现了retrofit.Call接口,平时使用到execute()和enqueue(Callback callback)这两个接口,前者是同步执行HTTP请求,后者为异步执行,真正的请求在okhttp3的RealCall类中进行,由以下代码可以看出enqueue()执行的异步请求。

void enqueue(Callback responseCallback, boolean forWebSocket) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
  }
  
  static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override public void execute(Runnable r) {
        handler.post(r);
      }
    }
public Response execute() throws IOException {
    okhttp3.Call call;

    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;
        ...
      call = rawCall;
      if (call == null) {
        try {
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException e) {
          creationFailure = e;
          throw e;
        }
      }
    }
    if (canceled) {
      call.cancel();
    }
    return parseResponse(call.execute());
  }

  private okhttp3.Call createRawCall() throws IOException {
    Request request = serviceMethod.toRequest(args);
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    ...
    return call;
  }

  Response 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();

    int code = rawResponse.code();
    if (code < 200 || code >= 300) {
      ...
    }

    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) {
     ...
    }
  }

由execute的源码可以看到执行HTTP请求包括三步:

  • 1 创建okhttp3.Call:createRawCall(),此处用到的callFactory默认为OkHttpClient;
  • 2 执行网络请求:call.execute();
  • 3 解析请求结果数据:parseResponse(call.execute());

2.2.3 CallAdapter

serviceMethod.callAdapter.adapt(okHttpCall);

CallAdapter#adapt(Call call) 函数负责把 retrofit2.Call 转为 T,此项目中使用的是RxJavaCallAdapterFactory,返回类型T也就是RxJavaCallAdapterFactory创建的CallAdapter行为;

RxJavaCallAdapterFactory的get 方法中对返回值的类型进行了检查,只支持 rx.Single,rx.Completable 和 rx.Observable,一般使用RxJava返回值类型都是Obeservable,主要看一下对Obeservable的支持。

private CallAdapter> getCallAdapter(Type returnType, Scheduler scheduler) {
    Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);
    Class rawObservableType = getRawType(observableType);
    if (rawObservableType == Response.class) {
     ...
      Type responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
      return new ResponseCallAdapter(responseType, scheduler);
    }

    if (rawObservableType == Result.class) {
      ...
      Type responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
      return new ResultCallAdapter(responseType, scheduler);
    }

    return new SimpleCallAdapter(observableType, scheduler);
  }

这里进一步对返回值的泛型类型进行检查;除了retrofit2.Response和retrofit2.adapter.rxjava.Result类型外,都返回的是SimpleCallAdapter,使用SimpleCallAdapter进行数据转换。

public  Observable adapt(Call call) {
      Observable observable = Observable.create(new CallOnSubscribe<>(call)) 
          .lift(OperatorMapResponseToBodyOrError.instance());
      if (scheduler != null) {
        return observable.subscribeOn(scheduler);
      }
      return observable;
    }

创建一个Observable对象,使用lift操作符将retrofit2.Response转换为OperatorMapResponseToBodyOrError.instance()我们声明的返回结果类型或者错误类型;
OperatorMapResponseToBodyOrError进行的操作如下:

public Subscriber> call(final Subscriber child) {
    return new Subscriber>(child) {
      @Override public void onNext(Response response) {
        if (response.isSuccessful()) {
          child.onNext(response.body());
        } else {
          child.onError(new HttpException(response));
        }
      }
      @Override public void onCompleted() {
        child.onCompleted();
      }
      @Override public void onError(Throwable e) {
        child.onError(e);
      }
    };
  }

请求成功之后就将请求结果数据向后传递,response.body()就是我们定义的泛型类型。

参考资料与鸣谢

Piasy拆轮子系列文章

你可能感兴趣的:(OkHttp+Retrofit使用分析)