利用Volley框架的可擴(kuò)展性自定義HttpRequest
上一篇博客中介紹了一下volley框架的原理與源碼解析缸血,事實(shí)上在實(shí)際開(kāi)發(fā)過(guò)程中展东,volley框架的自帶的JsonObjectRequest姑隅、JsonArrayRequest莱坎、ImageRequest已經(jīng)可以應(yīng)對(duì)大部分的場(chǎng)景,即使對(duì)于一些特殊的情況糙箍,如非json格式的POST請(qǐng)求渤愁、需要http基本驗(yàn)證的請(qǐng)求等,也可以通過(guò)重寫(xiě)Request<>抽象類深夯,自定義需要的請(qǐng)求與回調(diào)操作抖格,從而簡(jiǎn)化開(kāi)發(fā)過(guò)程中一些不必要的繁瑣操作。
以最近的課程作業(yè)Tss客戶端為例咕晋,服務(wù)器端提供了restful接口供客戶端調(diào)用并獲得數(shù)據(jù)雹拄,但所有接口都需要通過(guò)http基本驗(yàn)證。瀏覽器自帶保存base64驗(yàn)證序列的功能掌呜,但在android客戶端中為了避免每次請(qǐng)求都加上一次驗(yàn)證信息滓玖,因此我選擇通過(guò)重寫(xiě)volley框架中的Request<>自動(dòng)為已登陸的用戶添加驗(yàn)證信息,相關(guān)代碼如下:
public class HttpBasicJsonObjectRequest extends Request<JSONObject> {
private Listener<JSONObject> listener;
private SharedPreferences sp;
public HttpBasicJsonObjectRequest(String url, SharedPreferences sp, Listener<JSONObject> listener, ErrorListener errorListener) {
super(Method.GET, url, errorListener);
this.listener = listener;
this.sp = sp;
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
String base64 = sp.getString("auth", null);
Map<String, String> header = new HashMap<>();
header.put("Authorization", base64);
return header;
}
/*
@Override
protected Map<String, String> getParams() {
//some code
}
*/
@Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
return Response.success(new JSONObject(jsonString),HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
@Override
protected void deliverResponse(JSONObject response) {
listener.onResponse(response);
}
}
以上代碼的重寫(xiě)內(nèi)容包括:
構(gòu)造函數(shù)中額外傳入了來(lái)自Android Context的SharedPreferences站辉,這是經(jīng)常被用于在多個(gè)Activity間傳遞共享數(shù)據(jù)的類呢撞,我在這里也用它保存登陸成功后的base64驗(yàn)證數(shù)據(jù):
SharedPreferences sp = getSharedPreferences("auth", MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.clear();
editor.putString("auth", "Basic "+Base64.encodeToString((email+":"+password).getBytes(), Base64.DEFAULT));
重寫(xiě)getHeaders()方法,返回體為一個(gè)易于操作的Map饰剥,所以實(shí)現(xiàn)自動(dòng)Http驗(yàn)證的操作很簡(jiǎn)單殊霞,即在一個(gè)HashMap中添加一個(gè)鍵為"Authorization",值為base64驗(yàn)證碼的鍵值對(duì)并返回即可汰蓉,而base64驗(yàn)證碼通過(guò)SharedPreference傳遞也是非常方便的绷蹲。
對(duì)getParams()方法的重寫(xiě)主要用于實(shí)現(xiàn)一些Map<>數(shù)據(jù)格式的POST參數(shù),volley自帶的三種Request只支持JsonObject格式的參數(shù)顾孽,所以若需要傳入Map格式的參數(shù)時(shí)祝钢,除了提前把Map轉(zhuǎn)換為JsonObject,這種方式顯然更加直觀與簡(jiǎn)潔若厚。
deliverResponse (JSONObject response)方法與parseNetworkResponse (NetworkResponse response)方法的重寫(xiě)則是處理返回的Response的操作拦英,因?yàn)関olley自帶的JSONObject類有非常優(yōu)秀的直接把字符串轉(zhuǎn)換為json格式的對(duì)象的功能,因此這里的代碼非常簡(jiǎn)單测秸,如上所示的寫(xiě)法基本可以滿足我們的需求疤估。
下面是一個(gè)實(shí)際使用這個(gè)自定義Request的例子:
HttpBasicJsonObjectRequest request = new HttpBasicJsonObjectRequest(url, getSharedPreferences("auth", MODE_PRIVATE),
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
handleResponse(response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(getApplicationContext(), "HTTP驗(yàn)證失敗 請(qǐng)重新登陸", Toast.LENGTH_LONG).show();
startActivity(new Intent(QuestionAnalysisActivity.this, LoginActivity.class));
finish();
}
});
在因?yàn)橐恍┨厥庠驅(qū)е轮按鎯?chǔ)的http基本驗(yàn)證碼失效時(shí)灾常,便可以通過(guò)volley框架中的ErrorListener回調(diào),跳轉(zhuǎn)回登錄頁(yè)面铃拇,并清空之前的SharedPreference中存儲(chǔ)的內(nèi)容钞瀑。
圖片URL請(qǐng)求與Bitmap操作
通過(guò)URL獲得圖片的操作可以通過(guò)volley自帶的ImageRequest實(shí)現(xiàn),這種特殊的請(qǐng)求類的返回(回調(diào)參數(shù))為Bitmap慷荔,因此我們可以方便地使用ImageView的setImageBitmap方法顯示獲得的圖片資源雕什。一般來(lái)說(shuō)圖片的數(shù)據(jù)傳輸量遠(yuǎn)大于普通的rest請(qǐng)求,相對(duì)來(lái)說(shuō)比較耗時(shí)显晶,但volley框架自帶一個(gè)性能相當(dāng)優(yōu)秀的緩存機(jī)制贷岸,所以,雖然volley并不建議使用其進(jìn)行大數(shù)據(jù)量的網(wǎng)絡(luò)操作吧碾,但通過(guò)該緩存機(jī)制在實(shí)際應(yīng)用中可以避免很多重復(fù)的低速請(qǐng)求凰盔,一個(gè)簡(jiǎn)單的例子如下:
ImageRequest imageRequest = new ImageRequest(avatarUrl, new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap response) {
avatar.setImageBitmap(response);
}
}, 400, 400, Bitmap.Config.ALPHA_8, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(getApplicationContext(), "圖片加載失敗", Toast.LENGTH_SHORT).show();
}
});
傳參中可以自由地控制圖片的最大長(zhǎng)寬與顏色模式。
有一個(gè)很有意思的需求是倦春,希望加載出的圖片以圓形邊框呈現(xiàn)户敬。事實(shí)上這種需求有相當(dāng)多的實(shí)現(xiàn)方法,例如利用Bitmap的與或操作睁本,將原圖與一個(gè)圓形圖片相疊尿庐,非圓的地方會(huì)不再顯示∧匮撸或者繼承ImageView抄瑟,重寫(xiě)invalidate()、onDraw()等方法實(shí)現(xiàn)特殊的ImageView枉疼。
不過(guò)我們沒(méi)有必要重復(fù)造輪子皮假,如來(lái)自https://github.com/hdodenhof/CircleImageView 的CircleImageView便是一個(gè)優(yōu)秀的自定義ImageView實(shí)現(xiàn),它內(nèi)部通過(guò)對(duì)Paint骂维、Canvas惹资、BitmapShader、Matrix航闺、RectF類的操作實(shí)現(xiàn)一些必要的圓形化褪测、加邊框等功能,可以通過(guò)在gradle中直接引入
dependencies {
compile 'de.hdodenhof:circleimageview:2.1.0'
}
添加對(duì)其的依賴潦刃。在布局xml文件中直接將ImageView標(biāo)簽替換為CircleImageView標(biāo)簽便可直接操作一些可用的屬性侮措,如以下圓形化+邊框的xml代碼:
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/teacher_avatar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription=""
android:maxHeight="400sp"
android:maxWidth="400sp"
android:paddingBottom="20sp"
android:paddingTop="24sp"
app:civ_border_color="@color/grey"
app:civ_border_width="2dp"
app:srcCompat="@drawable/ic_image_black_24dp"
tools:ignore="ContentDescription" />
DownloadManager API介紹
當(dāng)需要進(jìn)行一些數(shù)據(jù)量較大的下載操作時(shí),之前涉及的一些網(wǎng)絡(luò)接口操作便不再適用乖杠。而Android API9之后自帶的DownloadManager組件便是用于處理這種場(chǎng)景分扎。相對(duì)來(lái)說(shuō)功能比較全面,使用也非常簡(jiǎn)潔胧洒,所以下面介紹一下DownloadManager的API與其功能畏吓。
DownloadManager對(duì)象獲得方式為調(diào)用Context的getSystemService (Context.DOWNLOAD_SERVICE)环揽。
addCompeletedDownload
long addCompletedDownload (String title, String description, boolean isMediaScannerScannable, String mimeType, String path, long length, boolean showNotification)
long addCompletedDownload (String title, String description, boolean isMediaScannerScannable, String mimeType, String path, long length, boolean showNotification, Uri uri, Uri referer)
該接口為API 24新增的接口,作用為將一個(gè)下載完成的文件加入Downloads App庵佣。參數(shù)分別為:
title 顯示在Downloads App中的標(biāo)題
description 顯示在Downloads App中的簡(jiǎn)介
isMediaScannerScannable 是否可以被MediaScanner檢測(cè)到
mimeType 文件類型
path 文件真實(shí)路徑
length 文件大小
showNotification 是否顯示通知
uri 下載的原始Http Uri
referer 下載來(lái)源Http referer部分
返回一個(gè)長(zhǎng)整型數(shù)據(jù),作為該文件的ID汛兜,方便其他地方獲得它
enqueue
long enqueue (DownloadManager.Request request)
DownloadManager.Request為DownloadManager的一個(gè)內(nèi)部類巴粪,含義為下載請(qǐng)求的Request對(duì)象,構(gòu)造一個(gè)request實(shí)例需要傳入下載鏈接Uri粥谬。
調(diào)用該方法即為將該鏈接加入下載隊(duì)列肛根,返回下載文件的一個(gè)長(zhǎng)整型ID。
getMaxBytesOverMobile
Long getMaxBytesOverMobile (Context context)
獲得Mobile Connection所支持的最大下載文件字節(jié)數(shù)漏策,若返回為Null則意為不做限制派哲,可以通過(guò)該方法判斷是否令要下載的文件僅在WIFI下可下載。
getRecommendedMaxBytesOverMobile
Long getRecommendedMaxBytesOverMobile (Context context)
同上掺喻,返回推薦字節(jié)大小
getMimeTypeForDownloadedFile
String getMimeTypeForDownloadedFile (long id)
獲得下載文件的文件格式芭届,傳入下載文件的id,即為之前的某幾個(gè)方法的返回值感耙,返回字符串表示的文件格式褂乍。
getUriForDownloadedFile
Uri getUriForDownloadedFile (long id)
傳入下載文件的id,獲得源下載鏈接的Uri即硼。
openDownloadedFile
ParcelFileDescriptor openDownloadedFile (long id)
傳入下載文件的id逃片,獲得一個(gè)只讀的文件描述符,利用ParcelFileDescriptor可進(jìn)行相應(yīng)的文件操作只酥。
query
Cursor query (DownloadManager.Query query)
Query對(duì)象通過(guò)setsetFilterById(long reference)方法與某個(gè)下載任務(wù)綁定褥实,通過(guò)傳入該對(duì)象可以返回一個(gè)Cursor類型的對(duì)象,而Cursor是一個(gè)關(guān)于該下載的結(jié)果集裂允∷鹄耄可以通過(guò)該方法來(lái)查詢某個(gè)下載的狀態(tài)。
remove
int remove (long... ids)
通過(guò)傳入一系列下載任務(wù)的ID來(lái)取消它們的下載操作叫胖,返回值為一個(gè)int草冈,代表被實(shí)際取消的下載數(shù)。
總之DownloadManager通過(guò)一些簡(jiǎn)潔的接口瓮增,封裝了繁瑣的網(wǎng)絡(luò)連接操作怎棱,幫助開(kāi)發(fā)者實(shí)現(xiàn)一些復(fù)雜的下載過(guò)程,具體案例相對(duì)比較豐富绷跑,因此不在此給出實(shí)踐代碼拳恋。
http://blog.csdn.net/lingjianglin/article/details/51658315 便為一個(gè)利用DownloadManager實(shí)現(xiàn)軟件自動(dòng)更新的實(shí)例代碼。
謝謝閱讀砸捏。