在同一個app里面,我們可能會在很多地方都使用到網(wǎng)絡(luò)請求,而發(fā)送網(wǎng)絡(luò)請求的代碼基本上都是相同的。
所以,在通常情況下,我們應(yīng)該將這些通用的網(wǎng)絡(luò)操作提取到一個公共的類里面,并提供一個靜態(tài)方法,當(dāng)想要發(fā)起網(wǎng)絡(luò)請求的時候只需要簡單的調(diào)用一下這個方法即可,比如:
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();
}
}
}
}
這樣,以后每次需要發(fā)送一條Http請求的時候就可以直接寫:
String address = "http://www.google.com";
String response = HttpUtil.sendHttpRequest(address);
但是,這里有一個問題。網(wǎng)絡(luò)請求通常都是屬于耗時操作,而我們在sendHttpRequest()
方法中并沒有開啟線程,這樣就很有可能導(dǎo)致在調(diào)用sendHttpRequest()
方法的時候使得主線程被阻塞。
那么,這個問題該如何解決呢?
我們是不是直接在sendHttpRequest()
方法內(nèi)部開啟一個線程就ok了呢?
答案當(dāng)然是否定的。
因?yàn)椋绻覀冊?code>sendHttpRequest()方法中開啟了一個線程來發(fā)起Http請求,那么服務(wù)器響應(yīng)的數(shù)據(jù)是無法進(jìn)行返回的,所有的耗時邏輯都是在子線程進(jìn)行的,sendHttpRequest()
方法會在服務(wù)器還沒來得及響應(yīng)的時候就執(zhí)行結(jié)束了,當(dāng)然也就無法返回響應(yīng)的數(shù)據(jù)了。
(思考 為什么?)
實(shí)際上,我們需要使用Java的回調(diào)機(jī)制來解決這個問題。
首先我們定義一個接口,
public interface HttpCallbackInterface {
void onSucceed(String response);
void onFail(Exception e);
}
我們在接口中定義了兩個方法,onSucceed()
方法表示當(dāng)服務(wù)器成功相應(yīng)請求時調(diào)用,onFail()
表示當(dāng)網(wǎng)絡(luò)請求出現(xiàn)錯誤時調(diào)用。
接著修改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) {
//回調(diào)onSucceed()方法
httpCallbackInterface.onSucceed(response.toString());
}
} catch (Exception e) {
if (httpCallbackInterface != null) {
//回調(diào)onFail()方法
httpCallbackInterface.onFail(e);
}
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}).start();
}
}
我們首先給sendHttpRequest()
方法添加了一個HttpCallbackInterface
參數(shù),并在方法里開啟了一個子線程,然后在子線程中去執(zhí)行具體的網(wǎng)絡(luò)操作。
注意,子線程中時無法通過return語句來返回?cái)?shù)據(jù)的。
因此,我們這里將服務(wù)器返回的數(shù)據(jù)傳入了onSucceed()
方法中,如果出現(xiàn)異常就將異常傳入onFail()
方法中。
現(xiàn)在,當(dāng)我們需要調(diào)用sendHttpRequest()
進(jìn)行網(wǎng)絡(luò)請求時,
HttpUtil.sendHttpRequest(address, new HttpCallbackInterface() {
@Override
public void onSucceed(String response) {
//在這里根據(jù)返回內(nèi)容執(zhí)行具體操作
}
@Override
public void onFail(Exception e) {
//在這里對異常情況進(jìn)行處理
}
});
這樣的話,當(dāng)服務(wù)器成功響應(yīng)的時候我們就可以在onSucceed()
方法里對響應(yīng)數(shù)據(jù)進(jìn)行處理,類似地,如果出現(xiàn)了異常,就可以在onFail()
方法里對異常進(jìn)行處理。如此一來,我們就巧妙地利用回調(diào)機(jī)制將響應(yīng)數(shù)據(jù)成功返回給調(diào)用方了。
另外需要注意的是,onSucceed()
方法和onFail()
方法最終還是在子線程中運(yùn)行的,因此我們不可以在這里執(zhí)行任何的UI操作,如果需要根據(jù)返回的結(jié)果來更新UI,則仍然要使用異步消息處理機(jī)制(如使用Handler
-Looper
-MessageQueue
機(jī)制)。