美文网首页闲暇时光Netty
OkHttp+Retrofit使用分析

OkHttp+Retrofit使用分析

作者: Zurich37度 | 来源:发表于2018-02-24 11:19 被阅读3117次

    目前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;
      }
    
    • 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<ResponseBody, T> 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<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请求包括三步:

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

    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拆轮子系列文章

    相关文章

      网友评论

        本文标题:OkHttp+Retrofit使用分析

        本文链接:https://www.haomeiwen.com/subject/cqvlvttx.html