Android OkHttp-based UI Layer callback Encapsulation
When OkHttp is used, you will find that all the returned results are in the subthread. After parsing the returned results, you must update the UI through handler, there will be a lot of repeated mechanical code. We need to encapsulate the returned results in the onResponse callback method, and then send the parsing results to the UI thread to update the UI.
Therefore, we need a resolution method, and we define an interface.
{ 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);}
This interface is used to pass in the Response returned by okhttp, And we will parse it. We will implement the specific parsing by ourselves, for example, directly returning the string form, or converting json into returning the object class.
Then we provide several implementations by default. The first is to directly return a string.
{ @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; }}
Then, convert json into Entity classes. This is relatively complicated. We need to pass in Class objects of the object Class, because we use gson, which is required for conversion. Of course, this should be common to all entity classes, and it is clear that generic is used.
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; }}
Updating the UI on the UI Layer is actually very simple. After Parsing is complete, a message is ready. How can this problem be solved.
First, implement the Callback Interface
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 { }}
The constructor passes our Parser in.
Now, if we have defined the Handler object, we need to send a failed message when the request fails.
There should be two constants that distinguish messages
private static final int CALLBACK_SUCCESSFUL=0x01;private static final int CALLBACK_FAILED=0x02;
Message that fails to be sent after request failure
public void onFailure(Request request, IOException e) { Message message=Message.obtain(); message.what=CALLBACK_FAILED; message.obj=e; mHandler.sendMessage(message);}
The processing of callback for successful requests depends on my personal situation. Here, the response code starting with 2 is regarded as request success, otherwise it is considered as failure, for example, 400,500. If the request succeeds, the Parser's parse method is called for parsing.
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); }}
The rest is our Handler. We define it as static to prevent memory leakage. Because we need to call external class methods, all of them need to hold external class references, it also prevents Memory leakage and uses weak references. Remember to use the Logoff of the main thread.
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);
The next step is to process the handleMessage method.
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; }}
We can see from the code that we have called back two functions. Yes, these two are empty functions, which are overwritten by the user.
public void onResponse(T t){}public void onFailure(IOException e){}
What are the benefits?
Parser can be used repeatedly to avoid multiple resolutions and use the same code to avoid writing Handler to process UI Layer updates.
Now let's take a look at how to use it.
We simply use StringParser to parse the returned results
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(); }});
From the code, we can see that we have rewritten the empty function we defined and directly used Toast to display the parsing result.
The original code is simplified to a certain extent. But it is not concise enough. Please look forward to future encapsulation.