張延年 米洪
摘 要:一般在Android應(yīng)用開發(fā)過程中都會涉及到圖片處理的問題,包括圖片的顯示、下載、上傳、壓縮等方面,隨著應(yīng)用中要處理的圖片數(shù)量的增加,如何快速而高效的對圖片進(jìn)行處理變得越來越重要。本文主要通過對幾種比較成熟的圖片處理開源框架的研究,結(jié)合實際項目案例詳細(xì)闡述了圖片高效處理的方法和步驟,同時對各處理方法的適用范圍、優(yōu)勢以及注意事項進(jìn)行了分析和說明。
關(guān)鍵詞: Android應(yīng)用開發(fā);圖片處理;開源框架
中圖分類號:TP311 文獻(xiàn)標(biāo)識號:A 文章編號:2095-2163(2015)06-
Abstract: Android application development process in general will be related to the problem of image processing, including image display, download, upload, compression, etc.. Along with the increase in the number of images to deal with, how to deal with the image quickly and efficiently becomes more and more important. This paper mainly studies the open source framework of several more mature image processing, and combined with the actual project case, this paper expounds the methods and steps of the image processing. Based on the aboved, the paper also analyzes and explains the application scope, advantages and points of attention.
Key words:Android Application Development; Image Processing; Open Source Framework
0引 言
時下,在Android應(yīng)用程序開發(fā)過程中對圖片的處理問題呈現(xiàn)出高頻的現(xiàn)實關(guān)注度,因此對其看站探索研究,勢將具有重大應(yīng)用價值。雖然一般的教科書上都會介紹圖片處理的基本方法,但對于如何在實際項目開發(fā)中根據(jù)用戶需求簡捷、高效地實現(xiàn)圖片處理功能這一重要內(nèi)容卻大都未予涉及。在早期的Android應(yīng)用開發(fā)過程中常規(guī)的解決方案多是通過使用Android SDK提供的相關(guān)類庫自定義相關(guān)方法,比如圖片下載需要使用openConnection方法獲取HttpURLConnection連接對象,然后使用getInputStream方法獲取圖片的輸入流,再將輸入流轉(zhuǎn)換成Bitmap,最后借助Handler更新UI,顯示下載的圖片。這種解決方案雖然能實現(xiàn)項目的功能需求,但存在編碼工作量大、代碼無法復(fù)用、圖片處理效率較低等問題,特別是對一些特定的應(yīng)用場景(比如大圖片處理、批量下載、斷點(diǎn)續(xù)傳等)將無法滿足用戶的客觀高端需求?;诖耍瑸榻鉀Q以上問題出現(xiàn)了一些針對圖片處理的開源框架,隨著這些框架的日漸成熟,越來越多的開發(fā)者更傾向于借助開源框架來完成圖片處理的功能。圖片處理的內(nèi)容包含很多方面,而其中網(wǎng)絡(luò)傳輸過程的圖片處理問題對程序的效率和性能影響堪稱首要顯著,因此本文重點(diǎn)討論如何高效地實現(xiàn)網(wǎng)絡(luò)傳輸過程中圖片的下載、上傳和壓縮功能。
1圖片下載
由于在Android應(yīng)用中不能在UI線程中執(zhí)行耗時操作[1],因此Android中圖片的網(wǎng)絡(luò)加載必須通過異步操作實施完成,也就是必須單獨(dú)開啟一個子線程來負(fù)責(zé)圖片下載任務(wù)[2],當(dāng)任務(wù)完成后將采用Handler機(jī)制通知UI線程更新界面。在ListView或GridView中加載圖片的過程可做如下描述:首先嘗試從內(nèi)存緩存中獲取,取到則返回,否則可試著從文件緩存中獲取,取到則返回并更新到內(nèi)存緩存,若仍落空則最后只能從網(wǎng)絡(luò)上下載,由此而更新內(nèi)存緩存和文件緩存 。評析上述過程可知,首先,實現(xiàn)這一指定功能至少要實現(xiàn)文件緩存類、內(nèi)存緩存類、圖片加載類,需要編寫的代碼相當(dāng)多。其次,若要實現(xiàn)圖片的高效、流暢下載還必須對代碼進(jìn)行全面優(yōu)化[3]。第三,如果項目具有特殊的需求,比如超大圖片或批量圖片的下載,則需要考慮內(nèi)存優(yōu)化、圖片壓縮等更多方面的技術(shù)要求。基于以上考慮,下面將研究采用兩種較為成熟的開源框架快速打造自己的圖片下載功能。
1.1使用Android-Universal-Image-Loader框架
一般情況下,Android應(yīng)用圖片下載都是使用HttpURLConnection對象來實現(xiàn)并完成[3],因此最直接的辦法就是使用此對象自定義下載圖片方法,但這種方式需要編寫較多的代碼,而且運(yùn)行效率和靈活性也較差,于當(dāng)前的實際項目開發(fā)中已很少采用。Android-Universal-Image-Loader框架是一個專門用于圖片下載的開源項目,能夠較好地實現(xiàn)圖片的異步下載功能,因而可以滿足大部分應(yīng)用對圖片下載功能的需求。
使用此框架首先要從以下網(wǎng)址:
https://github.com/nostra13/Android-Universal-Image-Loader 下載jar包:
universal-image-loader-1.8.6-with-sources.jar。
此后,通過一個較為簡單的案例來說明如何使用該框架實現(xiàn)圖片的下載。
1.1.1 圖片下載過程
(1)初始化ImageLoaderConfiguration對象(全局的,在整個application中初始化configuration,配置緩存、加載線程等)
ImageLoaderConfiguration config = new ImageLoaderConfiguration{ … }
以上的配置選項有很多,可以根據(jù)個人需求進(jìn)行選擇,不是所有都要進(jìn)行配置。
配置了ImageLoaderConfiguration后,調(diào)用以下方法來實現(xiàn)初始化:
ImageLoader.getInstance().init(config); //全局初始化此配置
注意:ImageLoaderConfiguration 配置中的.discCacheFileNameGenerator()方法是將緩存下來的文件以什么方式命名,其中可以調(diào)用的方法有:
new Md5FileNameGenerator() //使用MD5對UIL進(jìn)行加密命名
new HashCodeFileNameGenerator() //使用HASHCODE對UIL進(jìn)行加密命名
(2)創(chuàng)建圖片顯示選項:DisplayImageOptions options(根據(jù)不同的加載圖片的相應(yīng)顯示而構(gòu)造不同的選項,主要設(shè)定圖片加載過程中顯示配置,緩存,顯示動畫)。
DisplayImageOptions options;
options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.ic_launcher)// 設(shè)置圖片在下載期間顯示的圖片
…
以上的配置選項有很多,可以根據(jù)個人需求進(jìn)行選擇,但并非所有都需要進(jìn)行配置。
(3)通過ImageLoader下載并顯示圖片。
ImageLoader imageLoader = ImageLoader.getInstance(); // 使用
ImageLoader進(jìn)行圖片加載的時候,先要實例化ImageLoader,調(diào)用以下方法進(jìn)行實例化,而且在每個布局里面都要實例化后再使用。
imageLoader.displayImage(imageUrls[position], holder.image, options, loadingListener);// 第一個參數(shù)是uri,第二個參數(shù)是顯示圖片的imageView,第三個參數(shù)是最新構(gòu)造完成的圖片顯示選項,第四個參數(shù)是加載的回調(diào)方法,displayImage有很多重載方法,這只是其中一種。
1.1.2 框架優(yōu)勢分析
通過對以上案例的測試和分析,會發(fā)現(xiàn)此框架主要具有如下優(yōu)勢:
(1)多線程下載圖片,圖片可以來源于網(wǎng)絡(luò),文件系統(tǒng),項目文件夾assets以及drawable等;
(2)支持隨意配置ImageLoader,例如線程池、圖片下載器、內(nèi)存緩存策略,硬盤緩存策略、圖片顯示選項以及其他的一些配置;
(3)支持圖片的內(nèi)存緩存,文件系統(tǒng)緩存或者SD卡緩存;
(4)支持圖片下載過程的監(jiān)聽;
(5)根據(jù)控件(ImageView)的大小對Bitmap進(jìn)行裁剪,嚴(yán)格Bitmap占用過多的內(nèi)存;
(6)較好地控制圖片的加載過程,例如暫停圖片加載,重新開始加載圖片,一般使用在ListView,GridView中?;瑒舆^程中暫停加載圖片,停止滑動時則實行圖片加載;
(7)在較慢的網(wǎng)絡(luò)下對圖片提供加載功能。
1.2使用volley框架
Volley是谷歌公司于2013年發(fā)布的Android平臺上的網(wǎng)絡(luò)通信庫,能使網(wǎng)絡(luò)通信更快,更簡單,更健壯,并且更加適用于通信頻繁但數(shù)據(jù)量不大的場景。
使用此框架首先要從以下網(wǎng)址:
https://github.com/mcxiaoke/android-volley 下載項目并導(dǎo)出jar包。
volley框架本身提供了幾種主要的圖片下載方法,下面通過小案例對其進(jìn)行詳細(xì)說明。
1.2.1 使用ImageRequest下載圖片
首先需要獲取到一個RequestQueue對象,可以調(diào)用如下方法獲取得到:
RequestQueue mQueue = Volley.newRequestQueue(context);
接下來需要實例化一個ImageRequest對象了,代碼如下所示:
ImageRequest imageRequest = new ImageRequest(
"http://www.baidu.com/img/bdlogo.png",
new Response.Listener() {
… }
});
在此之后,將這個ImageRequest對象添加到RequestQueue里即可,如下所示:
mQueue.add(imageRequest);
1.2.2 使用ImageLoader下載圖片
首先,新建一個ImageLoader對象,代碼如下所示:
ImageLoader imageLoader = new ImageLoader(mQueue, new ImageCache() {
public void putBitmap(String url, Bitmap bitmap) {
}
public Bitmap getBitmap(String url) {
return null;
}
});
可以看到,ImageLoader的構(gòu)造函數(shù)接收兩個參數(shù),第一個參數(shù)就是RequestQueue對象,第二個參數(shù)是一個ImageCache對象,這里首先新建一個空的ImageCache的實現(xiàn)即可得到。
接下來需要獲取一個ImageListener對象,代碼如下所示:
ImageListener listener = ImageLoader.getImageListener(imageView,
R.drawable.default_image, R.drawable.failed_image);
研究中,通過調(diào)用ImageLoader的getImageListener()方法能夠獲取到一個ImageListener對象,getImageListener()方法接收三個參數(shù),第一個參數(shù)指定用于顯示圖片的ImageView控件,第二個參數(shù)指定加載圖片的過程中顯示的圖片,第三個參數(shù)指定加載圖片失敗的情況下顯示的圖片。
最后,調(diào)用ImageLoader的get()方法來加載圖片,代碼如下所示:
imageLoader.get("http://www.baidu.com/img/bdlogo.png", listener);
get()方法接收兩個參數(shù),第一個參數(shù)就是圖片的URL地址,第二個參數(shù)則是新近獲取得到的ImageListener對象。
1.2.3 NetworkImageView的用法
除了以上兩種方式之外,Volley還提供了第三種方式來加載網(wǎng)絡(luò)圖片,即使用NetworkImageView。不同于以上兩種方式,NetworkImageView是一個自定義控件,是繼承自ImageView的,具備ImageView控件的所有功能,并且在原生的基礎(chǔ)之上引入了加載網(wǎng)絡(luò)圖片的功能。NetworkImageView控件的用法要比前兩種方式更加簡單,大致可以分為以下五步:
(1)創(chuàng)建一個RequestQueue對象。
(2)創(chuàng)建一個ImageLoader對象。
(3)在布局文件中添加一個NetworkImageView控件。
(4)在代碼中獲取該控件的實例。
(5)設(shè)置要加載的圖片地址。
1.2.4 volley框架的使用特點(diǎn)
(1)volley 適用于輕量高并發(fā)的網(wǎng)絡(luò)請求, 能夠請求網(wǎng)絡(luò)的數(shù)據(jù)全部保存在內(nèi)存中,因此不適合請求較大的數(shù)據(jù),比如大文件、流媒體等。
(2)同一個程序中最好共用一個 RequestQueue。
(3)可以根據(jù)接口的放回數(shù)據(jù)類型定制任意的 Request,volley 已經(jīng)默認(rèn)為我們實現(xiàn)了 StringRequest、JsonArrayRequest、JsonObjectRequest、ImageRequest四個請求類型。
2圖片上傳
圖片上傳在Android應(yīng)用開發(fā)中也是一個會經(jīng)常遇到的問題,比如用戶頭像的上傳、身份證照片的上傳等。上傳圖片的功能其實就是獲得圖片的路徑,創(chuàng)建文件,并將圖片轉(zhuǎn)化為字節(jié)流寫入到request[4],再發(fā)送該請求,因此如果完全由用戶自身實現(xiàn)就要使用到HttpURLConnection 類和輸入輸出流,同事需要編寫較多的代碼。這里為了實現(xiàn)圖片的高效上傳,研究仍然采用了開源框架的方式,現(xiàn)介紹以下兩種較為常用的開源框架。
2.1使用android-async-http框架
使用此框架首先要從以下網(wǎng)址:
https://github.com/loopj/android-async-http下載項目并導(dǎo)出jar包。
使用此框架上傳文件比較簡單,主要通過RequestParams類就可以完成文件的上傳,關(guān)鍵代碼如下。
(1)添加文件對象用于上傳
File myFile = new File("/path/to/myPic.png");
RequestParams params = new RequestParams();
try {
params.put("profile_picture", myFile);
} catch(FileNotFoundException e) {}
(2)添加字節(jié)數(shù)組用于上傳
byte[] myByteArray = bytes;
RequestParams params = new RequestParams();
params.put("file", new ByteArrayInputStream(myByteArray), " myPic.png"");
2.2使用xUtils框架
使用此框架首先要從以下網(wǎng)址:https://github.com/wyouflf/xUtils下載項目并導(dǎo)出jar包。
2.2.1 使用xUtils框架上傳圖片的步驟
(1)創(chuàng)建并實例化HttpUtils類。
(2)創(chuàng)建并實例化HttpParams對象。
(3)創(chuàng)建并實例化HttpHandler對象, 將DefaultHttpClient的實例通過參數(shù)形式,傳給HttpHandler。
(4)創(chuàng)建并實例化HttpRequest對象,在此對象中,封裝了method、 url 以及params后,再通過HttpHandler對象來執(zhí)行這一request,并最終調(diào)用DefaultHttpClient來執(zhí)行同一request, 返回HttpResponse。
2.2.2 框架在圖片上傳方面的特點(diǎn)優(yōu)勢
(1)支持大文件上傳,上傳大文件不會oom(內(nèi)存耗盡)問題[5];
(2)加載bitmap的時候,無需考慮bitmap加載過程中出現(xiàn)的oom問題;另外,有關(guān)android容器快速滑動時印發(fā)的圖片錯位等現(xiàn)象也已一并消除;
(3)內(nèi)存管理使用lru算法,實現(xiàn)了對bitmap內(nèi)存的搞笑管理;
(4)可配置線程加載線程數(shù)量,緩存大小,緩存路徑,加載顯示動畫。
3圖片壓縮
在android應(yīng)用開發(fā)中,壓縮圖片的目的有兩個:第一,避免占用內(nèi)存過多;第二,如果要上傳圖片,而圖片又較大,則會耗費(fèi)流量,這時就需要對其進(jìn)行優(yōu)質(zhì)壓縮。
(1)避免內(nèi)存過多的壓縮方法
圖片是要顯示在界面組件上的,相應(yīng)地就要用到Bitmap。 Bitmap在內(nèi)存中的大小只和圖片尺寸及色彩模式有關(guān),若想改變Bitmap在內(nèi)存中的大小,或者選擇改變尺寸,或者選擇改變色彩模式[6]。
(2)避免上傳費(fèi)流量的壓縮方法
具體來說,既可改變圖片尺寸,也可改變色彩模式,或者改變圖片質(zhì)量都行。正常情況下,需首先改變圖片尺寸和色彩模式,其后改變圖片質(zhì)量。
(3)壓縮方法的程序?qū)崿F(xiàn)
根據(jù)Bitmap壓縮圖片質(zhì)量,關(guān)鍵代碼如下:
public static Bitmap cQuality(Bitmap bitmap){
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bOut);
ByteArrayInputStream bInt = new ByteArrayInputStream(bOut.toByteArray());
Bitmap newBitmap = BitmapFactory.decodeStream(bInt);
if(newBitmap!=null){
return newBitmap;
}else{
return bitmap;
}
}
改變圖片大小的壓縮算法,關(guān)鍵代碼如下:
public static boolean getCacheImage(String filePath,String cachePath){
OutputStream out = null;
BitmapFactory.Options option = new BitmapFactory.Options();
option.inJustDecodeBounds = true; //設(shè)置為true,只讀尺寸信息,不加載像素信息到內(nèi)存
Bitmap bitmap = BitmapFactory.decodeFile(filePath, option); //此時bitmap為空
int be = 1; //be = 1代表不縮放
option.inSampleSize = be; //設(shè)置縮放比例
bitmap = BitmapFactory.decodeFile(filePath, option);
return bitmap.compress(CompressFormat.JPEG, 100, out);
}
根據(jù)項目需求,也可以把以上兩種壓縮方法相結(jié)合,用于進(jìn)一步提高壓縮效率。
4結(jié)束語
本文內(nèi)容討論了如何在Android應(yīng)用開發(fā)中實現(xiàn)圖片的高效處理,主要包括圖片的下載、上傳和壓縮功能的實現(xiàn),其中解決方案主要采用了當(dāng)前幾種比較成熟的開源框架。文章結(jié)合典型案例對圖片處理的方法、步驟、注意事項進(jìn)行了詳細(xì)的闡述,同時通過嚴(yán)謹(jǐn)?shù)姆治龊驼撟C說明此種解決方案在方法上可行、而且高效。
參考文獻(xiàn):
[1]陳岑. 基于Android移動應(yīng)用的網(wǎng)絡(luò)圖片傳輸與存儲的設(shè)計與實現(xiàn)[D].北京:北京郵電大學(xué),2014.
[2]項亮. 基于移動平臺的圖片制作系統(tǒng)設(shè)計與實現(xiàn)[D]. 上海:上海交通大學(xué),2012.
[3]陳德春. Android優(yōu)化技術(shù)詳解 [M]. 北京:清華大學(xué)出版社,2014.
[4]舒尚春. 基于Android 的電子相框的設(shè)計與實現(xiàn)[J]. 電子技術(shù)與軟件工程,2013,6(12):68-69.
[5]陳文,郭依正. 深入理解Android網(wǎng)絡(luò)編程:技術(shù)詳解與最佳實踐[M]. 北京:機(jī)械工業(yè)出版社,2013.
[6]鄒紹武,蘇貴斌. Android應(yīng)用開發(fā)中圖片壓縮技術(shù)的研究應(yīng)用[J]. 計算機(jī)技術(shù)與發(fā)展,2015,25(6):106-109.