网络编程代码重用的一点问题与技巧
基本的重用代码事例
学习android开发时大家都会接触到发起http请求的相关技巧,以及解析服务器返回的数据,应用程序可能会在多个地方都是用到网络功能,而发送http请求的代码都是非常相似的,于是理所当然地,我们应将这些操作整合到一起,比如一个工具类里。
public class HttpUtil{
public static String sendHttpRequest(String address) {
HttpURLConnection connection = null;
try {
URL url = new URL(address);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
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);
}
return response.toString();
} catch (Exception e){
e.printStackTrace();
} finally {
if (reader != null){
try {
reader.close();
} catch (IOException e){
e.printStackTrace();
}
}
if (connection != null){
connection.disconnect();
}
}
}
}
这样我们只需要传入网址,获取服务器相应的数据后,就可以进行后续的解析和处理了。
String address = "http://blog.dota2.com/"
String response = HttpUtil.sendHttpRequest(address);
线程是个问题
网络请求通常都是耗时的操作,而我们的sendHtttpRequest()方法内部并没有开启线程,这样可能会使主线程被阻塞住,这对用户的体验有不好的影响。
但我们恐怕也不能简单地在sendHtttpRequest()方法开启一个线程来解决问题。因为所有的耗时逻辑都在子线程进行的,sendHtttpRequest()方法会在服务器还没来得及响应的时候就执行结束了,导致该方法无法返回相应的数据。
所以我们需要另寻他路。
java的回调机制
我们先来看下面的代码:
public interface HttpCallBackListener {
void onFinish(String response);
void onError(Exception e);
}
我们定义了一个接口并在其中定义了两个方法,onFinish()在服务器相应请求时调用,参数是服务器返回的数据,onError()在进行网络操作出现错误时调用,参数是错误的详细信息。
然后修改HttpUtil的代码:
public class HttpUtil{
public static String 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);
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);
}
} finally {
if (reader != null){
try {
reader.close();
} catch (IOException e){
e.printStackTrace();
}
}
if (connection != null){
connection.disconnect();
}
}
}
}).start;
}
}
我们给sendHttpRequest()方法添加了一个HttpCallbackListener参数,并在方法的内部开启了一个子线程,由子线程去执行网络操作。由于子线程无法通过return语句返回数据,因此我们将数据传入了HttpCallbackListener的onFinish()方法中,而异常传入了onError()方法中。
现在,调用sendHttpRequest()方法的代码是这样的:
HttpUtil.sendHttpRequest("http://blog.dota2.com/", new HttpCallbackListener() {
@Override
public void onFinish(String response) {
// 根据返回的数据执行具体逻辑
}
public void onError(Exception e) {
// 在此处理异常
}
});
如此一来,通过利用回调机制我们就解决了网络编程的线程问题。
使用OKHttp实现回调机制
有句话说得好,不要重复发明伦子,其实OKHttp已经提供了实现相同功能的接口,如下:
public static void sendOkHttpRequest(String address, okhttp3.Callback callback) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(address).build();
client.newCall(request).enqueue(callback);
}
sendOkHttpRequest传入了一个okhttp3.Callback参数,这是OkHttp库中自带的一个回调接口,类似于刚才的HttpCallbackListener。在client.newCall()之后调用enqueue()方法,并把okhttp3.Callback参数传入,没错,OkHttp会在enqueue()方法内部自己开好子线程并执行http请求,最终将请求结果回调到okhttp3.Callback当中。
所以,现在我们调用sendOkHttpRequest()方法是这样写:
HttpUtil.sendOkHttpRequest("http://blog.dota2.com/", new okhttp3.Callback() {
@Override
public void onResponse(Call call, Response response) throws IOException {
// 根据返回的数据执行具体逻辑
String responseData = response.body().string();
}
public void onFailure(Call call, IOException e) {
// 在此处理异常
}
});
这样,我们只需编写少量的代码就能完成较为复杂的网络操作了。