現在已經會使用Http請求了,但是請求的代碼都是基本相同的,若每次都重寫一遍就太差勁了,通常應該把這些通用的網絡操作提取到一個公共類中,并提供一個靜態(tài)的方法,當想要發(fā)起網絡請求的時候,就簡單的調用這個方法就可以了
- 自定義一個類,HttpUtil
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();
return e.getMessage();
}finally {
if (connection != null){
connection.disconnect();
}
}
}
}
- 以后每當需要發(fā)起一條HTTP請求的時候就可以這樣寫了
String address = "http://www.baidu.com"
String response = HttpUtil.sendHttpRequest(address)
- 在獲取服務器響應的數據后,就可以對它進行解析和處理了,但是,要注意的是,網絡請求通常是屬于耗時的操作,而sendHttpRequest()方法的內部沒有開啟線程,這樣就有可能在調用sendHttpRequest()方法的時候使得主線程被阻塞
- 這個時候如果在這個sendHttpRequest()方法中開一個線程的話,
那么服務器響應的數據是無法進行返回的,所有的耗時邏輯都是在子線程中進行的,這個方法sendHttpRequest()會在服務器還沒來的及響應的時候就執(zhí)行結束了,就沒有辦法返回數據了
- 這個時候就需要使用java的回調機制,先定義一個接口,HttpCallbackListener
public interface HttpCallbackListener {
void onFinish(String response);
void onError(Exception e);
}
- 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);
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);
}
// 回調onFinish()
if (listener != null){
listener.onFinish(response.toString());
}
} catch (Exception e) {
// 回調onErroe()方法
if (listener != null){
listener.onError(e);
}
}finally {
if (connection != null){
connection.disconnect();
}
}
}
}).start();
}
}
- 首先給sendHttpRequest()方法中添加了一個HttpCallbackListener參數,并在方法的內部開啟了一個線程,然后在子線程里去執(zhí)行具體的操作
- 注意,子線程是沒有辦法通過return語句來返回數據的,因此這里將服務器響應的數據傳入到HttpCallbackListener的onFinish()方法中,如果異常的話就將異常原因傳入到onError()方法中
- 現在sendHttpRequest()方法接收的是兩個參數,調用的時候還需要將HttpCallbackListener實例傳入,如下
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String address = "http://www.baidu.com";
HttpUtil.sendHttpRequest(address, new HttpCallbackListener() {
@Override
public void onFinish(String response) {
// 這里根據返回的內容執(zhí)行具體的操作
}
@Override
public void onError(Exception e) {
// 這里對異常情況進行處理
}
});
}
}
- 當服務器響應的時候,就可以在onFinish()方法中對響應數據進行處理了,如果出現了異常就可以在onError()方法中進行處理了
- 當然了,使用上面的還有點稍微復雜,那么就是用OkHttp,在HttpUtil中加入一個sendOkHttpRequest()方法,如下:
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庫中自帶的一個回調接口,類似于我們剛才編寫的,然后在client.newCall()方法之后沒有像以前一樣execute()方法,而是調用enqueue()方法,這個方法已將幫我們開好了子線程,然后會在子線程中去執(zhí)行HTTP請求,并將結果回調到okhttp3.Callback當中
- 在調用這個sendOkHttpRequest()方法的時候就可以這樣寫
HttpUtil.sendOkHttpRequest("http://www.baidu.com", new okhttp3.Callback() {
@Override
public void onFailure(Call call, IOException e) {
//在這里對異常情況進行處理
}
@Override
public void onResponse(Call call, Response response) throws IOException {
// 得到服務器返回的具體內容
String responseData = response.body().string();
}
});
- 這個方法比自定義的好用多了,只需少量的代碼就可以完成復雜的網絡操作
- 另外就是不管使用HttpURLConnection還是OkHttp,最終的回調接口都還是在子線程中運行的,因此我們不可以在這里執(zhí)行任何的UI操作,除非借助runOnUiThread()方法來進行線程的轉換,至于為什么這樣,以后會學到的