淺析KJFrameForAndroid框架如何高效加載Bitmap
我們?cè)趯?xiě)Android程序的時(shí)候,肯定會(huì)用到很多圖片。那么對(duì)于圖片的壓縮處理自然是必不可少。為什么要壓縮?我想這個(gè)問(wèn)題不必在強(qiáng)調(diào)了,每個(gè)人在最初學(xué)習(xí)Android的時(shí)候肯定都會(huì)知道這么一個(gè)原因:我們編寫(xiě)的應(yīng)用程序都是有一個(gè)最大內(nèi)存限制,其中JAVA程序和C程序(NDK調(diào)用時(shí))共享這一塊內(nèi)存大小,程序占用了過(guò)高的內(nèi)存就容易出現(xiàn)OOM(OutOfMemory)異常。至于這個(gè)最大內(nèi)存是多少,我們可以通過(guò)調(diào)用Runtime.getRuntime().maxMemory()方法驗(yàn)證一下。
正因?yàn)槭艿絻?nèi)存大小限制這一關(guān)鍵原因(其實(shí)不止這個(gè)原因,我想一張1M的圖片和一張10k的圖片,載入的速度必然也是不同的吧)。 如果你的控件大小只有40*40像素的大小,只是為了顯示一張縮略圖,這時(shí)候把一張1024*768像素的圖片完全加載到內(nèi)存中顯然是不值得的,因此我們都會(huì)對(duì)圖片做壓縮處理。
BitmapFactory這個(gè)類提供了多個(gè)方法(decodeByteArray, decodeFile, decodeResource等)用于創(chuàng)建Bitmap對(duì)象,我們可以根據(jù)圖片的來(lái)源選擇合適的方法。然而這些方法會(huì)為已經(jīng)讀取的bitmap分配內(nèi)存,這時(shí)如果是一張非常大的圖片就會(huì)導(dǎo)致OOM出現(xiàn)。為此,每一種解析方法都提供了一個(gè)BitmapFactory.Options參數(shù),可以通過(guò)將這個(gè)參數(shù)的inJustDecodeBounds屬性設(shè)置為true就可以讓解析方法禁止為bitmap分配內(nèi)存,但是如此設(shè)置后BitmapFactory的返回值也不再是一個(gè)Bitmap對(duì)象,而是null。雖然Bitmap是null了,但是BitmapFactory.Options的outWidth、outHeight和outMimeType屬性都會(huì)被賦值。使用這個(gè)技巧讓我們可以在加載圖片之前就獲取到圖片的長(zhǎng)寬值和類型,從而根據(jù)情況對(duì)圖片進(jìn)行壓縮。
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(pathName, options); int h = options.outHeight; int w = options.outWidth; String type = options.outMimeType;
那么知道了圖片的寬高,要如何壓縮呢?BitmapFactory.Options有一個(gè)inSampleSize屬性,這個(gè)int值表示圖片的原寬高變?yōu)?/inSampleSize倍,如果原圖是1024*768,inSampleSize=2,那么壓縮后圖片就變成了512*384。 最后將BitmapFactory.Options設(shè)置合適的inSampleSize值,并且記得將inJustDecodeBounds設(shè)置回false,再調(diào)用一次BitmapFactory相應(yīng)的創(chuàng)建Bitmap的方法,并把Options傳入,就可以得到壓縮后的圖片了。
這里有一個(gè)節(jié)選自開(kāi)源Android應(yīng)用開(kāi)發(fā)框架KJFrameForAndroid中的一段代碼
/** * 圖片壓縮處理(使用Options的方法) * * @使用方法 首先你要將Options的inJustDecodeBounds屬性設(shè)置為true,BitmapFactory.decode一次圖片。 * 然后將Options連同期望的寬度和高度一起傳遞到到本方法中。 * 之后再使用本方法的返回值做參數(shù)調(diào)用BitmapFactory.decode創(chuàng)建圖片。 * * @explain BitmapFactory創(chuàng)建bitmap會(huì)嘗試為已經(jīng)構(gòu)建的bitmap分配內(nèi)存 * ,這時(shí)就會(huì)很容易導(dǎo)致OOM出現(xiàn)。為此每一種創(chuàng)建方法都提供了一個(gè)可選的Options參數(shù) * ,將這個(gè)參數(shù)的inJustDecodeBounds屬性設(shè)置為true就可以讓解析方法禁止為bitmap分配內(nèi)存 * ,返回值也不再是一個(gè)Bitmap對(duì)象, 而是null。雖然Bitmap是null了,但是Options的outWidth、 * outHeight和outMimeType屬性都會(huì)被賦值。 * @param reqWidth * 目標(biāo)寬度 * @param reqHeight * 目標(biāo)高度 */ public static BitmapFactory.Options calculateInSampleSize( final BitmapFactory.Options options, int reqWidth, int reqHeight) { // 源圖片的高度和寬度 final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { // 計(jì)算出實(shí)際寬高和目標(biāo)寬高的比率 final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); // 選擇寬和高中最小的比率作為inSampleSize的值,這樣可以保證最終圖片的寬和高 // 一定都會(huì)大于等于目標(biāo)的寬和高。 inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } // 設(shè)置壓縮比例 options.inSampleSize = inSampleSize; options.inJustDecodeBounds = false; return options; }
以上的方法適合使用在讀取一個(gè)未知來(lái)源的圖片時(shí)使用,因?yàn)槟悴恢肋@個(gè)未知來(lái)源圖片的大小,那么還有一種方法是用在已經(jīng)載入內(nèi)存的圖片,對(duì)已經(jīng)載入內(nèi)存的圖片做壓縮以后重新保存到本地,從而可以把一張?jiān)?M大小的圖片變成一張10K的圖片。
這種方法的核心思想是首先將圖片轉(zhuǎn)成一個(gè)輸出流,并記錄輸出流的byte數(shù)組大小,通過(guò)調(diào)用bitmap對(duì)象的compress方法,對(duì)圖片做一次壓縮以及格式化,并將byte數(shù)組大小與期望壓縮的目標(biāo)大小比對(duì),得出壓縮比率,并調(diào)用Bitmap的縮放方法,縮放計(jì)算出的壓縮比率,從而得到壓縮后的方法。
下面我們繼續(xù)來(lái)看KJFrameForAndroid框架中的另一段代碼:
/** * 圖片壓縮方法:(使用compress的方法) * * @explain 如果bitmap本身的大小小于maxSize,則不作處理 * @param bitmap * 要壓縮的圖片 * @param maxSize * 壓縮后的大小,單位kb */ public static void imageZoom(Bitmap bitmap, double maxSize) { // 將bitmap放至數(shù)組中,意在獲得bitmap的大?。ㄅc實(shí)際讀取的原文件要大) ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 格式、質(zhì)量、輸出流 bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); byte[] b = baos.toByteArray(); // 將字節(jié)換成KB double mid = b.length / 1024; // 獲取bitmap大小 是允許最大大小的多少倍 double i = mid / maxSize; // 判斷bitmap占用空間是否大于允許最大空間 如果大于則壓縮 小于則不壓縮 if (i > 1) { // 縮放圖片 此處用到平方根 將寬帶和高度壓縮掉對(duì)應(yīng)的平方根倍 // (保持寬高不變,縮放后也達(dá)到了最大占用空間的大?。? bitmap = scale(bitmap, bitmap.getWidth() / Math.sqrt(i), bitmap.getHeight() / Math.sqrt(i)); } } /*** * 圖片的縮放方法 * * @param src * :源圖片資源 * @param newWidth * :縮放后寬度 * @param newHeight * :縮放后高度 */ public static Bitmap scale(Bitmap src, double newWidth, double newHeight) { // 記錄src的寬高 float width = src.getWidth(); float height = src.getHeight(); // 創(chuàng)建一個(gè)matrix容器 Matrix matrix = new Matrix(); // 計(jì)算縮放比例 float scaleWidth = ((float) newWidth) / width; float scaleHeight = ((float) newHeight) / height; // 開(kāi)始縮放 matrix.postScale(scaleWidth, scaleHeight); // 創(chuàng)建縮放后的圖片 return Bitmap.createBitmap(src, 0, 0, (int) width, (int) height, matrix, true); }
另外附上KJFrameForAndroid框架項(xiàng)目地址: https://github.com/kymjs/KJFrameForAndroid
或備用地址 http://git.oschina.net/kymjs/KJFrameForAndroid
有這方面需要的朋友可以下載下來(lái)自己研究下
- 六款值得推薦的android(安卓)開(kāi)源框架簡(jiǎn)介
- Android Retrofit 2.0框架上傳圖片解決方案
- Android中XUtils3框架使用方法詳解(一)
- Android中使用開(kāi)源框架Citypickerview實(shí)現(xiàn)省市區(qū)三級(jí)聯(lián)動(dòng)選擇
- Android使用Fragment打造萬(wàn)能頁(yè)面切換框架
- Android開(kāi)發(fā)框架之自定義ZXing二維碼掃描界面并解決取景框拉伸問(wèn)題
- Android Volley框架全面解析
- 簡(jiǎn)略分析Android的Retrofit應(yīng)用開(kāi)發(fā)框架源碼
- android上的一個(gè)網(wǎng)絡(luò)接口和圖片緩存框架enif簡(jiǎn)析
- SimpleCommand框架介紹以及簡(jiǎn)單使用(一)
相關(guān)文章
android使用ViewPager實(shí)現(xiàn)圖片自動(dòng)切換
這篇文章主要為大家詳細(xì)介紹了android使用ViewPager實(shí)現(xiàn)圖片自動(dòng)切換,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-02-02Android實(shí)現(xiàn)屏幕旋轉(zhuǎn)四個(gè)方向準(zhǔn)確監(jiān)聽(tīng)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)屏幕旋轉(zhuǎn)四個(gè)方向準(zhǔn)確監(jiān)聽(tīng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07Kotlin開(kāi)發(fā)筆記之委托屬性與區(qū)間(譯)
最近在學(xué)習(xí)kotlin,發(fā)現(xiàn)了一些比較重要的知識(shí)點(diǎn),所以下面這篇文章主要給大家介紹了關(guān)于Kotlin開(kāi)發(fā)筆記之委托屬性與區(qū)間的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-12-12Android實(shí)現(xiàn)字母導(dǎo)航控件的示例代碼
這篇文章主要介紹了通過(guò)自定義View實(shí)現(xiàn)字母導(dǎo)航控件的示例代碼,文中的實(shí)現(xiàn)過(guò)程講解詳細(xì),對(duì)我們學(xué)習(xí)或工作有一定幫助,感興趣的可以學(xué)習(xí)一下2022-01-01Android快速開(kāi)發(fā)系列 10個(gè)常用工具類實(shí)例代碼詳解
今天特此整理出10個(gè)基本每個(gè)項(xiàng)目中都會(huì)使用的工具類,用于快速開(kāi)發(fā),對(duì)android開(kāi)發(fā)常用工具類感興趣的朋友跟隨小編一起看看吧2018-09-09Android 錢包支付之輸入支付密碼的實(shí)現(xiàn)步驟
這篇文章主要介紹了Android 錢包支付之輸入支付密碼,需要的朋友可以參考下2018-04-04Android懸浮窗屏蔽懸浮窗外部所有的點(diǎn)擊事件的實(shí)例代碼
這篇文章主要介紹了Android懸浮窗屏蔽懸浮窗外部所有的點(diǎn)擊事件實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-03-03