我正在开发一个应连接到网络服务的应用程序。出于开发目的,我使用了简单的 http 协议。这个效果很好。
public Response sendValidRequest(Context context, String encodedAccountData) throws ExecutionException, InterruptedException {
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
RequestBody body = new MultipartBody.Builder().setType(MultipartBody.FORM)
.addFormDataPart(ID, stringParameter)
.build();
Request request = new Request.Builder()
.url(URL)
.method(POST, body)
.addHeader(AUTH_HEADER, encodedAccountData)
.build();
CompletableFuture<Response> future = new CompletableFuture<>();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
future.complete(null);
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) {
future.complete(response);
}
});
return future.get();
}
public ValidResponse getValidResponse(Context context, String encodedAccountData) {
Response response;
try {
response = sendValidRequest(context, encodedAccountData);
} catch (ExecutionException | InterruptedException e) {
//handle this case
}
if(response == null || response.body() == null) {
//handle this case
}
try {
final String myResponse = response.body().string();
JSONObject jsonObject = new JSONObject(myResponse);
//Do something here depending on the content of the response.
} catch (JSONException | IOException e) {
//handle this case
}
然后我为 Web 服务创建了一些证书以便使用 https。 Postman 中的测试(使用 https)工作正常,没有问题。
我添加到清单:
<application android:networkSecurityConfig="@xml/network_security_config">...</application>
Netwok_security_config.xml:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="@raw/server_certificate"/>
</trust-anchors>
</base-config>
</network-security-config>
还有 server_certificate.crt。
然后我将以下内容添加到代码中:
public Response sendValidRequest(Context context, String encodedAccountData) throws ExecutionException, InterruptedException {
OkHttpClient client = new OkHttpClient().newBuilder()
.sslSocketFactory(getSSLSocketFactory(), getTrustManager())
.hostnameVerifier((hostname, session)->true)
.build();
.... //no further changes.
private SSLSocketFactory getSSLSocketFactory() {
try {
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new SecureRandom());
return sslContext.getSocketFactory();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private X509TrustManager getTrustManager() {
return new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
}
这确实会调用网络服务并返回结果。 但是,我现在在该行中收到 NetworkOnMainThreadException
final String myResponse = response.body().string();
我不知道为什么现在会发生这种情况。使用http的时候没有出现这种情况。当我撤消更改时,异常也消失了。
有人可以向我解释一下为什么我现在遇到异常吗?为什么它可以与 http 请求配合使用?
我尝试寻找答案,但没有成功。
现在我更改了 ThreadPolicy,它有效,但我认为这不是最佳实践。 在类的构造函数中:
StrictMode.setThreadPolicy(policy);```
基于 Okhttp 库的文档: https://square.github.io/okhttp/3.x/okhttp/okhttp3/ResponseBody.html
响应正文应在与此指定示例相同的回调中进行管理:
Call call = client.newCall(request);
call.enqueue(new Callback() {
public void onResponse(Call call, Response response) throws IOException {
try (ResponseBody responseBody = response.body()) {
... // Use the response.
}
}
public void onFailure(Call call, IOException e) {
... // Handle the failure.
}
});
他们提到 如果您在另一个线程上使用响应主体,这些示例将不起作用。,因此请确保响应的管理不是在外部线程上完成的。
最后关于使用 StrictMode 的评论(StrictMode 是一个 开发人员工具,它可以检测您可能意外执行的操作,并引起您的注意,以便您可以修复它们。) 它应该在 DEV 模式下使用意味着您只能将其置于某些开发条件下才能使用,例如
if(DEBUG_MODE) {
StrictMode.setThreadPolicy(policy)
}