【Android 内存优化】Bitmap 图像尺寸缩小 ( 设置 Options 参数 | inJustDecodeBounds | inSampleSize | 工具类实现 )
文章目錄
- 一、解碼圖片參數(shù) inJustDecodeBounds
- 二、計(jì)算圖片的縮小比例
- 三、設(shè)置圖片縮小配置 inSampleSize
- 四、設(shè)置圖片像素格式 inPreferredConfig
- 五、設(shè)置圖片復(fù)用機(jī)制
- 六、Bitmap 圖像尺寸縮小代碼示例
- 1、圖片縮小工具類
- 2、Activity 調(diào)用工具類代碼
- 3、執(zhí)行結(jié)果
一、解碼圖片參數(shù) inJustDecodeBounds
1 . 解碼圖片參數(shù) :
① 設(shè)置獲取參數(shù)解碼選項(xiàng) : 設(shè)置解碼時(shí)的 BitmapFactory.Options 對(duì)象的 inJustDecodeBounds 為 true ,
② 解碼圖像 : 解析器返回的 Bitmap 對(duì)象為 null ;
③ 解碼選項(xiàng) : BitmapFactory.Options 中的 outXxx 字段會(huì)被設(shè)置對(duì)應(yīng)的圖片屬性值 ;
④ 解碼選項(xiàng)參數(shù)示例 : 如 : outWidth 輸出圖像的 寬度 , outHeight 輸出高度 , outMimeType 輸出類型 , outConfig 像素格式 , outColorSpace 輸出顏色空間 ;
2 . 代碼示例 :
// Bitmap 圖片加載選項(xiàng) BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(resources, iamgeResId, options);二、計(jì)算圖片的縮小比例
計(jì)算圖片的縮小比例 :
① 目標(biāo)圖片寬高要求 : 寬度和高度只要存在一個(gè)大于限定的最大值時(shí) , 就進(jìn)行縮小操作 ; 要求指定的圖片必須能放到 maxBitmapWidth 寬度 , maxBitmapHeight 高度的矩形框中 ; 最終要求就是 寬度必須小于 maxBitmapWidth, 同時(shí)高度也要小于 maxBitmapHeight ;
② 縮小倍數(shù)要求 : 縮小倍數(shù)只能是 2 的冪次方值 , 1 , 2 , 4 , 8 , 16 , 32 , 64 ;
/*計(jì)算縮小的比例寬度和高度只要存在一個(gè)大于限定的最大值時(shí) , 就進(jìn)行縮小操作要求指定的圖片必須能放到 maxBitmapWidth 寬度 , maxBitmapHeight 高度的矩形框中最終要求就是 寬度必須小于 maxBitmapWidth, 同時(shí)高度也要小于 maxBitmapHeight*/if(imageWidth > maxBitmapWidth || imageHeight > maxBitmapHeight){// 如果需要啟動(dòng)縮小功能 , 那么進(jìn)入如下循環(huán) , 試探最小的縮放比例是多少while ( imageWidth / inSampleSize > maxBitmapWidth ||imageHeight / inSampleSize > maxBitmapHeight ){// 注意該值必須是 2 的冪次方值 , 1 , 2 , 4 , 8 , 16 , 32 , 64inSampleSize = inSampleSize * 2;}// 執(zhí)行到此處 , 說明已經(jīng)找到了最小的縮放比例 , 打印下最小比例Log.w(TAG, "getResizedBitmap inSampleSize=" + inSampleSize);}三、設(shè)置圖片縮小配置 inSampleSize
1 . 圖片縮小配置 inSampleSize :
① inSampleSize 設(shè)置大于 1 : 如果值大于 1 , 那么就會(huì)縮小圖片 ;
② 解碼器操作 : 此時(shí)解碼器對(duì)原始的圖片數(shù)據(jù)進(jìn)行子采樣 , 返回較小的 Bitmap 對(duì)象 ;
③ 樣本個(gè)數(shù) : 樣本的大小是在兩個(gè)維度計(jì)算的像素個(gè)數(shù) , 每個(gè)像素對(duì)應(yīng)一個(gè)解碼后的圖片中的單獨(dú)的像素點(diǎn) ;
④ 樣本個(gè)數(shù)計(jì)算示例 : 如果 inSampleSize 值為 2 , 那么寬度的像素個(gè)數(shù)會(huì)縮小 2 倍 , 高度也會(huì)縮小兩倍 ; 整體像素個(gè)數(shù)縮小 4 倍 , 內(nèi)存也縮小了 4 倍 ;
2 . inSampleSize 取值要求 :
① 小于 1 取值 : 如果取值小于 1 , 那么就會(huì)被當(dāng)做 1 , 1 相當(dāng)于 2 的 0 次方 ;
② 取值要求 : 該值必須是 2 的冪次方值 , 2 的次方值 , 如 1 , 2 , 4 , 8 , 16 , 32
③ 不合法值 : 如果出現(xiàn)了不合法的值 , 就會(huì)就近四舍五入到最近的 2 的冪次方值
3 . 代碼示例 :
BitmapFactory.Options options = new BitmapFactory.Options(); // ... options.inSampleSize = inSampleSize;四、設(shè)置圖片像素格式 inPreferredConfig
1 . 解碼像素格式 :
① 指定配置解碼 : 如果配置為非空 , 解碼器會(huì)將 Bitmap 的像素解碼成該指定的非空像素格式 ;
② 自動(dòng)匹配配置解碼 : 如果該配置為空 , 或者像素配置無法滿足 , 解碼器會(huì)嘗試根據(jù)系統(tǒng)的屏幕深度 , 源圖像的特點(diǎn) , 選擇合適的像素格式 ; 如果源圖像有透明度通道 , 那么自動(dòng)匹配的默認(rèn)配置也有對(duì)應(yīng)通道 ;
③ 默認(rèn)配置 : 默認(rèn)使用 ARGB_8888 進(jìn)行解碼
2 . 代碼示例 :
options.inPreferredConfig = Bitmap.Config.RGB_565;五、設(shè)置圖片復(fù)用機(jī)制
1 . 圖片復(fù)用機(jī)制 :
① 圖片復(fù)用 : 如果設(shè)置了一個(gè) Bitmap 對(duì)象給 inBitmap 參數(shù) , 解碼方法會(huì)獲取該 Bitmap 對(duì)象 , 當(dāng)加載圖片內(nèi)容時(shí) , 會(huì)嘗試復(fù)用該 Bitmap 對(duì)象的內(nèi)存
② 無法復(fù)用拋出異常 : 如果解碼方法無法復(fù)用該 Bitmap 對(duì)象 , 解碼方法可能會(huì)拋出 IllegalArgumentException 異常 ;
③ 圖片可變性 : 當(dāng)前的實(shí)現(xiàn)是很有必要的 , 被復(fù)用的圖片必須是可變的 , 解碼后的 Bitmap 對(duì)象也是可變的 , 即使當(dāng)解碼一個(gè)資源圖片時(shí) , 經(jīng)常會(huì)得到一個(gè)不可變的 Bitmap 對(duì)象 ;
2 . 解碼結(jié)果判定 :
① 解碼可能失敗 : 該解碼方法返回的 Bitmap 對(duì)象是可以使用的 , 鑒于上述約束情況 和 可能發(fā)生的失敗故障 , 不能假定該圖片解碼操作是成功的 ;
② 檢查復(fù)用是否成功 : 解碼檢查解碼返回的 Bitmap 對(duì)象是否與設(shè)置給 Options 對(duì)象的 inBitmap 相匹配 , 來判斷該 inBitmap 是否被復(fù)用 ;
③ 后續(xù)操作 : 不管有沒有復(fù)用成功 , 你應(yīng)該使用解碼函數(shù)返回的 Bitmap 對(duì)象 , 保證程序的正常運(yùn)行 ;
3 . 與 BitmapFactory 配合使用 :
① Android 4.4 以后的復(fù)用機(jī)制 : 在 KITKAT 以后的代碼中 , 只要被解碼生成的 Bitmap 對(duì)象的字節(jié)大小 ( 縮放后的 ) , 小于等于 inBitmap 的字節(jié)大小 , 就可以復(fù)用成功 ;
② Android 4.4 之前的復(fù)用機(jī)制 : 在 KITKAT ( Android 4.4 系統(tǒng) , android-19 平臺(tái) ) 之前的代碼中 , 被解碼的圖像必須是
- JPEG 或 PNG 格式 ,
- 并且 圖像大小必須是相等的 ,
- inssampleSize 設(shè)置為 1 ,
才能復(fù)用成功 , 另外被復(fù)用的圖像的 像素格式 Config ( 如 RGB_565 ) 會(huì)覆蓋設(shè)置的 inPreferredConfig 參數(shù)
4 . 代碼示例 :
options.inBitmap = inBitmap;六、Bitmap 圖像尺寸縮小代碼示例
1、圖片縮小工具類
圖片縮小工具類 :
package kim.hsl.bm.utils;import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.util.Log;/*** Bitmap 尺寸縮小*/ public class BitmapSizeReduce {private static final String TAG = "BitmapSizeReduce";/*** 獲取指定尺寸于鏊求的 Bitmap 對(duì)象* 該方法有缺陷 , 計(jì)算值的時(shí)候沒有考慮像素密度* 如果從不同像素密度的資源文件中加載* 可能計(jì)算出的值與指定的 maxBitmapWidth maxBitmapHeight 略有出入** @param context 上下文對(duì)象* @param iamgeResId 要解析的圖片資源 id* @param maxBitmapWidth Bitmap 的最大寬度* @param maxBitmapHeight Bitmap 的最大高度* @param hasAlphaChannel 是否包含 ALPHA 通道, 即透明度信息* @param inBitmap 復(fù)用的 Bitmap, 將新的 Bitmap 對(duì)象解析到該 Bitmap 內(nèi)存中* @return 返回新的 Bitmap 對(duì)象*/public static Bitmap getResizedBitmap(Context context,int iamgeResId, int maxBitmapWidth, int maxBitmapHeight,boolean hasAlphaChannel, Bitmap inBitmap){// 0. 聲明方法中使用的局部變量// 用于解析資源Resources resources = context.getResources();// Bitmap 圖片加載選項(xiàng)BitmapFactory.Options options = new BitmapFactory.Options();// 圖片寬度int imageWidth;// 圖片高度int imageHeight;/*根據(jù) 圖片寬度 imageWidth , 圖片高度 imageHeight ,最大寬度 maxBitmapWidth , 最大高度 maxBitmapHeight ,計(jì)算出的圖片縮放系數(shù) , 該值最終要設(shè)置到 BitmapFactory.Options 對(duì)象中*/int inSampleSize = 1;// 1. 解析圖片參數(shù) : 該階段不解析所有的數(shù)據(jù) , 否則會(huì)將實(shí)際的圖片數(shù)據(jù)解析到內(nèi)存中 , 這里只解析圖片的寬高信息/*設(shè)置 inJustDecodeBounds 為 true , 解析器會(huì)返回 null但是 outXxx 字段會(huì)被設(shè)置對(duì)應(yīng)的圖片屬性值 ,如 : outWidth 輸出圖像的 寬度 , outHeight 輸出高度 , outMimeType 輸出類型 ,outConfig 像素格式 , outColorSpace 輸出顏色空間*/options.inJustDecodeBounds = true;/*由于設(shè)置了 inJustDecodeBounds = true , 該方法返回值為空 ;但是傳入的 BitmapFactory.Options 對(duì)象中的 outXxx 字段都會(huì)被賦值 ;如 outWidth , outHeight , outConfig , outColorSpace 等 ;可以獲取該圖片的寬高 , 像素格式 , 顏色空間等信息*/BitmapFactory.decodeResource(resources, iamgeResId, options);// 獲取 iamgeResId 圖片資源對(duì)應(yīng)的圖片寬度imageWidth = options.outWidth;// 獲取 iamgeResId 圖片資源對(duì)應(yīng)的圖片高度imageHeight = options.outHeight;// 2. 計(jì)算圖片縮小比例/*計(jì)算縮小的比例寬度和高度只要存在一個(gè)大于限定的最大值時(shí) , 就進(jìn)行縮小操作要求指定的圖片必須能放到 maxBitmapWidth 寬度 , maxBitmapHeight 高度的矩形框中最終要求就是 寬度必須小于 maxBitmapWidth, 同時(shí)高度也要小于 maxBitmapHeight*/if(imageWidth > maxBitmapWidth || imageHeight > maxBitmapHeight){// 如果需要啟動(dòng)縮小功能 , 那么進(jìn)入如下循環(huán) , 試探最小的縮放比例是多少while ( imageWidth / inSampleSize > maxBitmapWidth ||imageHeight / inSampleSize > maxBitmapHeight ){// 注意該值必須是 2 的冪次方值 , 1 , 2 , 4 , 8 , 16 , 32 , 64inSampleSize = inSampleSize * 2;}// 執(zhí)行到此處 , 說明已經(jīng)找到了最小的縮放比例 , 打印下最小比例Log.w(TAG, "getResizedBitmap inSampleSize=" + inSampleSize);}// 3. 設(shè)置圖像解碼參數(shù)/*inSampleSize 設(shè)置大于 1 : 如果值大于 1 , 那么就會(huì)縮小圖片 ;解碼器操作 : 此時(shí)解碼器對(duì)原始的圖片數(shù)據(jù)進(jìn)行子采樣 , 返回較小的 Bitmap 對(duì)象 ;樣本個(gè)數(shù) : 樣本的大小是在兩個(gè)維度計(jì)算的像素個(gè)數(shù) , 每個(gè)像素對(duì)應(yīng)一個(gè)解碼后的圖片中的單獨(dú)的像素點(diǎn) ;樣本個(gè)數(shù)計(jì)算示例 :如果 inSampleSize 值為 2 , 那么寬度的像素個(gè)數(shù)會(huì)縮小 2 倍 , 高度也會(huì)縮小兩倍 ;整體像素個(gè)數(shù)縮小 4 倍 , 內(nèi)存也縮小了 4 倍 ;小于 1 取值 : 如果取值小于 1 , 那么就會(huì)被當(dāng)做 1 , 1 相當(dāng)于 2 的 0 次方 ;取值要求 : 該值必須是 2 的冪次方值 , 2 的次方值 , 如 1 , 2 , 4 , 8 , 16 , 32如果出現(xiàn)了不合法的值 , 就會(huì)就近四舍五入到最近的 2 的冪次方值*/options.inSampleSize = inSampleSize;// 用戶設(shè)置的是否保留透明度選項(xiàng) , 如果不保留透明度選項(xiàng) , 設(shè)置像素格式為 RGB_565// 每個(gè)像素占 2 字節(jié)內(nèi)存if (!hasAlphaChannel){/*指定配置解碼 : 如果配置為非空 , 解碼器會(huì)將 Bitmap 的像素解碼成該指定的非空像素格式 ;自動(dòng)匹配配置解碼 : 如果該配置為空 , 或者像素配置無法滿足 , 解碼器會(huì)嘗試根據(jù)系統(tǒng)的屏幕深度 ,源圖像的特點(diǎn) , 選擇合適的像素格式 ;如果源圖像有透明度通道 , 那么自動(dòng)匹配的默認(rèn)配置也有對(duì)應(yīng)通道 ;默認(rèn)配置 : 默認(rèn)使用 ARGB_8888 進(jìn)行解碼*/options.inPreferredConfig = Bitmap.Config.RGB_565;}/*注意解碼真實(shí)圖像的時(shí)候 , 要將 inJustDecodeBounds 設(shè)置為 false否則將不會(huì)解碼 Bitmap 數(shù)據(jù) , 只會(huì)將outWidth , outHeight , outConfig , outColorSpace 等 outXxx 圖片參數(shù)解碼出來*/options.inJustDecodeBounds = false;/*設(shè)置圖片可以被復(fù)用*/options.inMutable = true;/*如果設(shè)置了一個(gè) Bitmap 對(duì)象給 inBitmap 參數(shù)解碼方法會(huì)獲取該 Bitmap 對(duì)象 , 當(dāng)加載圖片內(nèi)容時(shí) , 會(huì)嘗試復(fù)用該 Bitmap 對(duì)象的內(nèi)存如果解碼方法無法復(fù)用該 Bitmap 對(duì)象 , 解碼方法可能會(huì)拋出 IllegalArgumentException 異常 ;當(dāng)前的實(shí)現(xiàn)是很有必要的 , 被復(fù)用的圖片必須是可變的 , 解碼后的 Bitmap 對(duì)象也是可變的 ,即使當(dāng)解碼一個(gè)資源圖片時(shí) , 經(jīng)常會(huì)得到一個(gè)不可變的 Bitmap 對(duì)象 ;確保是否解碼成功 :該解碼方法返回的 Bitmap 對(duì)象是可以使用的 ,鑒于上述約束情況 和 可能發(fā)生的失敗故障 , 不能假定該圖片解碼操作是成功的 ;檢查解碼返回的 Bitmap 對(duì)象是否與設(shè)置給 Options 對(duì)象的 inBitmap 相匹配 ,來判斷該 inBitmap 是否被復(fù)用 ;不管有沒有復(fù)用成功 , 你應(yīng)該使用解碼函數(shù)返回的 Bitmap 對(duì)象 , 保證程序的正常運(yùn)行 ;與 BitmapFactory 配合使用 :在 KITKAT 以后的代碼中 , 只要被解碼生成的 Bitmap 對(duì)象的字節(jié)大小 ( 縮放后的 )小于等于 inBitmap 的字節(jié)大小 , 就可以復(fù)用成功 ;在 KITKAT 之前的代碼中 , 被解碼的圖像必須是JPEG 或 PNG 格式 ,并且 圖像大小必須是相等的 ,inssampleSize 設(shè)置為 1 ,才能復(fù)用成功 ;另外被復(fù)用的圖像的 像素格式 Config ( 如 RGB_565 ) 會(huì)覆蓋設(shè)置的 inPreferredConfig 參數(shù)*/options.inBitmap = inBitmap;// 4. 解碼圖片 , 并返回被解碼的圖片return BitmapFactory.decodeResource(resources, iamgeResId, options);}}2、Activity 調(diào)用工具類代碼
Activity 代碼 :
package kim.hsl.bm;import androidx.appcompat.app.AppCompatActivity;import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.util.Log; import android.widget.TextView;import kim.hsl.bm.utils.BitmapSizeReduce;public class MainActivity extends AppCompatActivity {static {System.loadLibrary("native-lib");}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);TextView tv = findViewById(R.id.sample_text);tv.setText(stringFromJNI());// 縮小圖像尺寸sizeReduce();}/*** 圖像尺寸縮小*/private void sizeReduce(){// 從資源文件中加載內(nèi)存Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.blog);// 打印 Bitmap 對(duì)象的寬高, 字節(jié)大小Log.i("Bitmap", "blog : " + bitmap.getWidth() + " , " +bitmap.getHeight() + " , " +bitmap.getByteCount());// 從資源文件中加載內(nèi)存Bitmap reduceSizeBitmap = BitmapSizeReduce.getResizedBitmap(this, R.drawable.blog,100, 100 , false , null);// 打印 Bitmap 對(duì)象的寬高, 字節(jié)大小Log.i("Bitmap", "reduceSizeBitmap : " + reduceSizeBitmap.getWidth() + " , " +reduceSizeBitmap.getHeight() + " , " +reduceSizeBitmap.getByteCount());}public native String stringFromJNI(); }3、執(zhí)行結(jié)果
執(zhí)行結(jié)果 :
2020-06-30 22:04:22.959 3766-3766/? I/Bitmap: blog : 5224 , 2678 , 55959488 2020-06-30 22:04:22.960 3766-3766/? W/BitmapSizeReduce: getResizedBitmap inSampleSize=32 2020-06-30 22:04:22.980 3766-3766/? I/Bitmap: reduceSizeBitmap : 163 , 81 , 26406分析結(jié)果 :
① 源圖像分析 : 從資源中加載 , 普通情況下寬度 5224 像素 , 高度 2678 像素 , ARGB_8888 像素格式 , 每個(gè)像素 444 字節(jié) , 計(jì)算公式為
5224×2678×4=55,959,4885224 \times 2678 \times 4 = 55,959,4885224×2678×4=55,959,488
② 縮小后的圖像分析 : 從資源中加載 , 普通情況下寬度 163 像素 , 高度 81 像素 , RGB_565 像素格式 , 每個(gè)像素 222 字節(jié) , 計(jì)算公式為
163×81×2=26,406?163 \times 81 \times 2 = 26,406?163×81×2=26,406?
總結(jié)
以上是生活随笔為你收集整理的【Android 内存优化】Bitmap 图像尺寸缩小 ( 设置 Options 参数 | inJustDecodeBounds | inSampleSize | 工具类实现 )的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Android 内存优化】Bitmap
- 下一篇: 【Android 内存优化】Bitmap