网络编程的最佳实践
目前你已经掌握了 HttpURLConnection的用法,知道了如何发起 HTTP请 求
HttpURLConnection使用
以及解析服务器返回的数据
解析 XML格式数据
解析JSON数据
其实之前我们的写法其实是很有问题的。 因为一个应用程序很可能会在许多地方都使用到网络功能,而发送 HTTP请求的代码基本都 是相同的,如果我们每次都去编写一遍发送 HTTP请求的代码,这显然是非常差劲的做法
通常情况下我们都应该将这些通用的网络操作提取到一个公共的类里,并提供一 个静态方法,当想要发起网络请求的时候只需简单地调用一下这个方法即可。比如使用如下 的写法:
public class HttpUtil {
public static String sendHttpRequest(String address){
HttpURLConnection connection = null;
StringBuilder response = new StringBuilder();
try {
URL url = new URL(address);
connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
connection.setDoInput(true);
connection.setDoOutput(true);
InputStream in = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = reader.readLine())!=null){
response.append(line);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return response.toString();
}
}
以后每当需要发起一条 HTTP请求的时候就可以这样写:
String address = "https://www.baidu.com";
String response = HttpUtil.sendHttpRequest(address);
在获取到服务器响应的数据后我们就可以对它进行解析和处理了。但是需要注意,网络 请求通常都是属于耗时操作,而 sendHttpRequest()方法的内部并没有开启线程,这样就有可 能导致在调用 sendHttpRequest()方法的时候使得主线程被阻塞住
那么遇到这种情况应该怎么办呢?其实解决方法并不难,只需要使用 Java的回调机制 就可以了,下面就让我们来学习一下回调机制到底是如何使用的
首先需要定义一个接口,比如将它命名成 HttpCallbackListener
public interface HttpCallbackListener {
void onFinish(String response);
void onError(Exception e);
}
我们在接口中定义了两个方法,onFinish()方法表示当服务器成功响应我们请 求的时候调用,onError()表示当进行网络操作出现错误的时候调用
这两个方法都带有参数, onFinish()方法中的参数代表着服务器返回的数据,而 onError()方法中的参数记录着错误的 详细信息
接着修改 HttpUtil中的代码
public class HttpUtil {
public static void sendHttpRequest(final String address, final HttpCallbackListener listener) {
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection connection = null;
try {
URL url = new URL(address);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
connection.setDoOutput(true);
connection.setDoInput(true);
InputStream in = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine())!=null){
response.append(line);
}
if(listener != null){
listener.onFinish(response.toString());
}
} catch (Exception e) {
if(listener != null){
listener.onError(e);
}
}
}
}).start();
}
}
我们首先给 sendHttpRequest()方法添加了一个 HttpCallbackListener参数,并在方法的内 部开启了一个子线程,然后在子线程里去执行具体的网络操作
注意子线程中是无法通过 return语句来返回数据的,因此这里我们将服务器响应的数据传入了 HttpCallbackListener的 onFinish()方法中,如果出现了异常就将异常原因传入到 onError()方法中
现在 sendHttpRequest()方法接收两个参数了,因此我们在调用它的时候还需要将 HttpCallbackListener的实例传入,如下所示:
private void sendRequestWithURLConnection() {
String address = "https://www.baidu.com";
HttpUtil.sendHttpRequest(address, new HttpCallbackListener() {
@Override
public void onFinish(String response) {
//这里根据返回内容执行具体的逻辑
Message message = new Message();
message.what = SHOW_RESPONSE;
//将服务器返回的结果存放到Message中
message.obj = response;
handler.sendMessage(message);
}
@Override
public void onError(Exception e) {
//这里对异常情况进行处理
}
});
}
这样的话,当服务器成功响应的时候我们就可以在 onFinish()方法里对响应数据进行处 理了,类似地,如果出现了异常,就可以在 onError()方法里对异常情况进行处理
如此一来, 我们就巧妙地利用回调机制将响应数据成功返回给调用方了
另外需要注意的是,onFinish()方法和 onError()方法最终还是在子线程中运行的,因此 我们不可以在这里执行任何的 UI操作,如果需要根据返回的结果来更新 UI,则仍然要使用 上一章中我们学习的异步消息处理机制
上一篇: WPF 自定义ComboBox样式,自定义多选控件
下一篇: LeetCode71. 简化路径