1. 程式人生 > >關於Retrofit2+Okhttp3實現統一新增請求引數和重定向

關於Retrofit2+Okhttp3實現統一新增請求引數和重定向

Android開發中難免會遇到一些比較“不友好”的服務端介面。比如以前遇到的json資料中,某個欄位偶爾為Object,偶爾為List…

最近遇到的一個問題就是:所有請求介面都要增加一個token引數… 並且token引數有可能過期,比如請求某一條介面,如果token失效則在該請求的響應中把新的token帶回來,客戶端就得用新的token再次傳送該請求,類似重定向。

token失效的stateCode為3,新的token在data欄位中返回。如下:

{
    "stateCode":3,
    "data":"E78kH6",
    "errorMsg":null
}

慶幸的是,Okhttp提供了強大的攔截器功能,是一種能夠監控,重寫,重試呼叫的強大機制。

public class TokenInterceptord implements Interceptor {

     private final String TAG = "respond";

     @Override
     public Response intercept(Chain chain) throws IOException {
         Request oldRequest = chain.request();
         String url = oldRequest.url().toString();
         Response response = null
; // 新的請求,新增引數 Request newRequest = addParam(oldRequest); response = chain.proceed(newRequest); ResponseBody value = response.body(); byte[] resp = value.bytes(); String json = new String(resp, "UTF-8"); // 判斷stateCode值 try { JSONObject jsonObject = new
JSONObject(json); int stateCode = jsonObject.optInt("stateCode"); if (stateCode == 3) { String data = jsonObject.optString("data"); Log.d(TAG, "token失效,新的token:" + data); DataStorageUtils.saveToken(data); // token失效,重新執行請求 Request newTokenRequest = addParam(oldRequest); response = chain.proceed(newTokenRequest); } else { // 這裡值得注意。由於前面value.bytes()把響應流讀完並關閉了,所以這裡需要重新生成一個response,否則資料就無法正常解析了 response = response.newBuilder() .body(ResponseBody.create(null, resp)) .build(); } } catch (Exception e) { } return response; } /** * 新增公共引數 * * @param oldRequest * @return */ private Request addParam(Request oldRequest) { HttpUrl.Builder builder = oldRequest.url() .newBuilder() .setEncodedQueryParameter("lversion", PackagesUtils.getAppVersionName()) .setEncodedQueryParameter("token", DataStorageUtils.getToken()); Request newRequest = oldRequest.newBuilder() .method(oldRequest.method(), oldRequest.body()) .url(builder.build()) .build(); return newRequest; } }

當然,也可以為請求或響應新增Header。

Request request = oldRequest.newBuilder()
        .header("Content-Encoding", "gzip")
        .build();

Response response = response.newBuilder()
        .header("Content-Encoding", "gzip")
        .build();

然後為OkHttp配置一個攔截器。

public static OkHttpClient getOkHttpClient() {
        Interceptor interceptor = new TokenInterceptor();
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(30 * 1000, TimeUnit.MILLISECONDS)
                .readTimeout(30 * 1000, TimeUnit.MILLISECONDS)
                .addInterceptor(interceptor)
                .addInterceptor(new HttpLoggingInterceptor(new MyLog()).setLevel(HttpLoggingInterceptor.Level.BODY))
                .build();
        return client;
    }

Retrofit 使用 Okhttp作為client

Retrofit retrofit = new Retrofit.Builder()
    .addConverterFactory(ScalarsConverterFactory.create())
    .addConverterFactory(GsonConverterFactory.create())
    .baseUrl(baseUrl)
    .client(getOkHttpClient())
    .build();

Api api = retrofit.create(Api.class);