Android應(yīng)用開(kāi)發(fā)過(guò)程中網(wǎng)絡(luò)相關(guān)操作筆記

利用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í)例代碼。

謝謝閱讀砸捏。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末谬运,一起剝皮案震驚了整個(gè)濱河市隙赁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌梆暖,老刑警劉巖伞访,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異轰驳,居然都是意外死亡厚掷,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門级解,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)冒黑,“玉大人,你說(shuō)我怎么就攤上這事勤哗÷盏” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵芒划,是天一觀的道長(zhǎng)冬竟。 經(jīng)常有香客問(wèn)我,道長(zhǎng)民逼,這世上最難降的妖魔是什么诱咏? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮缴挖,結(jié)果婚禮上袋狞,老公的妹妹穿的比我還像新娘。我一直安慰自己映屋,他們只是感情好苟鸯,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著棚点,像睡著了一般早处。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瘫析,一...
    開(kāi)封第一講書(shū)人閱讀 49,837評(píng)論 1 290
  • 那天砌梆,我揣著相機(jī)與錄音,去河邊找鬼贬循。 笑死咸包,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的杖虾。 我是一名探鬼主播烂瘫,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼奇适!你這毒婦竟也來(lái)了坟比?” 一聲冷哼從身側(cè)響起芦鳍,我...
    開(kāi)封第一講書(shū)人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎葛账,沒(méi)想到半個(gè)月后柠衅,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡籍琳,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年茄茁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片巩割。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖付燥,靈堂內(nèi)的尸體忽然破棺而出宣谈,到底是詐尸還是另有隱情,我是刑警寧澤键科,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布闻丑,位于F島的核電站,受9級(jí)特大地震影響勋颖,放射性物質(zhì)發(fā)生泄漏嗦嗡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一饭玲、第九天 我趴在偏房一處隱蔽的房頂上張望侥祭。 院中可真熱鬧,春花似錦茄厘、人聲如沸矮冬。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)胎署。三九已至,卻和暖如春窑滞,著一層夾襖步出監(jiān)牢的瞬間琼牧,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工哀卫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留巨坊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓此改,卻偏偏與公主長(zhǎng)得像抱究,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子带斑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容