日本搞逼视频_黄色一级片免费在线观看_色99久久_性明星video另类hd_欧美77_综合在线视频

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > 綜合技術 > 教你寫Android網絡框架之Request、Response類與請求隊列

教你寫Android網絡框架之Request、Response類與請求隊列

來源:程序員人生   發布時間:2015-01-30 08:14:59 閱讀次數:4687次

  轉載請注明出處,本文來自【 Mr.Simple的博客 】

我正在參加博客之星,點擊這里投我1票吧,謝謝~   

前言

在教你寫Android網絡框架之基本架構1文中我們已介紹了SimpleNet網絡框架的基本結構,今天我們就開始從代碼的角度來開始切入該網絡框架的實現,在剖析的同時我們會分析設計思路,和為何要這樣做,這樣做的好處是甚么。這樣我們不但學到了如何實現網絡框架,也會學到設計1個通用的框架應當有哪些斟酌,這就擴大到框架設計的范疇,通過這個簡單的實例希望能給新人1些幫助。固然這只是1家之言,大家都可以有自己的實現思路。

正如你所看到的,這系列博客是為新人準備的,如果你是高手,請疏忽。

在框架開發當中,很重要的1點就是抽象。也就是面向對象中重要的1條原則: 依賴顛倒原則,簡單來講就是要依賴抽象,而不依賴具體。這樣就使得我們的框架具有可擴大性,同時也滿足了開閉原則,即對擴大開放,對修改關閉。針對我們的網絡框架來講,最重要的抽象就是Reqeust類、Response類,因此今天我們就從兩個類開始切入。最后我們再引入網絡框架中的要求隊列(RequestQueue),這是SimpleNet中的中樞神經,所有的要求都需要放到該隊列,然后等待著被履行。要求隊列就像工廠中的流水線1樣,而網絡要求就像流水線上的待加工的產品。履行網絡要求的對象就類似工廠中的工人,在自己的崗位上等待流水線上傳遞過來的產品,然后對其加工,加工完就將產品放到其他的位置。它們角色對應關系參考圖1,如對SimpleNet的1些角色不太清楚可參考教你寫Android網絡框架之基本架構1文。

圖1


Request類

既然網絡框架,那末我們先從網絡要求類開始。前文已說過,既然是框架,那末就需要可擴大性。因此注定了Request是抽象,而不是具體。而對網絡要求來講,用戶得到的要求結果格式是不肯定,比如有的服務器返回的是json,有的返回的是xml,有的直接是字符串。但是對Http Response來講,它的返回數據類型都是Stream,也就是我們得到的原始數據都是2進制的流。所以在Request基類中我們必須預留方法來解析Response返回的具體類型,雖然返回的類型不同,但是他們的處理邏輯是1樣的,因此我們可把Request作為泛型類,它的泛型類型就是它的返回數據類型,比如Request<String>,那末它的返回數據類型就是String類型的。另外還有要求的優先級、可取消等,我們這里先給出核心代碼,然后再繼續分析。

/** * 網絡要求類. 注意GET和DELETE不能傳遞要求參數,由于其要求的性質而至,用戶可以將參數構建到url后傳遞進來到Request中. * * @author mrsimple * @param <T> T為要求返回的數據類型 */ public abstract class Request<T> implements Comparable<Request<T>> { /** * http要求方法枚舉,這里我們只有GET, POST, PUT, DELETE4種 * * @author mrsimple */ public static enum HttpMethod { GET("GET"), POST("POST"), PUT("PUT"), DELETE("DELETE"); /** http request type */ private String mHttpMethod = ""; private HttpMethod(String method) { mHttpMethod = method; } @Override public String toString() { return mHttpMethod; } } /** * 優先級枚舉 * * @author mrsimple */ public static enum Priority { LOW, NORMAL, HIGN, IMMEDIATE } /** * Default encoding for POST or PUT parameters. See * {@link #getParamsEncoding()}. */ private static final String DEFAULT_PARAMS_ENCODING = "UTF⑻"; /** * 要求序列號 */ protected int mSerialNum = 0; /** * 優先級默許設置為Normal */ protected Priority mPriority = Priority.NORMAL; /** * 是不是取消該要求 */ protected boolean isCancel = false; /** 該要求是不是應當緩存 */ private boolean mShouldCache = true; /** * 要求Listener */ protected RequestListener<T> mRequestListener; /** * 要求的url */ private String mUrl = ""; /** * 要求的方法 */ HttpMethod mHttpMethod = HttpMethod.GET; /** * 要求的header */ private Map<String, String> mHeaders = new HashMap<String, String>(); /** * 要求參數 */ private Map<String, String> mBodyParams = new HashMap<String, String>(); /** * @param method * @param url * @param listener */ public Request(HttpMethod method, String url, RequestListener<T> listener) { mHttpMethod = method; mUrl = url; mRequestListener = listener; } /** * 從原生的網絡要求中解析結果,子類覆寫 * * @param response * @return */ public abstract T parseResponse(Response response); /** * 處理Response,該方法運行在UI線程. * * @param response */ public final void deliveryResponse(Response response) { // 解析得到要求結果 T result = parseResponse(response); if (mRequestListener != null) { int stCode = response != null ? response.getStatusCode() : ⑴; String msg = response != null ? response.getMessage() : "unkown error"; mRequestListener.onComplete(stCode, result, msg); } } public String getUrl() { return mUrl; } public int getSerialNumber() { return mSerialNum; } public void setSerialNumber(int mSerialNum) { this.mSerialNum = mSerialNum; } public Priority getPriority() { return mPriority; } public void setPriority(Priority mPriority) { this.mPriority = mPriority; } protected String getParamsEncoding() { return DEFAULT_PARAMS_ENCODING; } public String getBodyContentType() { return "application/x-www-form-urlencoded; charset=" + getParamsEncoding(); } public HttpMethod getHttpMethod() { return mHttpMethod; } public Map<String, String> getHeaders() { return mHeaders; } public Map<String, String> getParams() { return mBodyParams; } public void cancel() { isCancel = true; } public boolean isCanceled() { return isCancel; } /** * 返回POST或PUT要求時的Body參數字節數組 * */ public byte[] getBody() { Map<String, String> params = getParams(); if (params != null && params.size() > 0) { return encodeParameters(params, getParamsEncoding()); } return null; } /** * 將參數轉換為Url編碼的參數串 */ private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) { StringBuilder encodedParams = new StringBuilder(); try { for (Map.Entry<String, String> entry : params.entrySet()) { encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding)); encodedParams.append('='); encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding)); encodedParams.append('&'); } return encodedParams.toString().getBytes(paramsEncoding); } catch (UnsupportedEncodingException uee) { throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee); } } // 用于對要求的排序處理,根據優先級和加入到隊列的序號進行排序 @Override public int compareTo(Request<T> another) { Priority myPriority = this.getPriority(); Priority anotherPriority = another.getPriority(); // 如果優先級相等,那末依照添加到隊列的序列號順序來履行 return myPriority.equals(another) ? this.getSerialNumber() - another.getSerialNumber() : myPriority.ordinal() - anotherPriority.ordinal(); } /** * 網絡要求Listener,會被履行在UI線程 * * @author mrsimple * @param <T> 要求的response類型 */ public static interface RequestListener<T> { /** * 要求完成的回調 * * @param response */ public void onComplete(int stCode, T response, String errMsg); } }

上述代碼Request<T>為抽象類,T則為該要求Response的數據格式。這個T是要求類中的1個比較重要的點,不同的人有不同的需求,即要求Reponse的數據格式其實不是都是1樣的,我們必須斟酌到要求返回類型的多樣性,用泛型T來表示返回的數據格式類型,然后Request子類覆寫對應的方法實現解析Response的數據格式,最后調用要求Listener將要求結果履行在UI線程,這樣全部要求就完成了。

每一個Request都有1個序列號,該序列號由要求隊列生成,標識該要求在隊列中的序號,該序號和要求優先級決定了該要求在隊列中的排序,即它在要求隊列的履行順序。每一個要求有要求方式,例如"POST"、"GET",這里我們用枚舉來代替,具名類型比單純的字符串更容易于使用。每一個Request都可以添加Header、Body參數 ( 關于要求參數的格式可以參考 4種常見的 POST 提交數據方式),并且可以取消。抽象類封裝了通用的代碼,只有可變的部份是抽象函數,這里只有parseResponse這個函數。

例如,我們返回的數據格式是Json,那末我們構建1個子類叫做JsonRequest,示例代碼以下。

/** * 返回的數據類型為Json的要求, Json對應的對象類型為JSONObject * * @author mrsimple */ public class JsonRequest extends Request<JSONObject> { public JsonRequest(HttpMethod method, String url, RequestListener<JSONObject> listener) { super(method, url, listener); } /** * 將Response的結果轉換為JSONObject */ @Override public JSONObject parseResponse(Response response) { String jsonString = new String(response.getRawData()); try { return new JSONObject(jsonString); } catch (JSONException e) { e.printStackTrace(); } return null; } }

可以看到,實現1個要求類還是非常簡單的,只需要覆寫parseResponse函數來解析你的要求返回的數據便可。這樣就保證了可擴大性,比如后面如果我想使用這個框架來做1個ImageLoader,那末我可以創建1個ImageRequest,該要求返回的類型就是Bitmap,那末我們只需要覆寫parseResponse函數,然后把結果轉換成Bitmap便可。

這里引入了Response類,這個Response類存儲了要求的狀態碼、要求結果等內容,我們繼續往下看。

Response類

每一個要求都對應1個Response,但這里的問題是這個Response的數據格式我們是不知道的。我們寫的是框架,不是利用。框架只是構建1個基本環境,并且附帶1些比較經常使用的類,比如這里的JsonRequest。但是重要的1點是可讓用戶自由、簡單的擴大以實現他的需求。對Response類來講,我們最重要的1點就是要肯定要求結果的數據格式類型。我們都知道,HTTP實際上是基于TCP協議,而TCP協議又是基于Socket,Socket實際上操作的也就是輸入、輸出流,輸出流是向服務器寫數據,輸入流自然是從服務器讀取數據。因此我們在Response類中應當使用InputStream存儲結果或使用更加易于使用的字節數組,這里我們使用字節數組來存儲。我們來看Response類。

/** * 要求結果類,繼承自BasicHttpResponse,將結果存儲在rawData中. * @author mrsimple */ public class Response extends BasicHttpResponse { public byte[] rawData = new byte[0]; public Response(StatusLine statusLine) { super(statusLine); } public Response(ProtocolVersion ver, int code, String reason) { super(ver, code, reason); } @Override public void setEntity(HttpEntity entity) { super.setEntity(entity); rawData = entityToBytes(getEntity()); } public byte[] getRawData() { return rawData; } public int getStatusCode() { return getStatusLine().getStatusCode(); } public String getMessage() { return getStatusLine().getReasonPhrase(); } /** Reads the contents of HttpEntity into a byte[]. */ private byte[] entityToBytes(HttpEntity entity) { try { return EntityUtils.toByteArray(entity); } catch (IOException e) { e.printStackTrace(); } return new byte[0]; } }

這個類很簡單,只是繼承了BasicHttpResponse,然后將輸入流轉換成字節數組,然后包裝了幾個經常使用的方法,主要是為了使用簡單吧。我們將結果存儲為字節數組,這樣可以用戶可以很方便的將結果轉換為String、bitmap等數據類型,如果直接存儲的是InputStream,那末在很多時候用戶需要在外圍將InputStream先轉換為字節數組,然后再轉換為終究的格式,例如InputStream轉為String類型。這也是為何我們這里選用byte[]而不用InputStream的緣由。


要求隊列

網絡要求隊列也比較簡單,實際上就是內部封裝了1個優先級隊列,在構建隊列時會啟動幾個NetworkExecutor ( 子線程 )來從要求隊列中獲得要求,并且履行要求。要求隊列會根據要求的優先級進行排序,這樣就保證了1些優先級高的要求得到盡快的處理,這也就是為何Request類中實現了Comparable接口的緣由。如果優先級1致的情況下,則會根據要求加入到隊列的順序來排序,這個序號由要求隊列生成,這樣就保證了優先級1樣的情況下依照FIFO的策略履行。

/** * 要求隊列, 使用優先隊列,使得要求可以依照優先級進行處理. [ Thread Safe ] * * @author mrsimple */ public final class RequestQueue { /** * 要求隊列 [ Thread-safe ] */ private BlockingQueue<Request<?>> mRequestQueue = new PriorityBlockingQueue<Request<?>>(); /** * 要求的序列化生成器 */ private AtomicInteger mSerialNumGenerator = new AtomicInteger(0); /** * 默許的核心數 */ public static int DEFAULT_CORE_NUMS = Runtime.getRuntime().availableProcessors() + 1; /** * CPU核心數 + 1個分發線程數 */ private int mDispatcherNums = DEFAULT_CORE_NUMS; /** * NetworkExecutor,履行網絡要求的線程 */ private NetworkExecutor[] mDispatchers = null; /** * Http要求的真正履行者 */ private HttpStack mHttpStack; /** * @param coreNums 線程核心數 */ protected RequestQueue(int coreNums, HttpStack httpStack) { mDispatcherNums = coreNums; mHttpStack = httpStack != null ? httpStack : HttpStackFactory.createHttpStack(); } /** * 啟動NetworkExecutor */ private final void startNetworkExecutors() { mDispatchers = new NetworkExecutor[mDispatcherNums]; for (int i = 0; i < mDispatcherNums; i++) { mDispatchers[i] = new NetworkExecutor(mRequestQueue, mHttpStack); mDispatchers[i].start(); } } public void start() { stop(); startNetworkExecutors(); } /** * 停止NetworkExecutor */ public void stop() { if (mDispatchers != null && mDispatchers.length > 0) { for (int i = 0; i < mDispatchers.length; i++) { mDispatchers[i].quit(); } } } /** * 不能重復添加要求 * * @param request */ public void addRequest(Request<?> request) { if (!mRequestQueue.contains(request)) { request.setSerialNumber(this.generateSerialNumber()); mRequestQueue.add(request); } else { Log.d("", "### 要求隊列中已含有"); } } public void clear() { mRequestQueue.clear(); } public BlockingQueue<Request<?>> getAllRequests() { return mRequestQueue; } /** * 為每一個要求生成1個系列號 * * @return 序列號 */ private int generateSerialNumber() { return mSerialNumGenerator.incrementAndGet(); } }

這里引入了1個HttpStack,這是1個接口,只有1個函數。該接口定義了履行網絡要求的抽象,代碼以下:

/** * 履行網絡要求的接口 * * @author mrsimple */ public interface HttpStack { /** * 履行Http要求 * * @param request 待履行的要求 * @return */ public Response performRequest(Request<?> request); }

今天就先到這里吧,關于HttpStack、NetworkExecutor、ResponseDelivery的介紹將在下1篇博客中更新,敬請期待。

如果你看到這里都不給我投1篇,那簡直太不夠意思了!點擊這里投我1票吧,謝謝~   

Github地址

SimpleNet網絡框架地址

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 亚洲精区二区三区四区麻豆 | 国产高清在线观看 | av另类 | 欧美一级网址 | 欧美成人精品在线 | 三区av | 欧美日在线 | 中文字幕在线观看一区二区三区 | 98色花堂最新地址网址 | 亚洲一区在线免费观看 | 久久av在线 | 久久www免费人成看片小草 | av影视大全| 毛片久久 | 久久久久久久久国产 | 性色av一区二区三区 | 91麻豆精品国产91久久久资源速度 | 能免费看av的网站 | 日韩 国产 欧美 | 国产成人精品久久久 | 国产经典一区二区三区 | 日韩精品视频国产 | 亚洲成年| 夜夜嗨av色综合久久久综合网 | 亚洲一二三区视频 | 久久男人 | 国产精品18久久久久久久网站 | 国产区在线 | 日韩三级电影 | 一区二区中文字幕 | 精品久久久999 | 免费黄色小视频 | 在线观看二区 | 成年人av网站 | 欧美精品一区二区三区蜜桃视频 | 久久久蜜桃 | 中文字幕一区二区三区在线观看 | 国产精品成av人在线视午夜片 | 不卡视频一区二区 | 黄包网站| 亚洲高清免费 |