Android 基於OkHttp的UI層回調封裝
用多了OkHttp你會發現,它的返回結果都是在子線程中的,我們對返回結果進行解析後,必須通過handler去更新UI,這麼一來,便會多出很多重複的機械代碼。我們需要進行一層封裝,在onResponse回調方法中對返回結果進行解析,然後將解析結果發出去到UI線程進行更新UI。
因此,我們需要一個解析的方法,我們定義一個介面。
{ T parse(Response response);} data-snippet-id=ext.cec61e79aa461b746feef11602eb4015 data-snippet-saved=false data-csrftoken=FFB8F3V0-cMZ5HYXGR2ciooUmZ0MydkD49ok data-codota-status=done>public interface Parser { T parse(Response response);}
該介面傳入okhttp給我們返回的Response ,我們將其進行解析,具體怎麼解析由我們自己實現,比如直接返回字串形式,或者將json轉化為實體類返回等等。
然後我們預設提供幾種實現,首先是直接返回字串。
{ @Override public String parse(Response response) { String result=null; try { result=response.body().string(); } catch (IOException e) { e.printStackTrace(); } return result; }} data-snippet-id=ext.516a89f6a521c25637bfc09af9f05630 data-snippet-saved=false data-csrftoken=y3cxcXmx-n-JLN_KclziMMzSOdhEgpZDwWcc data-codota-status=done>public class StringParser implements Parser { @Override public String parse(Response response) { String result=null; try { result=response.body().string(); } catch (IOException e) { e.printStackTrace(); } return result; }}
再實現一個將json轉化為實體類的。這個相對複雜一點,需要將實體類的Class對象傳入,因為我們使用的是gson,進行轉換的時候需要該參數。當然這個應該是對所有實體類通用的,很顯然的用到了泛型。
implements Parser { private Class mClass=null; public GsonParser(Class 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; }} data-snippet-id=ext.eb19a9e01dbbd2b4c51c65e05d4020f0 data-snippet-saved=false data-csrftoken=IZar1yFn-UJkcVXawAxgbtZEYAKOCG964lOA data-codota-status=done>public class GsonParser implements Parser { private Class mClass=null; public GsonParser(Class 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介面
implements com.squareup.okhttp.Callback { private Parser mParser; public Callback(Parser 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 { }} data-snippet-id=ext.7e4050d9ffcd016000b2d5fce948b0e4 data-snippet-saved=false data-csrftoken=3LIOZuhp-3Fb7sxOuPVtPQBo_1fubSAlgGkE data-codota-status=done>public class Callback implements com.squareup.okhttp.Callback { private Parser mParser; public Callback(Parser 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。
extends Handler{ private WeakReference mWeakReference; public UIHandler(cn.edu.zafu.coreokhttp.callback.Callback callback){ super(Looper.getMainLooper()); mWeakReference=new WeakReference(callback); } @Override public void handleMessage(Message msg) { }}private Handler mHandler=new UIHandler(this); data-snippet-id=ext.f51a388bd196ac6782b89d87fe2e01fd data-snippet-saved=false data-csrftoken=WyHTOoMS-rPdG952wjFxjTAikJsxdDhHQ54A data-codota-status=done>static class UIHandler extends Handler{ private WeakReference mWeakReference; public UIHandler(cn.edu.zafu.coreokhttp.callback.Callback 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(parser) { @Override public void onResponse(String s) { Toast.makeText(getApplicationContext(),s,Toast.LENGTH_SHORT).show(); }});
從代碼中看到,我們重寫了我們定義的空函數,直接用Toast顯示出瞭解析結果。
在一定程度上簡化了原來的代碼。但是還不夠精簡,請期待後續的封裝。