Retrofit最佳動態配置請求timeout辦法–Invocation
最近重構項目需要調整,
需要區分普通上傳和輔助功能校驗的超時.
爲了提高用戶體驗,需要動態去進行配置.
傳統方式
1.OkHttpClient設置
最傳統的設置 請求超時時間的方法無疑是
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(connTimeout, TimeUnit.SECONDS)
.readTimeout(connTimeout, TimeUnit.SECONDS)
.writeTimeout(connTimeout, TimeUnit.SECONDS));
在創建OkhttpClient的時候,傳入指定的timeout,
還可以在這裏加上各種自定義攔截器(比如日誌輸出,user_agent,加簽)
這個當然可以.
但是如果是一個大型項目,一個host的service內有着大量的請求接口.
在請求的時候,無疑是需要對OkHttpClient進行單例複用的,
這個時候需要對這種同一個host的其中的幾個請求接口的超時時間進行獨立控制,就不好處理了.
當然還有一種辦法,
2.自定義攔截器請求地址比對
在創建OkHttpClient的時候,新增一個自定義攔截器,繼承Interceptor,然後維護一個需要獨立處理請求超時的地址集合.
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String questUrl = request.url().toString();
//在這裏進行請求地址判斷.
if ( isContains(questUrl)){
Response proceed = chain.withConnectTimeout(timeout, TimeUnit.SECONDS)
.withReadTimeout(timeout, TimeUnit.SECONDS)
.withWriteTimeout(timeout, TimeUnit.SECONDS)
.proceed(request);
return proceed;
}
return chain.proceed(request);
}
通過維護一個地址集合,然後判斷地址,再設置對應的超時時間.
這種方法也可以,
但是如果某些地址需要超時1秒,某些需要超時2秒,某些需要超時6秒,
那麼可以預期得到,這樣寫起來會很蛋疼,
那麼有沒有什麼更好的辦法?
當然是有的!
最佳&最優雅的方式–Invocation
3.自定義攔截器與Invocation
經過上面的鋪墊,
前面的2種方法,多多少少都有着一點缺陷和遺憾.
那麼現在隆重介紹第3種方法,也是我個人認爲最佳也是最優雅的辦法.
首先請將Retrofit升級至 2.5.0
首先請將Retrofit升級至 2.5.0
首先請將Retrofit升級至 2.5.0
重要的事情說3遍.
先創建一個自定義註解,
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DynamicTimeout {
int timeout();
}
註解名稱可以按照自己的習慣起,代碼內容也通俗易懂,當然也可以再加一個參數,比如說 Unit,也就是時間單位,指定設置超時時間是 毫秒或者 秒,這看具體的個人需求.
使用起來也很簡單,直接在指定方法上面添加註解,設置指定的超時時間即可,
簡單易用!
還是創建一個攔截器
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
//核心代碼!!!
final Invocation tag = request.tag(Invocation.class);
final Method method = tag != null ? tag.method() : null;
final DynamicTimeout timeout = method != null ? method.getAnnotation(DynamicTimeout.class) : null;
XLog.d("invocation",tag!= null ? tag.toString() : "");
if(timeout !=null && timeout.timeout() > 0){
Response proceed = chain.withConnectTimeout(timeout.timeout(), TimeUnit.SECONDS)
.withReadTimeout(timeout.timeout(), TimeUnit.SECONDS)
.withWriteTimeout(timeout.timeout(), TimeUnit.SECONDS)
.proceed(request);
return proceed;
}
return chain.proceed(request);
}
注意看註釋,標註的那3行核心代碼.
在Request內,通過tag,獲取到Invocation,
然後再獲取Invocaion對象的method,最後獲取method的註解信息,
判斷註解對象存不存在,
如果存在直接讀取對應註解對象的設置參數值,動態設置進去.
Wow~
Awesome !!!
拓展
作爲一個有追求的程序員,當然會很好奇,
這玩意是怎麼實現的 以及 舉一反三.
能不能用這個再去做一點其他騷操作,來改善一下我們現有的代碼呢?
Invocation這個類點進去一看,
平平無奇…
還是去看看他的method屬性是怎麼設置進去的吧.
我們在RequestFactory內看到了是如何設置tag,
以及設置進去的tag也就是Invocation對象內如何保存 method和args的.
那麼這2個屬性的來源呢
當然是來自於Retrofit的核心,這個動態代理方法.
具體的源碼分析也不講了,
網上實在是太多了,我自己在2018年底也寫了一篇.
看了一圈,Invocation這個對象是有2個屬性的,我們只用到了method,
另外一個args呢,
我們其實也可以拿來利用一下.
比如 一些定義要在header內添加的 sign,user-agent,
可以通過invocation.arguments()獲取到,然後進行處理.
這些拓展用法就不細說了.
最後說一句:
JakeWharton牛逼!
總結
OK,我們回顧完所有的代碼,最後的總結就是