原文鏈接:http://blog.csdn.net/bboyfeiyu/article/details/43022703
前言
在《教你寫Android網絡框架》專欄的前兩篇博客中,我們已經介紹了SimpleNet框架的基本結構,以及Request、Response、請求隊列的實現,以及爲什麼要這麼設計,這麼設計的考慮是什麼。前兩篇博客中已經介紹了各個角色,今天我們就來剖析另外幾個特別重要的角色,即NetworkExecutor、HttpStack以及ResponseDelivery,它們分別對應的功能是網絡請求線程、Http執行器、Response分發,這三者是執行http請求和處理Response的核心。
我們再來回顧一下,SimpleNet各個角色的分工合作。首先用戶需要創建一個請求隊列,然後將各個請求添加到請求隊列中。多個NetworkExecutor ( 實質上是一個線程 )共享一個消息隊列,在各個NetworkExecutor中循環的取請求隊列中的請求,拿到一個請求,然後通過HttpStack來執行Http請求,請求完成後最終通過ResponseDelivery將Response結果分發到UI線程,保證請求回調執行在UI線程,這樣用戶就可以直接在回調中更新UI。執行流程如圖1.
圖1
還有不太瞭解這幅架構圖的可以參考專欄中的第一篇博客。
NetworkExecutor
作爲SimpleNet中的“心臟”,NetworkExecutor起着非常重要的作用。之所以稱之爲“心臟”,是由於NetworkExecutor的功能是源源不斷地從請求隊列中獲取請求,然後交給HttpStack來執行。它就像汽車中的發動機,人體中的心臟一樣,帶動着整個框架的運行。
NetworkExecutor實質上是一個Thread,在run方法中我們會執行一個循環,不斷地從請求隊列中取得請求,然後交給HttpStack,由於比較簡單我們直接上代碼吧。
- /**
- * 網絡請求Executor,繼承自Thread,從網絡請求隊列中循環讀取請求並且執行
- *
- * @author mrsimple
- */
- final class NetworkExecutor extends Thread {
- /**
- * 網絡請求隊列
- */
- private BlockingQueue<Request<?>> mRequestQueue;
- /**
- * 網絡請求棧
- */
- private HttpStack mHttpStack;
- /**
- * 結果分發器,將結果投遞到主線程
- */
- private static ResponseDelivery mResponseDelivery = new ResponseDelivery();
- /**
- * 請求緩存
- */
- private static Cache<String, Response> mReqCache = new LruMemCache();
- /**
- * 是否停止
- */
- private boolean isStop = false;
- public NetworkExecutor(BlockingQueue<Request<?>> queue, HttpStack httpStack) {
- mRequestQueue = queue;
- mHttpStack = httpStack;
- }
- @Override
- public void run() {
- try {
- while (!isStop) {
- final Request<?> request = mRequestQueue.take();
- if (request.isCanceled()) {
- Log.d("### ", "### 取消執行了");
- continue;
- }
- Response response = null;
- if (isUseCache(request)) {
- // 從緩存中取
- response = mReqCache.get(request.getUrl());
- } else {
- // 從網絡上獲取數據
- response = mHttpStack.performRequest(request);
- // 如果該請求需要緩存,那麼請求成功則緩存到mResponseCache中
- if (request.shouldCache() && isSuccess(response)) {
- mReqCache.put(request.getUrl(), response);
- }
- }
- // 分發請求結果
- mResponseDelivery.deliveryResponse(request, response);
- }
- } catch (InterruptedException e) {
- Log.i("", "### 請求分發器退出");
- }
- }
- private boolean isSuccess(Response response) {
- return response != null && response.getStatusCode() == 200;
- }
- private boolean isUseCache(Request<?> request) {
- return request.shouldCache() && mReqCache.get(request.getUrl()) != null;
- }
- public void quit() {
- isStop = true;
- interrupt();
- }
- }
HttpStack
HttpStack只是一個接口,只有一個performRequest函數,也就是執行請求。
- /**
- * 執行網絡請求的接口
- *
- * @author mrsimple
- */
- public interface HttpStack {
- /**
- * 執行Http請求
- *
- * @param request 待執行的請求
- * @return
- */
- public Response performRequest(Request<?> request);
- }
HttpStack是網絡請求的真正執行者,有HttpClientStack和HttpUrlConnStack,兩者分別爲Apache的HttpClient和java的HttpURLConnection,關於這兩者的區別請參考:Android訪問網絡,使用HttpURLConnection還是HttpClient? 默認情況下,我們會根據api版本來構建對應的HttpStack,當然用戶也可以自己實現一個HttpStack,然後通過SimpleNet的工廠函數傳遞進來。
例如 :
- /**
- * @param coreNums 線程核心數
- * @param httpStack http執行器
- */
- protected RequestQueue(int coreNums, HttpStack httpStack) {
- mDispatcherNums = coreNums;
- mHttpStack = httpStack != null ? httpStack : HttpStackFactory.createHttpStack();
- }
- /**
- * 根據api版本選擇HttpClient或者HttpURLConnection
- *
- * @author mrsimple
- */
- public final class HttpStackFactory {
- private static final int GINGERBREAD_SDK_NUM = 9;
- /**
- * 根據SDK版本號來創建不同的Http執行器,即SDK 9之前使用HttpClient,之後則使用HttlUrlConnection,
- * 兩者之間的差別請參考 :
- * http://android-developers.blogspot.com/2011/09/androids-http-clients.html
- *
- * @return
- */
- public static HttpStack createHttpStack() {
- int runtimeSDKApi = Build.VERSION.SDK_INT;
- if (runtimeSDKApi >= GINGERBREAD_SDK_NUM) {
- return new HttpUrlConnStack();
- }
- return new HttpClientStack();
- }
- }
HttpClientStack和HttpUrlConnStack分別就是封裝了HttpClient和HttpURLConnection的http請求,構建請求、設置header、設置請求參數、解析Response等操作。針對於這一層,我們沒有給出一個抽象類,原因是HttpClient和HttpURLConnection並不屬於同一個類族,他們的行爲雖然都很相似,但是其中涉及到的一些類型卻是不同的。這裏我們給出HttpUrlConnStack的示例,最近比較忙,因此寫的配置比較簡單,有需要的同學自己優化了。
- /**
- * 使用HttpURLConnection執行網絡請求的HttpStack
- *
- * @author mrsimple
- */
- public class HttpUrlConnStack implements HttpStack {
- /**
- * 配置Https
- */
- HttpUrlConnConfig mConfig = HttpUrlConnConfig.getConfig();
- @Override
- public Response performRequest(Request<?> request) {
- HttpURLConnection urlConnection = null;
- try {
- // 構建HttpURLConnection
- urlConnection = createUrlConnection(request.getUrl());
- // 設置headers
- setRequestHeaders(urlConnection, request);
- // 設置Body參數
- setRequestParams(urlConnection, request);
- // https 配置
- configHttps(request);
- return fetchResponse(urlConnection);
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- if (urlConnection != null) {
- urlConnection.disconnect();
- }
- }
- return null;
- }
- private HttpURLConnection createUrlConnection(String url) throws IOException {
- URL newURL = new URL(url);
- URLConnection urlConnection = newURL.openConnection();
- urlConnection.setConnectTimeout(mConfig.connTimeOut);
- urlConnection.setReadTimeout(mConfig.soTimeOut);
- urlConnection.setDoInput(true);
- urlConnection.setUseCaches(false);
- return (HttpURLConnection) urlConnection;
- }
- private void configHttps(Request<?> request) {
- if (request.isHttps()) {
- SSLSocketFactory sslFactory = mConfig.getSslSocketFactory();
- // 配置https
- if (sslFactory != null) {
- HttpsURLConnection.setDefaultSSLSocketFactory(sslFactory);
- HttpsURLConnection.setDefaultHostnameVerifier(mConfig.getHostnameVerifier());
- }
- }
- }
- private void setRequestHeaders(HttpURLConnection connection, Request<?> request) {
- Set<String> headersKeys = request.getHeaders().keySet();
- for (String headerName : headersKeys) {
- connection.addRequestProperty(headerName, request.getHeaders().get(headerName));
- }
- }
- protected void setRequestParams(HttpURLConnection connection, Request<?> request)
- throws ProtocolException, IOException {
- HttpMethod method = request.getHttpMethod();
- connection.setRequestMethod(method.toString());
- // add params
- byte[] body = request.getBody();
- if (body != null) {
- // enable output
- connection.setDoOutput(true);
- // set content type
- connection
- .addRequestProperty(Request.HEADER_CONTENT_TYPE, request.getBodyContentType());
- // write params data to connection
- DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream());
- dataOutputStream.write(body);
- dataOutputStream.close();
- }
- }
- private Response fetchResponse(HttpURLConnection connection) throws IOException {
- // Initialize HttpResponse with data from the HttpURLConnection.
- ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
- int responseCode = connection.getResponseCode();
- if (responseCode == -1) {
- throw new IOException("Could not retrieve response code from HttpUrlConnection.");
- }
- // 狀態行數據
- StatusLine responseStatus = new BasicStatusLine(protocolVersion,
- connection.getResponseCode(), connection.getResponseMessage());
- // 構建response
- Response response = new Response(responseStatus);
- // 設置response數據
- response.setEntity(entityFromURLConnwction(connection));
- addHeadersToResponse(response, connection);
- return response;
- }
- /**
- * 執行HTTP請求之後獲取到其數據流,即返回請求結果的流
- *
- * @param connection
- * @return
- */
- private HttpEntity entityFromURLConnwction(HttpURLConnection connection) {
- BasicHttpEntity entity = new BasicHttpEntity();
- InputStream inputStream = null;
- try {
- inputStream = connection.getInputStream();
- } catch (IOException e) {
- e.printStackTrace();
- inputStream = connection.getErrorStream();
- }
- // TODO : GZIP
- entity.setContent(inputStream);
- entity.setContentLength(connection.getContentLength());
- entity.setContentEncoding(connection.getContentEncoding());
- entity.setContentType(connection.getContentType());
- return entity;
- }
- private void addHeadersToResponse(BasicHttpResponse response, HttpURLConnection connection) {
- for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
- if (header.getKey() != null) {
- Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
- response.addHeader(h);
- }
- }
- }
- }
ResponseDelivery
在HttpStack的performRequest函數中,我們會返回一個Response對象,該對象包含了我們請求對應的Response。關於Response類你不太瞭解的可以參考教你寫Android網絡框架之Request、Response類與請求隊列。我們在NetworkExecutor中執行http請求的最後一步會將結果分發給UI線程,主要工作其實就是將請求的回調執行到UI線程,以便用戶可以更新UI等操作。
- @Override
- public void run() {
- try {
- while (!isStop) {
- final Request<?> request = mRequestQueue.take();
- if (request.isCanceled()) {
- Log.d("### ", "### 取消執行了");
- continue;
- }
- Response response = null;
- if (isUseCache(request)) {
- // 從緩存中取
- response = mReqCache.get(request.getUrl());
- } else {
- // 從網絡上獲取數據
- response = mHttpStack.performRequest(request);
- // 如果該請求需要緩存,那麼請求成功則緩存到mResponseCache中
- if (request.shouldCache() && isSuccess(response)) {
- mReqCache.put(request.getUrl(), response);
- }
- }
- // 分發請求結果
- mResponseDelivery.deliveryResponse(request, response);
- }
- } catch (InterruptedException e) {
- Log.i("", "### 請求分發器退出");
- }
- }
ResponseDelivery其實就是封裝了關聯了UI線程消息隊列的Handler,在deliveryResponse函數中將request的deliveryResponse執行在UI線程中。既然我們有了關聯了UI線程的Handler對象,那麼直接構建一個Runnable,在該Runnable中執行request的deliveryResponse函數即可。在Request類的deliveryResponse中,又會調用parseResponse解析Response結果,返回的結果類型就是Request<T>中的T,這個T是在Request子類中指定,例如JsonRequest,那麼返回的Response的結果就是JSONObject。這樣我們就得到了服務器返回的json數據,並且將這個json結果通過回調的形式傳遞給了UI線程。用戶就可以在該回調中更新UI了。
這其中主要就是抽象和泛型,寫框架很多時候泛型是很重要的手段,因此熟悉使用抽象和泛型是面向對象開發的重要一步。
ResponseDelivery代碼如下 :
- /**
- * 請求結果投遞類,將請求結果投遞給UI線程
- *
- * @author mrsimple
- */
- class ResponseDelivery implements Executor {
- /**
- * 主線程的hander
- */
- Handler mResponseHandler = new Handler(Looper.getMainLooper());
- /**
- * 處理請求結果,將其執行在UI線程
- *
- * @param request
- * @param response
- */
- public void deliveryResponse(final Request<?> request, final Response response) {
- Runnable respRunnable = new Runnable() {
- @Override
- public void run() {
- request.deliveryResponse(response);
- }
- };
- execute(respRunnable);
- }
- @Override
- public void execute(Runnable command) {
- mResponseHandler.post(command);
- }
- }
Request類的deliveryResponse函數。
- /**
- * 處理Response,該方法運行在UI線程.
- *
- * @param response
- */
- public final void deliveryResponse(Response response) {
- T result = parseResponse(response);
- if (mRequestListener != null) {
- int stCode = response != null ? response.getStatusCode() : -1;
- String msg = response != null ? response.getMessage() : "unkown error";
- mRequestListener.onComplete(stCode, result, msg);
- }
- }
這樣,整個請求過程就完成了。下面我們總結一下這個過程。
不同用戶的服務器返回的數據格式是不一致的,因此我們定義了Request<T>泛型基類,泛型T就是返回的數據格式類型。比如返回的數據格式爲json,那對應的請求就是JsonRequest,泛型T爲JSONObject,在JsonRequest中覆寫parseResponse函數,將得到的Response中的原始數據轉換成JSONObject。然後將請求放到隊列中,NetworkExecutor將請求分發給HttpStack執行,執行完成之後得到Response對象,最終ResponseDelivery將結果通過請求回調投遞到UI線程。