在做 Android 开发时,我们免不了使用 HTTP 请求,有一个好用的框架对于开发的帮助很大,OKHttp(Github 地址 )是 Square 公司实现的开源 HTTP 请求框架,基本能够满足 Android 开发中的各种网络应用场景。
但在使用时,我发现 OKHttp 所提供的异步请求接口在执行回调函数时,不能更新 UI 组件,原因是框架执行回调,并不是在 UI 线程上执行的。这一点问题在开发时造成了一些不便。为了解决这个问题,我使用框架提供的同步请求接口封装了一个自己实现的异步接口,使得回调函数能够在 UI 线程上执行,也能够处理网络请求中出现的一些异常。实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 import android.app.Activity;import android.content.Context;import android.util.Log;import org.json.JSONException;import java.io.IOException;import java.util.concurrent.TimeUnit;import okhttp3.Call;import okhttp3.MediaType;import okhttp3.OkHttpClient;import okhttp3.Request;import okhttp3.RequestBody;public class HttpRequestHelper { private String url; private String body; private String method; private Call call; private Activity activity; private final Boolean[] ifSuccessfully = {true }; public HttpRequestHelper (Context c, String u, String m, String b) { url = u; method = m; body = b; activity = (Activity) c; OkHttpClient client = new OkHttpClient .Builder() .readTimeout(30 , TimeUnit.SECONDS) .writeTimeout(30 , TimeUnit.SECONDS) .build(); Request request; if (m.equals("get" )) { request = new Request .Builder() .url(u) .get() .build(); } else { request = new Request .Builder() .url(u) .post( RequestBody.create( MediaType.parse("application/json" ),b ) ) .build(); } call = client.newCall(request); } public void handleResponse (ResponseHandler rh, Class invoker, String info) { Thread t = new Thread (new Runnable () { @Override public void run () { String response = "" ; try { Log.i(invoker.getName(), "HTTP发送数据\"" + info + "\"(方法" + method + "):" + body + " 到 " + url); response = call.execute().body().string(); Log.i(invoker.getName(), "HTTP接收数据\"" + info + "\":" + response); } catch (IOException e) { Log.e(invoker.getName(), "HTTP获取异常\"" + info + "\":" + e.getMessage()); ifSuccessfully[0 ] = false ; } finally { String finalResponse = response; activity.runOnUiThread(new Runnable () { @Override public void run () { try { rh.handle(finalResponse); } catch (JSONException e) { Log.e(invoker.getName(), "HTTP获取JSON解析异常\"" + info + "\":" + e.getMessage()); ifSuccessfully[0 ] = false ; } finally { rh.handleResult(); } } }); } } }); t.start(); } public Boolean ifSuccess () { return ifSuccessfully[0 ]; } public interface ResponseHandler { void handle (String response) throws JSONException; void handleResult () ; } }
这个 HTTP 协助类用于发起异步 HTTP 请求,并对结果调用传入的回调函数进行处理。回调函数一共有两个,即 ResponseHandler 接口中的两个方法。
handle 方法用于处理网络返回的数据(已转换为字符串,代码中默认返回的和发送的数据都为 JSON 格式),在此方法中可以进行对数据的解析提取等操作。
handleResult 方法用于处理整个网络请求的结果,例如请求成功和请求失败(需要调用协助类中的 ifSuccess 方法进行判断)。对不同的结果进行不同的处理,对于 UI 组件的更新也是在此方法中进行的,此方法会在 UI 线程调用。注意,此方法不抛出任何异常,需要在方法中对异常进行处理。
协助类中的 ifSuccess 返回的结果包含了请求中任意异常的发生,也就是说,无论是 handle 方法中的异常,还是在请求中出现的异常,都能在 ifSuccess 方法返回的结果中体现,可以认为只要发生了异常,ifSuccess 方法返回的就是 false,否则为 true。
下面是调用协助类的代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 HttpRequestHelper hrh = new HttpRequestHelper ( this , authUrl, "post" , loginData.toString() ); hrh.handleResponse(new HttpRequestHelper .ResponseHandler() { @Override public void handle (String response) throws JSONException { authData = new JSONObject (response); System.out.println("Auth接受数据:" + authData.toString()); if (!authData.getString("username" ).equals(username)) { throw new JSONException ("自动登录异常:用户名不对应" ); } ifLoginSuccessfully= Boolean.valueOf(authData.getString("auth_result" )); if (ifStudent) { ifUploadStandardPhoto = authData.getBoolean("if_upload_standard_photo" ); } } @Override public void handleResult () { handleNetForAutoLogin(hrh, navigationView, ifLoginSuccessfully); } }, this .getClass(), "自动登录" );
调用的代码中,首先是实例化了一个 HttpRequestHelper hrh,确定了 URL、请求方法(“post”)和消息体数据(JSON对象字符串)。然后调用 hrh 对象的handleResponse 方法来启动请求,同时实例化回调方法的接口,将回调函数传入 handleResponse 方法。
可以看到,在 ResponseHandler 接口中,handle方法主要用来解析和提取网络返回的数据。在其他地方,我定义了一个 handleNetForAutoLogin 方法,用于对返回的网络数据的后续使用和处理。这里我把 handle 方法中解析出的数据(ifLoginSuccessfully)传入了 handleNetForAutoLogin 方法中进行处理,同时也传入了 hrh 和 一个 View 组件。传入 hrh 是为了检查网络请求是否出现了异常,若出现了异常,就不会使用解析出的数据,并通知用户发生了错误。若没有发生异常,则使用传入的数据进行下一步的操作。
需要注意的是,handleResult 方法在 HttpRequestHelper 类中是在 finally 块中执行的,也就是说,无论网络请求是否发生异常,handleResult 方法都一定会执行(所以就可以在此方法中判断网络请求是否发生异常)。
无论是 handle 还是 handleResult,在协助类中,他们都是被放在 activity.runOnUiThread 方法中执行,都可以直接操作和更新 UI 组件。
以上。