Android 开源库分析——浅谈Retrofit的封装

本文链接:https://rainmonth.github.io/posts/A180205.html

Android 浅谈Retrofit的封装

先了解Retrofit一般的使用步骤:

  1. 配置OkHttpClient,设置各种拦截器;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    private volatile static OkHttpClient okHttpClient;
    private volatile static Retrofit retrofit;

    private static final int DEFAULT_CACHE_SIZE = 1024 * 1024 * 20;//默认缓存大小20M
    private static final int DEFAULT_MAX_AGE = 60 * 60;// 默认缓存时间单位
    private static final int DEFAULT_MAX_STALE_ONLINE = DEFAULT_MAX_AGE * 24;// 默认在线缓存时间
    private static final int DEFAULT_MAX_STALE_OFFLINE = DEFAULT_MAX_AGE * 24 * 7;// 默认离线缓存时间

    /**
    * 保存cookies拦截器
    */
    private static Interceptor SAVE_COOKIES_INTERCEPTOR = new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
    SharedPreferences sharedPreferences = SuperRandyApplicationContext.context
    .getSharedPreferences("cookie_sp", Context.MODE_PRIVATE);
    Response originalResponse = chain.proceed(chain.request());
    // 如果cookie为空,保存cookie到sp文件
    if (CommonUtils.isNullOrEmpty(sharedPreferences.getString("cookie", ""))) {
    // 获取请求返回的cookies
    if (!CommonUtils.isNullOrEmpty(originalResponse.header("Set-Cookie")) &&
    !originalResponse.header("Set-Cookie").isEmpty()) {
    final StringBuffer cookieBuffer = new StringBuffer();
    Observable.from(originalResponse.headers("Set-Cookie"))
    .map(new Func1<String, String>() {
    @Override
    public String call(String s) {
    String[] cookieArray = s.split(";");
    return cookieArray[0];
    }
    })
    .subscribe(new Action1<String>() {
    @Override
    public void call(String s) {
    cookieBuffer.append(s).append(";");
    }
    });
    KLog.e("save_cookie", cookieBuffer.toString());
    // 保存cookies到sp文件
    SharedPreferences.Editor editor = sharedPreferences.edit();
    editor.putString("cookie", cookieBuffer.deleteCharAt(cookieBuffer.length() - 1).toString());
    editor.apply();
    }
    }

    return originalResponse;
    }
    };

    /**
    * 添加cookies拦截器
    */
    private static Interceptor ADD_COOKIES_INTERCEPTOR = new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
    final Request.Builder builder = chain.request().newBuilder();
    SharedPreferences sharedPreferences = SuperRandyApplicationContext.context
    .getSharedPreferences("cookie_sp", Context.MODE_PRIVATE);
    Observable.just(sharedPreferences.getString("cookie", ""))
    .subscribe(new Action1<String>() {
    @Override
    public void call(String s) {
    builder.addHeader("Cookie", s);
    }
    });
    KLog.e("add_cookie", sharedPreferences.getString("cookie", ""));
    return chain.proceed(builder.build());
    }
    };

    /**
    * request 拦截器定义
    */
    private static final Interceptor REQUEST_INTERCEPTOR = new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    int maxStale = DEFAULT_MAX_STALE_ONLINE;
    //向服务期请求数据缓存1个小时
    CacheControl tempCacheControl = new CacheControl.Builder()
    // .onlyIfCached()
    .maxStale(5, TimeUnit.SECONDS)
    .build();
    request = request.newBuilder()
    .cacheControl(tempCacheControl)
    .build();
    return chain.proceed(request);
    }
    };

    /**
    * response 拦截器定义
    */
    private static final Interceptor RESPONSE_INTERCEPTOR = new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
    //针对那些服务器不支持缓存策略的情况下,使用强制修改响应头,达到缓存的效果
    //响应拦截只不过是出于规范,向服务器发出请求,至于服务器搭不搭理我们我们不管他,我们在响应里面做手脚,有网没有情况下的缓存策略
    Request request = chain.request();
    Response originalResponse = chain.proceed(request);
    int maxAge;
    // 缓存的数据
    if (!NetworkUtils.isConnected(SuperRandyApplicationContext.context)) {
    maxAge = DEFAULT_MAX_STALE_OFFLINE;
    } else {
    maxAge = DEFAULT_MAX_STALE_ONLINE;
    }
    return originalResponse.newBuilder()
    .removeHeader("Pragma")// 清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效
    .removeHeader("Cache-Control")
    .header("Cache-Control", "public, max-age=" + maxAge)
    .build();
    }
    };
    /**
    * 打印返回的json数据拦截器
    */
    private static final Interceptor LOGGING_INTERCEPTOR = new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {

    final Request request = chain.request();
    final Response response = chain.proceed(request);

    final ResponseBody responseBody = response.body();
    final long contentLength = responseBody.contentLength();

    BufferedSource source = responseBody.source();
    source.request(Long.MAX_VALUE); // Buffer the entire body.
    Buffer buffer = source.buffer();

    Charset charset = Charset.forName("UTF-8");
    MediaType contentType = responseBody.contentType();
    if (contentType != null) {
    try {
    charset = contentType.charset(charset);
    } catch (UnsupportedCharsetException e) {
    KLog.e("");
    KLog.e("Couldn't decode the response body; charset is likely malformed.");
    return response;
    }
    }

    if (contentLength != 0) {
    long t1 = System.nanoTime();
    Log.i("OkHttp:", String.format("Sending %s request %s on %s%n%s", request.method(), request.url(), chain.connection(), request.headers()));
    long t2 = System.nanoTime();
    Log.i("OkHttp:", String.format("Received response for %s in %.1fms%n%s", response.request().url(), (t2 - t1) / 1e6d, response.headers()));
    KLog.v("--------------------------------------------开始打印返回数据----------------------------------------------------");
    KLog.json(buffer.clone().readString(charset));
    KLog.v("--------------------------------------------结束打印返回数据----------------------------------------------------");
    }

    return response;
    }
    };

    /**
    * 获取OkHttpClient实例
    *
    * @return OkHttpClient实例
    */
    private static OkHttpClient getOkHttpClient() {
    if (okHttpClient == null) {
    synchronized (OkHttpClient.class) {// 同步访问
    if (okHttpClient == null) {
    // 网络请求相关缓存
    File cacheFile = new File(SuperRandyApplicationContext.application.getCacheDir(), "responses");
    Cache cache = new Cache(cacheFile, DEFAULT_CACHE_SIZE);
    okHttpClient = new OkHttpClient.Builder()
    .cache(cache)
    // 添加相关拦截器
    .addInterceptor(SAVE_COOKIES_INTERCEPTOR)
    .addInterceptor(ADD_COOKIES_INTERCEPTOR)
    .addNetworkInterceptor(RESPONSE_INTERCEPTOR)
    .addInterceptor(REQUEST_INTERCEPTOR)
    .addInterceptor(LOGGING_INTERCEPTOR)
    .build();
    }
    }
    }
    return okHttpClient;
    }
  1. 配置Retrofit实例,主要是client;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    /**
    * 获取OkHttpClient实例
    *
    * @return OkHttpClient实例
    */
    private static OkHttpClient getOkHttpClient() {
    if (okHttpClient == null) {
    synchronized (OkHttpClient.class) {// 同步访问
    if (okHttpClient == null) {
    // 网络请求相关缓存
    File cacheFile = new File(SuperRandyApplicationContext.application.getCacheDir(), "responses");
    Cache cache = new Cache(cacheFile, DEFAULT_CACHE_SIZE);
    okHttpClient = new OkHttpClient.Builder()
    .cache(cache)
    // 添加相关拦截器
    .addInterceptor(SAVE_COOKIES_INTERCEPTOR)
    .addInterceptor(ADD_COOKIES_INTERCEPTOR)
    .addNetworkInterceptor(RESPONSE_INTERCEPTOR)
    .addInterceptor(REQUEST_INTERCEPTOR)
    .addInterceptor(LOGGING_INTERCEPTOR)
    .build();
    }
    }
    }
    return okHttpClient;
    }
  2. 定义Service接口类,如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public interface User {

    @FormUrlEncoded
    @POST("login/login")
    Observable<Response<BaseResponse<LoginResultBean>>> login(@Field("mobile") String username,
    @Field("password") String psw);

    @POST("login/logout")
    Observable<Response<BaseResponse<Object>>> logout();

    @FormUrlEncoded
    @POST("login/is_registered")
    Observable<Response<BaseResponse<Boolean>>> isRegistered(@Field("mobile") String mobile);

    }
  1. 获取Retrofit实例;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    /**
    * 获取retrofit实例
    *
    * @param baseUrl baseURL
    * @return retrofit实例
    */
    public static Retrofit getRetrofit(String baseUrl) {
    if (retrofit == null) {
    synchronized (Retrofit.class) {
    if (retrofit == null) {
    retrofit = new Retrofit.Builder()
    .client(getOkHttpClient())
    .baseUrl(baseUrl)
    .addConverterFactory(GsonConverterFactory.create())
    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    .build();
    }
    }
    }
    return retrofit;
    }
  2. 创建Service,并访问接口

    1
    getRetrofit("http://local.rainmonth.com").create(User.class).login("mobile", "password");
  3. 处理返回结果。

封装所需要的功能

  1. 采用现在流行的链式结构进行创建;
  2. 提供一个接口形式的ApiService即可完成各种请求;
  3. 提供必要的回调函数处理返回结果;
  4. 提供通用的接收返回数据的容器;
  5. 支持多文件上传下载,并提供进度显示;(Download and upload)
  6. 可以动态配置;(Config)
  7. 良好的容错处理,能对返回结果进行统一处理和差异性处理;(网络错误,通用异常)(Exception and Error)
  8. Cookies持久化处理;(Cookie)
  9. 数据的缓存功能;(Cache,不同的缓存方式:内存,SD卡,数据库)
  10. 支持请求的取消,支持同步请求、支持异步请求;(方便调用get、post方法)
  11. 支持请求数据的拦截(方便后期调试)