使用OKHTTP框架异步更新UI组件

在做 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;

/**
* 用于发起HTTP请求的协助类。
*/
public class HttpRequestHelper {

/**
* 请求的地址。
*/
private String url;

/**
* 请求的消息体。
*/
private String body;

/**
* 请求的方法。
*/
private String method;

private Call call;
private Activity activity;

private final Boolean[] ifSuccessfully = {true};

/**
* 构造方法。
* @param c Context
* @param u URL
* @param m Method
* @param b Body
*/
public HttpRequestHelper(Context c, String u, String m, String b) {
url = u;
method = m;
body = b;
activity = (Activity) c;

// 设置超时时间为10秒
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);
}

/**
* 用于处理网络请求结果的方法。
* @param rh 处理接口。
* @param invoker 调用者Class
* @param info 用于显示的Debug信息。
*/
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();
}

/**
* 返回网络请求是否成功。
* @return 是否成功。
*/
public Boolean ifSuccess() {
return ifSuccessfully[0];
}

/**
* 处理结果的接口。
*/
public interface ResponseHandler {
/**
* 用于处理网络返回结果的回调方法。
* @param response 网络返回的字符串结果。
* @throws JSONException 可能的JSON异常。
*/
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 {
// 解析自动登录JSON数据
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 组件。

以上。


使用OKHTTP框架异步更新UI组件
https://maphical.cn/2019/04/update-ui-using-okhttp/
作者
MaphicalYng
发布于
2019年4月14日
许可协议