闲暇时光Netty

OkHttp+Retrofit使用分析

2018-02-24  本文已影响3117人  Zurich37度

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

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<HttpResult<List<AppInfoBean>>> getGoogleFrameApks(
            @Field("param")JSONObject param);

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

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

public <T> T create(final Class<T> 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<ResponseBody> downloadCall = googleService.downloadFile(apkUrl);
//外部传入回调接口实例
downloadCall.enqueue(callback);

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

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

            @Override
            public void onNext(List<ArticleData> 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<T> 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;
  }

负责创建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,如需使用其他客户端要修改源码重新编译。

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

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

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

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

2.2.2 OkHttpCall

OkHttpCall实现了retrofit.Call接口,平时使用到execute()和enqueue(Callback<T> 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<T> 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<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();

    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请求包括三步:

2.2.3 CallAdapter

serviceMethod.callAdapter.adapt(okHttpCall);

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

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

private CallAdapter<Observable<?>> 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 <R> Observable<R> adapt(Call<R> call) {
      Observable<R> observable = Observable.create(new CallOnSubscribe<>(call)) 
          .lift(OperatorMapResponseToBodyOrError.<R>instance());
      if (scheduler != null) {
        return observable.subscribeOn(scheduler);
      }
      return observable;
    }

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

public Subscriber<? super Response<T>> call(final Subscriber<? super T> child) {
    return new Subscriber<Response<T>>(child) {
      @Override public void onNext(Response<T> 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拆轮子系列文章

上一篇下一篇

猜你喜欢

热点阅读