okHttp
okHttp是square出的Http通訊Lib
目前有很多知名的Lib可以支援, 例如 Volley, Glide, Retrofit...
參考文件
ref | link |
---|---|
okHttp | http://square.github.io/okhttp/ |
github | https://github.com/square/okhttp |
如何使用
1.gradle
compile 'com.squareup.okhttp:okhttp:2.4.0'
2.Initial
建議只要new一個實體做全部的操作就行了
okHttpClient = new OkHttpClient();
okHttpClient.setConnectTimeout(30, TimeUnit.SECONDS);
okHttpClient.setReadTimeout(30, TimeUnit.SECONDS);
3.GET
GET and POST 的用法主要的差別只有在Request而已
Request request = new Request.Builder()
.url(url)
.build();
4.POST
builde Requestbody
RequestBody formBody = new FormEncodingBuilder()
.add("name", "Cuber")
.add("age", "26")
.build();
Request request = new Request.Builder()
.url(url)
.post(RequestBody)
.build();
5.Send
把上面build出來的Request帶進來
Response response = client.newCall(request).execute();
//如果response回傳是null, 就代表timeout或 沒有網路
或是你想使用Callback...
Response response = client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
//timeout或 沒有網路
//注意!這裏是backgroundThread
}
@Override
public void onResponse(Response response) throws IOException {
//成功
//注意!這裏是backgroundThread
}
});
6.心得
我覺得okHttp最難寫的地方應該就是Callback了
相信很多人都會遇到,如果Callback回來之後,我的Activity finish了,或是我的Fragment replace了
此時更改UI,就會產生找不到View的問題
而且Callback回來,居然是在backgroundThread上,
這時候如果要操作View又要切換到mainThread去,略顯麻煩
所以我在寫的時候,是沒有使用Callback的 以下提供一種漂亮(自認...)的寫法給大家做參考
/* 以fragment為例 */
public class BaseFragment extends Fragment implements Handler.Callback {
private static final int MSG_QUERY_DATA = 0x00;
private static final int MSG_DISPLAY_DATA = 0x01;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.activity = activity;
/* setup handler */
HandlerThread handlerThread = new HandlerThread(getClass().getName());
handlerThread.start();
backgroundHandler = new Handler(handlerThread.getLooper(), this);
uiHandler = new Handler(activity.getMainLooper(), this);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
/* start */
backgroundHandler.sendEmptyMessage(MSG_QUERY_DATA);
}
@Override
public void onDestroyView() {
/* 將Message清空,backgroundThread結束掉 */
backgroundHandler.removeCallbacksAndMessages(null);
uiHandler.removeCallbacksAndMessages(null);
backgroundHandler.getLooper().quit();
super.onDestroyView();
}
@Override
public boolean handleMessage(Message msg) {
/* 如果fragment不在Activity上了,直接return掉,避免NPE */
if (!isAdded()) return false;
/* 做各種MSG */
switch(msg.what){
case MSG_QUERY_DATA:
// do okHttp without callback
Response response = client.newCall(request).execute();
// 傳回 uiThread 做UI更新
Message respMsg = uiHandler.obtainMessage();
respMsg.what = MSG_DISPLAY_DATA;
respMsg.obj = response;
backgroundHandler.sendMessage(respMsg);
break;
case MSG_DISPLAY_DATA:
Response apiResponse = (Response)msg.obj;
// 失敗
if(null == apiResponse){
//show error
}
// 成功
else{
//display data on UI
}
break;
return false;
}
}
Interceptor
okHttp 的 Interceptor 就如同名稱「攔截器」一樣,攔截你的 Request 做一些你想做的事情再送出去。例如:
- 自動加上使用者目前使用的語言送出去取得對應語言的回傳內容。
- 將 Request 計算出這個 Request 的 sigunature 再附加上送出去。
在 okHttp 中分成 Application Interceptor 和 Network Interceptor 兩種。 Application Interceptor 是會可以被 cache 起來的。</br> 如官網的圖片:
也就是在前面舉的 2 個例子中:
1.自動加上使用者目前使用的語言送出去取得對應語言的回傳內容
因為會根據不同參數的 Request 取回不同內容分別 cache,所以要使用 Application Interceptor 實作。
2.將 Request 計算出這個 Request 的 sigunature 再附加上送出去。
因為會每次 Request 只會根據不同時間計算出不同 sigunature,可是內容本身還是一樣的,所以要使用 Network Interceptor 實作。
Sample Code
如果今天要做計算 Request 的 sigunature ,則使用方式如下:
public class ApiClient {
Interceptor signedRequestInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Response response = null;
try {
Request originalRequest = chain.request();
Request signedRequest = SignRequestUtil.signRequest(originalRequest);
response = chain.proceed(signedRequest);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
e.printStackTrace();
}
return response;
}
};
OkHttpClient client = new OkHttpClient();
client.networkInterceptors().add(signedRequestInterceptor);
}
public class SignRequestUtil {
public static Request signRequest(Request originalRequest) {
Request.Builder requestBuilder = originalRequest.newBuilder();
// 在這做所有你需要做的事情,重新產生一個 Request 送出去。
return requestBuilder
.headers(getSignedHeaders(originalRequest))
.build();
}
}