在同一个app里面,我们可能会在很多地方都使用到网络请求,而发送网络请求的代码基本上都是相同的。
所以,在通常情况下,我们应该将这些通用的网络操作提取到一个公共的类里面,并提供一个静态方法,当想要发起网络请求的时候只需要简单的调用一下这个方法即可,比如:
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);
connection.setDoInput(true);
connection.setDoOutput(true);
InputStream is = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
return response.toString();
} catch (Exception e) {
e.printStackTrace();
return e.getMessage();
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}
这样,以后每次需要发送一条Http请求的时候就可以直接写:
String address = "http://www.google.com";
String response = HttpUtil.sendHttpRequest(address);
但是,这里有一个问题。网络请求通常都是属于耗时操作,而我们在sendHttpRequest()
方法中并没有开启线程,这样就很有可能导致在调用sendHttpRequest()
方法的时候使得主线程被阻塞。
那么,这个问题该如何解决呢?
我们是不是直接在sendHttpRequest()
方法内部开启一个线程就ok了呢?
答案当然是否定的。
因为,如果我们在sendHttpRequest()
方法中开启了一个线程来发起Http请求,那么服务器响应的数据是无法进行返回的,所有的耗时逻辑都是在子线程进行的,sendHttpRequest()
方法会在服务器还没来得及响应的时候就执行结束了,当然也就无法返回响应的数据了。
(思考 为什么?)
实际上,我们需要使用Java的回调机制来解决这个问题。
首先我们定义一个接口,
public interface HttpCallbackInterface {
void onSucceed(String response);
void onFail(Exception e);
}
我们在接口中定义了两个方法,onSucceed()
方法表示当服务器成功相应请求时调用,onFail()
表示当网络请求出现错误时调用。
接着修改HttpUtil中的代码,
public class HttpUtil {
public static void sendHttpRequest(final String address, final HttpCallbackInterface httpCallbackInterface) {
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.setDoInput(true);
connection.setDoOutput(true);
InputStream is = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
if (httpCallbackInterface != null) {
//回调onSucceed()方法
httpCallbackInterface.onSucceed(response.toString());
}
} catch (Exception e) {
if (httpCallbackInterface != null) {
//回调onFail()方法
httpCallbackInterface.onFail(e);
}
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}).start();
}
}
我们首先给sendHttpRequest()
方法添加了一个HttpCallbackInterface
参数,并在方法里开启了一个子线程,然后在子线程中去执行具体的网络操作。
注意,子线程中时无法通过return语句来返回数据的。
因此,我们这里将服务器返回的数据传入了onSucceed()
方法中,如果出现异常就将异常传入onFail()
方法中。
现在,当我们需要调用sendHttpRequest()
进行网络请求时,
HttpUtil.sendHttpRequest(address, new HttpCallbackInterface() {
@Override
public void onSucceed(String response) {
//在这里根据返回内容执行具体操作
}
@Override
public void onFail(Exception e) {
//在这里对异常情况进行处理
}
});
这样的话,当服务器成功响应的时候我们就可以在onSucceed()
方法里对响应数据进行处理,类似地,如果出现了异常,就可以在onFail()
方法里对异常进行处理。如此一来,我们就巧妙地利用回调机制将响应数据成功返回给调用方了。
另外需要注意的是,onSucceed()
方法和onFail()
方法最终还是在子线程中运行的,因此我们不可以在这里执行任何的UI操作,如果需要根据返回的结果来更新UI,则仍然要使用异步消息处理机制(如使用Handler
-Looper
-MessageQueue
机制)。