用多了OkHttp你會發現,它的返回結果都是在子線程中的,我們對返回結果進行解析後,必須通過handler去更新UI,這麼一來,便會多出很多重複的機械代碼。我們需要進行一層封裝,在onResponse回調方法中對返回結果進行解析,然後將解析結果發出去到UI線程進行更新UI。
因此,我們需要一個解析的方法,我們定義一個接口。
public interface Parser<T> {
T parse(Response response);
}
該接口傳入okhttp給我們返回的Response ,我們將其進行解析,具體怎麼解析由我們自己實現,比如直接返回字符串形式,或者將json轉化爲實體類返回等等。
然後我們默認提供幾種實現,首先是直接返回字符串。
public class StringParser implements Parser<String> {
@Override
public String parse(Response response) {
String result=null;
try {
result=response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
}
再實現一個將json轉化爲實體類的。這個相對複雜一點,需要將實體類的Class對象傳入,因爲我們使用的是gson,進行轉換的時候需要該參數。當然這個應該是對所有實體類通用的,很顯然的用到了泛型。
public class GsonParser<T> implements Parser<T> {
private Class<T> mClass=null;
public GsonParser(Class<T> clazz){
if (clazz==null){
throw new IllegalArgumentException("Class can't be null");
}
this.mClass=clazz;
}
@Override
public T parse(Response response) {
try {
Gson gson=new Gson();
String str=response.body().string();
T t=gson.fromJson(str,mClass);
return t;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
要在UI層進行更新UI,其實很簡單,解析完成後發生一個消息就好了,那麼要怎麼做呢。
首先實現Callback接口
public class Callback<T> implements com.squareup.okhttp.Callback {
private Parser<T> mParser;
public Callback(Parser<T> mParser) {
if (mParser == null) {
throw new IllegalArgumentException("Parser can't be null");
}
this.mParser = mParser;
}
@Override
public void onFailure(Request request, IOException e) {
}
@Override
public void onResponse(Response response) throws IOException {
}
}
通過構造函數將我們的Parser傳遞了進去。
現在假設,我們已經定義好了Handler對象,則在請求失敗的時候我們需要發送一個失敗的消息
應該有兩個區別消息的常量
private static final int CALLBACK_SUCCESSFUL=0x01;
private static final int CALLBACK_FAILED=0x02;
請求失敗後發送失敗的消息
public void onFailure(Request request, IOException e) {
Message message=Message.obtain();
message.what=CALLBACK_FAILED;
message.obj=e;
mHandler.sendMessage(message);
}
請求成功的回調的處理看個人情況了,這裏認爲響應碼是2開頭的就是請求成功,否則也認爲是失敗,比如400,500。如果請求成功了就調用Parser的parse方法解析
public void onResponse(Response response) throws IOException {
if (response.isSuccessful()) {
T parseResult = mParser.parse(response);
Message message=Message.obtain();
message.what=CALLBACK_SUCCESSFUL;
message.obj=parseResult;
mHandler.sendMessage(message);
} else {
Message message=Message.obtain();
message.what=CALLBACK_FAILED;
mHandler.sendMessage(message);
}
}
剩下的就是我們的Handler了,我們把它定義成靜態的,防止內存泄露,由於需要調用外部類的方法,所有還需要持有外部類的引用,同樣的防止內存泄露,使用弱引用。同時記得使用主線程的Looper。
static class UIHandler<T> extends Handler{
private WeakReference mWeakReference;
public UIHandler(cn.edu.zafu.coreokhttp.callback.Callback<T> callback){
super(Looper.getMainLooper());
mWeakReference=new WeakReference(callback);
}
@Override
public void handleMessage(Message msg) {
}
}
private Handler mHandler=new UIHandler(this);
接下來就是handleMessage方法的處理了。
public void handleMessage(Message msg) {
switch (msg.what){
case CALLBACK_SUCCESSFUL: {
T t = (T) msg.obj;
cn.edu.zafu.coreokhttp.callback.Callback callback = (cn.edu.zafu.coreokhttp.callback.Callback) mWeakReference.get();
if (callback != null) {
callback.onResponse(t);
}
break;
}
case CALLBACK_FAILED: {
IOException e = (IOException) msg.obj;
cn.edu.zafu.coreokhttp.callback.Callback callback = (cn.edu.zafu.coreokhttp.callback.Callback) mWeakReference.get();
if (callback != null) {
callback.onFailure(e);
}
break;
}
default:
super.handleMessage(msg);
break;
}
}
從代碼中看到,我們回調了兩個函數,沒錯,這兩個是空函數,由用戶去覆蓋重寫實現
public void onResponse(T t){
}
public void onFailure(IOException e){
}
這樣有什麼好處呢?
- Parser可以重複使用,避免多次解析使用同樣的代碼
- 避免編寫多次Handler去處理UI層更新
現在我們看看如何使用。
我們簡單的使用StringParser進行解析返回結果
OkHttpClient okHttpClient=new OkHttpClient();
StringParser parser=new StringParser();
Request request = new Request.Builder().url("https://www.baidu.com").build();
okHttpClient.newCall(request).enqueue(new cn.edu.zafu.coreokhttp.callback.Callback<String>(parser) {
@Override
public void onResponse(String s) {
Toast.makeText(getApplicationContext(),s,Toast.LENGTH_SHORT).show();
}
});
從代碼中看到,我們重寫了我們定義的空函數,直接用Toast顯示出瞭解析結果。
在一定程度上簡化了原來的代碼。但是還不夠精簡,大家可以進一步精簡。