【Android 内存优化】Bitmap 图像尺寸缩小 ( 设置 Options 参数 | inJustDecodeBounds | inSampleSize | 工具类实现 )
文章目錄
- 一、解碼圖片參數(shù) inJustDecodeBounds
- 二、計算圖片的縮小比例
- 三、設(shè)置圖片縮小配置 inSampleSize
- 四、設(shè)置圖片像素格式 inPreferredConfig
- 五、設(shè)置圖片復(fù)用機制
- 六、Bitmap 圖像尺寸縮小代碼示例
- 1、圖片縮小工具類
- 2、Activity 調(diào)用工具類代碼
- 3、執(zhí)行結(jié)果
一、解碼圖片參數(shù) inJustDecodeBounds
1 . 解碼圖片參數(shù) :
① 設(shè)置獲取參數(shù)解碼選項 : 設(shè)置解碼時的 BitmapFactory.Options 對象的 inJustDecodeBounds 為 true ,
② 解碼圖像 : 解析器返回的 Bitmap 對象為 null ;
③ 解碼選項 : BitmapFactory.Options 中的 outXxx 字段會被設(shè)置對應(yīng)的圖片屬性值 ;
④ 解碼選項參數(shù)示例 : 如 : outWidth 輸出圖像的 寬度 , outHeight 輸出高度 , outMimeType 輸出類型 , outConfig 像素格式 , outColorSpace 輸出顏色空間 ;
2 . 代碼示例 :
// Bitmap 圖片加載選項 BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(resources, iamgeResId, options);二、計算圖片的縮小比例
計算圖片的縮小比例 :
① 目標圖片寬高要求 : 寬度和高度只要存在一個大于限定的最大值時 , 就進行縮小操作 ; 要求指定的圖片必須能放到 maxBitmapWidth 寬度 , maxBitmapHeight 高度的矩形框中 ; 最終要求就是 寬度必須小于 maxBitmapWidth, 同時高度也要小于 maxBitmapHeight ;
② 縮小倍數(shù)要求 : 縮小倍數(shù)只能是 2 的冪次方值 , 1 , 2 , 4 , 8 , 16 , 32 , 64 ;
/*計算縮小的比例寬度和高度只要存在一個大于限定的最大值時 , 就進行縮小操作要求指定的圖片必須能放到 maxBitmapWidth 寬度 , maxBitmapHeight 高度的矩形框中最終要求就是 寬度必須小于 maxBitmapWidth, 同時高度也要小于 maxBitmapHeight*/if(imageWidth > maxBitmapWidth || imageHeight > maxBitmapHeight){// 如果需要啟動縮小功能 , 那么進入如下循環(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 , 那么就會縮小圖片 ;
② 解碼器操作 : 此時解碼器對原始的圖片數(shù)據(jù)進行子采樣 , 返回較小的 Bitmap 對象 ;
③ 樣本個數(shù) : 樣本的大小是在兩個維度計算的像素個數(shù) , 每個像素對應(yīng)一個解碼后的圖片中的單獨的像素點 ;
④ 樣本個數(shù)計算示例 : 如果 inSampleSize 值為 2 , 那么寬度的像素個數(shù)會縮小 2 倍 , 高度也會縮小兩倍 ; 整體像素個數(shù)縮小 4 倍 , 內(nèi)存也縮小了 4 倍 ;
2 . inSampleSize 取值要求 :
① 小于 1 取值 : 如果取值小于 1 , 那么就會被當做 1 , 1 相當于 2 的 0 次方 ;
② 取值要求 : 該值必須是 2 的冪次方值 , 2 的次方值 , 如 1 , 2 , 4 , 8 , 16 , 32
③ 不合法值 : 如果出現(xiàn)了不合法的值 , 就會就近四舍五入到最近的 2 的冪次方值
3 . 代碼示例 :
BitmapFactory.Options options = new BitmapFactory.Options(); // ... options.inSampleSize = inSampleSize;四、設(shè)置圖片像素格式 inPreferredConfig
1 . 解碼像素格式 :
① 指定配置解碼 : 如果配置為非空 , 解碼器會將 Bitmap 的像素解碼成該指定的非空像素格式 ;
② 自動匹配配置解碼 : 如果該配置為空 , 或者像素配置無法滿足 , 解碼器會嘗試根據(jù)系統(tǒng)的屏幕深度 , 源圖像的特點 , 選擇合適的像素格式 ; 如果源圖像有透明度通道 , 那么自動匹配的默認配置也有對應(yīng)通道 ;
③ 默認配置 : 默認使用 ARGB_8888 進行解碼
2 . 代碼示例 :
options.inPreferredConfig = Bitmap.Config.RGB_565;五、設(shè)置圖片復(fù)用機制
1 . 圖片復(fù)用機制 :
① 圖片復(fù)用 : 如果設(shè)置了一個 Bitmap 對象給 inBitmap 參數(shù) , 解碼方法會獲取該 Bitmap 對象 , 當加載圖片內(nèi)容時 , 會嘗試復(fù)用該 Bitmap 對象的內(nèi)存
② 無法復(fù)用拋出異常 : 如果解碼方法無法復(fù)用該 Bitmap 對象 , 解碼方法可能會拋出 IllegalArgumentException 異常 ;
③ 圖片可變性 : 當前的實現(xiàn)是很有必要的 , 被復(fù)用的圖片必須是可變的 , 解碼后的 Bitmap 對象也是可變的 , 即使當解碼一個資源圖片時 , 經(jīng)常會得到一個不可變的 Bitmap 對象 ;
2 . 解碼結(jié)果判定 :
① 解碼可能失敗 : 該解碼方法返回的 Bitmap 對象是可以使用的 , 鑒于上述約束情況 和 可能發(fā)生的失敗故障 , 不能假定該圖片解碼操作是成功的 ;
② 檢查復(fù)用是否成功 : 解碼檢查解碼返回的 Bitmap 對象是否與設(shè)置給 Options 對象的 inBitmap 相匹配 , 來判斷該 inBitmap 是否被復(fù)用 ;
③ 后續(xù)操作 : 不管有沒有復(fù)用成功 , 你應(yīng)該使用解碼函數(shù)返回的 Bitmap 對象 , 保證程序的正常運行 ;
3 . 與 BitmapFactory 配合使用 :
① Android 4.4 以后的復(fù)用機制 : 在 KITKAT 以后的代碼中 , 只要被解碼生成的 Bitmap 對象的字節(jié)大小 ( 縮放后的 ) , 小于等于 inBitmap 的字節(jié)大小 , 就可以復(fù)用成功 ;
② Android 4.4 之前的復(fù)用機制 : 在 KITKAT ( Android 4.4 系統(tǒng) , android-19 平臺 ) 之前的代碼中 , 被解碼的圖像必須是
- JPEG 或 PNG 格式 ,
- 并且 圖像大小必須是相等的 ,
- inssampleSize 設(shè)置為 1 ,
才能復(fù)用成功 , 另外被復(fù)用的圖像的 像素格式 Config ( 如 RGB_565 ) 會覆蓋設(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 對象* 該方法有缺陷 , 計算值的時候沒有考慮像素密度* 如果從不同像素密度的資源文件中加載* 可能計算出的值與指定的 maxBitmapWidth maxBitmapHeight 略有出入** @param context 上下文對象* @param iamgeResId 要解析的圖片資源 id* @param maxBitmapWidth Bitmap 的最大寬度* @param maxBitmapHeight Bitmap 的最大高度* @param hasAlphaChannel 是否包含 ALPHA 通道, 即透明度信息* @param inBitmap 復(fù)用的 Bitmap, 將新的 Bitmap 對象解析到該 Bitmap 內(nèi)存中* @return 返回新的 Bitmap 對象*/public static Bitmap getResizedBitmap(Context context,int iamgeResId, int maxBitmapWidth, int maxBitmapHeight,boolean hasAlphaChannel, Bitmap inBitmap){// 0. 聲明方法中使用的局部變量// 用于解析資源Resources resources = context.getResources();// Bitmap 圖片加載選項BitmapFactory.Options options = new BitmapFactory.Options();// 圖片寬度int imageWidth;// 圖片高度int imageHeight;/*根據(jù) 圖片寬度 imageWidth , 圖片高度 imageHeight ,最大寬度 maxBitmapWidth , 最大高度 maxBitmapHeight ,計算出的圖片縮放系數(shù) , 該值最終要設(shè)置到 BitmapFactory.Options 對象中*/int inSampleSize = 1;// 1. 解析圖片參數(shù) : 該階段不解析所有的數(shù)據(jù) , 否則會將實際的圖片數(shù)據(jù)解析到內(nèi)存中 , 這里只解析圖片的寬高信息/*設(shè)置 inJustDecodeBounds 為 true , 解析器會返回 null但是 outXxx 字段會被設(shè)置對應(yīng)的圖片屬性值 ,如 : outWidth 輸出圖像的 寬度 , outHeight 輸出高度 , outMimeType 輸出類型 ,outConfig 像素格式 , outColorSpace 輸出顏色空間*/options.inJustDecodeBounds = true;/*由于設(shè)置了 inJustDecodeBounds = true , 該方法返回值為空 ;但是傳入的 BitmapFactory.Options 對象中的 outXxx 字段都會被賦值 ;如 outWidth , outHeight , outConfig , outColorSpace 等 ;可以獲取該圖片的寬高 , 像素格式 , 顏色空間等信息*/BitmapFactory.decodeResource(resources, iamgeResId, options);// 獲取 iamgeResId 圖片資源對應(yīng)的圖片寬度imageWidth = options.outWidth;// 獲取 iamgeResId 圖片資源對應(yīng)的圖片高度imageHeight = options.outHeight;// 2. 計算圖片縮小比例/*計算縮小的比例寬度和高度只要存在一個大于限定的最大值時 , 就進行縮小操作要求指定的圖片必須能放到 maxBitmapWidth 寬度 , maxBitmapHeight 高度的矩形框中最終要求就是 寬度必須小于 maxBitmapWidth, 同時高度也要小于 maxBitmapHeight*/if(imageWidth > maxBitmapWidth || imageHeight > maxBitmapHeight){// 如果需要啟動縮小功能 , 那么進入如下循環(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 , 那么就會縮小圖片 ;解碼器操作 : 此時解碼器對原始的圖片數(shù)據(jù)進行子采樣 , 返回較小的 Bitmap 對象 ;樣本個數(shù) : 樣本的大小是在兩個維度計算的像素個數(shù) , 每個像素對應(yīng)一個解碼后的圖片中的單獨的像素點 ;樣本個數(shù)計算示例 :如果 inSampleSize 值為 2 , 那么寬度的像素個數(shù)會縮小 2 倍 , 高度也會縮小兩倍 ;整體像素個數(shù)縮小 4 倍 , 內(nèi)存也縮小了 4 倍 ;小于 1 取值 : 如果取值小于 1 , 那么就會被當做 1 , 1 相當于 2 的 0 次方 ;取值要求 : 該值必須是 2 的冪次方值 , 2 的次方值 , 如 1 , 2 , 4 , 8 , 16 , 32如果出現(xiàn)了不合法的值 , 就會就近四舍五入到最近的 2 的冪次方值*/options.inSampleSize = inSampleSize;// 用戶設(shè)置的是否保留透明度選項 , 如果不保留透明度選項 , 設(shè)置像素格式為 RGB_565// 每個像素占 2 字節(jié)內(nèi)存if (!hasAlphaChannel){/*指定配置解碼 : 如果配置為非空 , 解碼器會將 Bitmap 的像素解碼成該指定的非空像素格式 ;自動匹配配置解碼 : 如果該配置為空 , 或者像素配置無法滿足 , 解碼器會嘗試根據(jù)系統(tǒng)的屏幕深度 ,源圖像的特點 , 選擇合適的像素格式 ;如果源圖像有透明度通道 , 那么自動匹配的默認配置也有對應(yīng)通道 ;默認配置 : 默認使用 ARGB_8888 進行解碼*/options.inPreferredConfig = Bitmap.Config.RGB_565;}/*注意解碼真實圖像的時候 , 要將 inJustDecodeBounds 設(shè)置為 false否則將不會解碼 Bitmap 數(shù)據(jù) , 只會將outWidth , outHeight , outConfig , outColorSpace 等 outXxx 圖片參數(shù)解碼出來*/options.inJustDecodeBounds = false;/*設(shè)置圖片可以被復(fù)用*/options.inMutable = true;/*如果設(shè)置了一個 Bitmap 對象給 inBitmap 參數(shù)解碼方法會獲取該 Bitmap 對象 , 當加載圖片內(nèi)容時 , 會嘗試復(fù)用該 Bitmap 對象的內(nèi)存如果解碼方法無法復(fù)用該 Bitmap 對象 , 解碼方法可能會拋出 IllegalArgumentException 異常 ;當前的實現(xiàn)是很有必要的 , 被復(fù)用的圖片必須是可變的 , 解碼后的 Bitmap 對象也是可變的 ,即使當解碼一個資源圖片時 , 經(jīng)常會得到一個不可變的 Bitmap 對象 ;確保是否解碼成功 :該解碼方法返回的 Bitmap 對象是可以使用的 ,鑒于上述約束情況 和 可能發(fā)生的失敗故障 , 不能假定該圖片解碼操作是成功的 ;檢查解碼返回的 Bitmap 對象是否與設(shè)置給 Options 對象的 inBitmap 相匹配 ,來判斷該 inBitmap 是否被復(fù)用 ;不管有沒有復(fù)用成功 , 你應(yīng)該使用解碼函數(shù)返回的 Bitmap 對象 , 保證程序的正常運行 ;與 BitmapFactory 配合使用 :在 KITKAT 以后的代碼中 , 只要被解碼生成的 Bitmap 對象的字節(jié)大小 ( 縮放后的 )小于等于 inBitmap 的字節(jié)大小 , 就可以復(fù)用成功 ;在 KITKAT 之前的代碼中 , 被解碼的圖像必須是JPEG 或 PNG 格式 ,并且 圖像大小必須是相等的 ,inssampleSize 設(shè)置為 1 ,才能復(fù)用成功 ;另外被復(fù)用的圖像的 像素格式 Config ( 如 RGB_565 ) 會覆蓋設(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 對象的寬高, 字節(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 對象的寬高, 字節(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 像素格式 , 每個像素 444 字節(jié) , 計算公式為
5224×2678×4=55,959,4885224 \times 2678 \times 4 = 55,959,4885224×2678×4=55,959,488
② 縮小后的圖像分析 : 從資源中加載 , 普通情況下寬度 163 像素 , 高度 81 像素 , RGB_565 像素格式 , 每個像素 222 字節(jié) , 計算公式為
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