Home - GilBert1987/RetrofitStudy GitHub Wiki

[[TOC]]

目的

学习研究并最终实现基于Retrofit OkHttp 和RxJava Gson的Android 网络请求库

已经完成的工作

  • 实现AuthToken的过期和二次自动获取机制 100%
  • 实现基础的参数的自动加入 100%
  • 实现参数的加密和签名 100%
  • 支持GET POST 100%
  • 实现一个NodeJs Server 模拟真实的服务器 100%
  • 方便的支持Mock 40%
  • 支持自定义Cache 100%

尚未完成

  • 安装包大小测评
  • 缓存机制深入研究

1. 题外话 URL Connection和 OkHttp 之间的关系?

对外接口

conn = (HttpURLConnection) myUrl.openConnection();

    public URLConnection openConnection() throws IOException {
        return streamHandler.openConnection(this);
    }

内部实现

    /**
     * Sets the stream handler factory for this VM.
     */
    public static synchronized void setURLStreamHandlerFactory(URLStreamHandlerFactory factory) {
        if (streamHandlerFactory != null) {
            throw new Error("Factory already set");
        }
        streamHandlers.clear();
        streamHandlerFactory = factory;
    }
    void setupStreamHandler() {
        // Check for a cached (previously looked up) handler for
        // the requested protocol.
        streamHandler = streamHandlers.get(protocol);
        if (streamHandler != null) {
            return;
        }
        if (protocol.equals("http")) {
            try {
                String name = "com.android.okhttp.HttpHandler";
                streamHandler = (URLStreamHandler) Class.forName(name).newInstance();
            } catch (Exception e) {
                throw new AssertionError(e);
            }
        } else if (protocol.equals("https")) {
            try {
                String name = "com.android.okhttp.HttpsHandler";
                streamHandler = (URLStreamHandler) Class.forName(name).newInstance();
            } catch (Exception e) {
                throw new AssertionError(e);
            }
        } else if (protocol.equals("jar")) {
            streamHandler = new JarHandler();
        }
        if (streamHandler != null) {
            streamHandlers.put(protocol, streamHandler);
        }
    }

设计思想

基于接口+注册

问题:第三方Jar包怎么统一统计网络请求时间?

设置自己实现的 streamHandler + 反射 invoke 系统的handler

Retrofit 介绍及其API 简介

Retrofit 是什么

  • Retrofit 是Square公司提供的高效的Http库,基于OK HTTP提供网络访问功能
  • 将底层的代码都封装起来, 应用关注业务中的数据模型和操作方法

API 设计

public interface IUserProfileService {
    @FormUrlEncoded
    @POST("/users/{user}/profile")
    Call<ResultModel> updateUserProfile(@Field("userId") String userId, @Body UserProfile profile);

    @Headers("Cache-Control: max-age=6400")
    @GET("/users/{user}/profile")
    Observable<Response<UserProfile>> getUserProfile(@Query("userId") String userId);
}

API 使用

// 初始化OkHttp
OkHttpClient.Builder clientBuilder = new OkHttpClient().newBuilder();
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
clientBuilder.addInterceptor(httpLoggingInterceptor);
clientBuilder.**addInterceptor**(new AddParamIterceptor());
CacheInterceptor cacheInterceptor = new CacheInterceptor(mContext);
clientBuilder.addInterceptor(new **SignatureIterceptor**()).addInterceptor(cacheInterceptor);
sOkHttpClient = clientBuilder.build();
cacheInterceptor.setCache(sOkHttpClient.cache());
// 初始化 retrofit
sRetrofit = new Retrofit.Builder().client(sOkHttpClient)
                        .baseUrl(API)
                        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                        .addConverterFactory(GsonConverterFactory.create())
                        .build();
//调用API
RetrofitUtil.getInstance(this)
      .create(IUserProfileService.class)
      .getUserProfile("498238400")
      .subscribeOn(Schedulers.io())
      .observeOn(AndroidSchedulers.mainThread())
      .subscribe(new Subscriber<Response<UserProfile>>() {
                });

整体结构

内部原理

如何实现基于接口的一步调用--动态代理

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

如何实现返回结果的动态转换 Call 转换成Rx Observable

  • 通过adapter 实现,Gradle 中引入 compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
  • 基于注册查找机制,returnType和CallAdapter对应
  • 通过addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 注册
  • 在运行时通过returnType和CallAdapter 的关系找到

动态查找

    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;
      }
    }
public interface CallAdapter<T> {
  /**
   * Returns the value type that this adapter uses when converting the HTTP response body to a Java
   * object. For example, the response type for {@code Call<Repo>} is {@code Repo}. This type
   * is used to prepare the {@code call} passed to {@code #adapt}.
   * <p>
   * Note: This is typically not the same type as the {@code returnType} provided to this call
   * adapter's factory.
   */
  Type responseType();

  /**
   * Returns an instance of {@code T} which delegates to {@code call}.
   * <p>
   * For example, given an instance for a hypothetical utility, {@code Async}, this instance would
   * return a new {@code Async<R>} which invoked {@code call} when run.
   * <pre><code>
   * &#64;Override
   * public &lt;R&gt; Async&lt;R&gt; adapt(final Call&lt;R&gt; call) {
   *   return Async.create(new Callable&lt;Response&lt;R&gt;&gt;() {
   *     &#64;Override
   *     public Response&lt;R&gt; call() throws Exception {
   *       return call.execute();
   *     }
   *   });
   * }
   * </code></pre>
   */
  <R> T adapt(Call<R> call);

  /**
   * Creates {@link CallAdapter} instances based on the return type of {@linkplain
   * Retrofit#create(Class) the service interface} methods.
   */
  abstract class Factory {
    /**
     * Returns a call adapter for interface methods that return {@code returnType}, or null if it
     * cannot be handled by this factory.
     */
    public abstract CallAdapter<?> get(Type returnType, Annotation[] annotations,
        Retrofit retrofit);

    /**
     * Extract the upper bound of the generic parameter at {@code index} from {@code type}. For
     * example, index 1 of {@code Map<String, ? extends Runnable>} returns {@code Runnable}.
     */
    protected static Type getParameterUpperBound(int index, ParameterizedType type) {
      return Utils.getParameterUpperBound(index, type);
    }

    /**
     * Extract the raw class type from {@code type}. For example, the type representing
     * {@code List<? extends Runnable>} returns {@code List.class}.
     */
    protected static Class<?> getRawType(Type type) {
      return Utils.getRawType(type);
    }
  }
}

返回值的处理 Converter

By default, Retrofit can only deserialize HTTP bodies into OkHttp's ResponseBody type and it can only accept its RequestBody type for @Body.

Gson: com.squareup.retrofit2:converter-gson

Jackson: com.squareup.retrofit2:converter-jackson

Moshi: com.squareup.retrofit2:converter-moshi

Protobuf: com.squareup.retrofit2:converter-protobuf

Wire: com.squareup.retrofit2:converter-wire

Simple XML: com.squareup.retrofit2:converter-simplexml

Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars

OKHttp 接口

支持GET POST form stream file multipart cookie 设置Timeout

支持同步异步调用

支持连接缓存,连接池,支持ProxySelector

支持Authenticator,服务器返回401的时候回调,可以自定义retry次数

Per-call Configuration

良好的支持Https HostVerifier && CertPinning

Interceptor

  • OkHttp 使用List 管理Interceptor,按照先后顺序至执行
  @Override public Response execute() throws IOException {
      Response result = getResponseWithInterceptorChain();
  }

  private Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors())// 应用设置的Interceptor
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client)); // 执行真正的网络请求
    if (!retryAndFollowUpInterceptor.isForWebSocket()) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(
        retryAndFollowUpInterceptor.isForWebSocket()));

    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }
public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    Connection connection();
  }
}

项目代码分析

服务器基于 nodejs + express + npm包管理器

客户端基于Retrotfit + Gson + Okhttp

统一的responseBody 格式

// type是具体的类型 JavaBean.class

    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(final Type type, Annotation[] annotations, Retrofit retrofit) {
        Type newType = new ParameterizedType() {
            @Override
            public Type[] getActualTypeArguments() {
                return new Type[] { type };
            }

            @Override
            public Type getOwnerType() {
                return null;
            }

            @Override
            public Type getRawType() {
                return ApiModel.class; //公共基类
            }
        };
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(newType));
        return new GsonResponseBodyConverter<>(adapter);
    }

final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, Object> {

    private final TypeAdapter<T> adapter;

    GsonResponseBodyConverter(TypeAdapter<T> adapter) {
        this.adapter = adapter;
    }

    @Override
    public Object convert(ResponseBody value) throws IOException {
        try {
            ApiModel apiModel = (ApiModel) adapter.fromJson(value.charStream());
            if (apiModel.mErrorCode == ErrorCode.TOKEN_NOT_EXIST) {
                throw new TokenNotExistException();
            } else if (apiModel.mErrorCode == ErrorCode.TOKEN_INVALID) {
                throw new TokenInvalidException();
            } else if (apiModel.mErrorCode != 0) {
                // 特定 API 的错误,在相应的 Subscriber 的 onError 的方法中进行处理
                throw new ApiException();
            } else {
                return apiModel.data;
            }
        } finally {
            value.close();
        }}
}

public class ApiModel<T> {
    @SerializedName("error_code")
    public int mErrorCode; // 链接成功,但服务器异常
    @SerializedName("error_desc")
    public String mErrorDesc; // 服务器异常的描述

    public T data;
}

Mock 服务器

基于org.nanohttpd:nanohttpd:2.3.1 设置Interceptor 截断网络链接,读取本地文件。

会增加多大的安装包

依赖和参考项目

  1. https://github.com/alighters/AndroidDemos
  2. npm modules express body-parsre

参考文献

⚠️ **GitHub.com Fallback** ⚠️