目錄介紹
- 01.如何計算Bitmap佔用內存
- 1.1 如何計算佔用內存
- 1.2 上面方法計算內存對嗎
- 1.3 一個像素佔用多大內存
- 02.Bitmap常見四種顏色格式
- 2.1 什麼是bitmap
- 2.2 Android常見是那種
- 2.3 常見四種顏色格式介紹
- 2.4 Bitmap到底有幾種顏色格式
- 03.Bitmap壓縮技術
- 3.1 質量壓縮
- 3.2 採樣率壓縮
- 3.3 縮放法壓縮
- 04.Bitmap回收問題
- 4.1 recycle()方法
- 4.2 緩存原理
- 4.3 Bitmap的複用
- 05.Bitmap常見操作
- 5.1 Bitmap的壓縮方式
- 5.2 Bitmap如何複用
- 5.3 Bitmap使用API獲取內存
- 5.4 該博客對應測試項目地址
好消息
- 博客筆記大彙總【16年3月到至今】,包括Java基礎及深入知識點,Android技術博客,Python學習筆記等等,還包括平時開發中遇到的bug彙總,當然也在工作之餘收集了大量的面試題,長期更新維護並且修正,持續完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計50篇[近30萬字],轉載請註明出處,謝謝!
- 鏈接地址:https://github.com/yangchong211/YCBlogs
- 如果覺得好,可以star一下,謝謝!當然也歡迎提出建議,萬事起於忽微,量變引起質變!
- 輪播圖封裝庫:https://github.com/yangchong211/YCBanner
- 輕量級版本更新彈窗:https://github.com/yangchong211/YCUpdateApp
- 通知欄封裝庫:https://github.com/yangchong211/YCNotification
01.如何計算Bitmap佔用內存
- 歡迎直接查看demo的壓縮效果,https://github.com/yangchong211/YCBanner
1.1 如何計算佔用內存
- 如果圖片要顯示下Android設備上,ImageView最終是要加載Bitmap對象的,就要考慮單個Bitmap對象的內存佔用了,如何計算一張圖片的加載到內存的佔用呢?其實就是所有像素的內存佔用總和:
- bitmap內存大小 = 圖片長度 x 圖片寬度 x 單位像素佔用的字節數
- 起決定因素就是最後那個參數了,Bitmap'常見有2種編碼方式:ARGB_8888和RGB_565,ARGB_8888每個像素點4個byte,RGB_565是2個byte,一般都採用ARGB_8888這種。那麼常見的1080*1920的圖片內存佔用就是:1920 x 1080 x 4 = 7.9M
1.2 上面方法計算內存對嗎
-
我看到好多博客都是這樣計算的,但是這樣算對嗎?有沒有哥們試驗過這種方法正確性?我覺得看博客要對博主表示懷疑,論證別人寫的是否正確。更多詳細可以看我的GitHub:https://github.com/yangchong211
- 說出我的結論:上面1.1這種說法也對,但是不全對,沒有說明場景,同時也忽略了一個影響項:Density。接下來看看源代碼。
- inDensity默認爲圖片所在文件夾對應的密度;inTargetDensity爲當前系統密度。
-
加載一張本地資源圖片,那麼它佔用的內存 = width height nTargetDensity/inDensity nTargetDensity/inDensity 一個像素所佔的內存。
@Nullable public static Bitmap decodeResourceStream(@Nullable Resources res, @Nullable TypedValue value, @Nullable InputStream is, @Nullable Rect pad, @Nullable Options opts) { validate(opts); if (opts == null) { opts = new Options(); } if (opts.inDensity == 0 && value != null) { final int density = value.density; if (density == TypedValue.DENSITY_DEFAULT) { opts.inDensity = DisplayMetrics.DENSITY_DEFAULT; } else if (density != TypedValue.DENSITY_NONE) { opts.inDensity = density; } } if (opts.inTargetDensity == 0 && res != null) { opts.inTargetDensity = res.getDisplayMetrics().densityDpi; } return decodeStream(is, pad, opts); }
- 正確說法,這個注意呢?計算公式如下所示
- 對資源文件:width height nTargetDensity/inDensity nTargetDensity/inDensity 一個像素所佔的內存;
- 別的:width height 一個像素所佔的內存;
1.3 一個像素佔用多大內存
- Bitmap.Config用來描述圖片的像素是怎麼被存儲的?
- ARGB_8888: 每個像素4字節. 共32位,默認設置。
- Alpha_8: 只保存透明度,共8位,1字節。
- ARGB_4444: 共16位,2字節。
- RGB_565:共16位,2字節,只存儲RGB值。
02.Bitmap常見四種顏色格式
2.1 什麼是bitmap
- 位圖文件(Bitmap),擴展名可以是.bmp或者.dib。位圖是Windows標準格式圖形文件,它將圖像定義爲由點(像素)組成,每個點可以由多種色彩表示,包括2、4、8、16、24和32位色彩。位圖文件是非壓縮格式的,需要佔用較大存儲空間。
2.2 Android常見是那種
- 在Gesture類中
- 在Notification類中
- 在fw源碼中bitmap圖片一般是以ARGB_8888(ARGB分別代表的是透明度,紅色,綠色,藍色,每個值分別用8bit來記錄,也就是一個像素會佔用4byte,共32bit)來進行存儲的。
2.3 常見四種顏色格式介紹
- 四種顏色格式如下所示
- 說明
- 在實際應用中而言,建議使用ARGB_8888以及RGB_565。 如果你不需要透明度,選擇RGB_565,可以減少一半的內存佔用。
- ARGB_8888:ARGB分別代表的是透明度,紅色,綠色,藍色,每個值分別用8bit來記錄,也就是一個像素會佔用4byte,共32bit.
- ARGB_4444:ARGB的是每個值分別用4bit來記錄,一個像素會佔用2byte,共16bit.
- RGB_565:R=5bit,G=6bit,B=5bit,不存在透明度,每個像素會佔用2byte,共16bit
- ALPHA_8:該像素只保存透明度,會佔用1byte,共8bit.
2.4 Bitmap到底有幾種顏色格式
- 上面我說到了常見的四種,言下之意應該不止四種,那到底有幾種呢?查看源碼可知,具體有6種類型。查看Bitmap源碼之Config配置。
- 配置Config.HARDWARE爲啥異常,看下面源碼提示
03.Bitmap壓縮技術
3.1 質量壓縮
-
質量壓縮方法:在保持像素的前提下改變圖片的位深及透明度等,來達到壓縮圖片的目的,這樣適合去傳遞二進制的圖片數據,比如分享圖片,要傳入二進制數據過去,限制500kb之內。
- 1、bitmap圖片的大小不會改變
- 2、bytes.length是隨着quality變小而變小的。
/** * 第一種:質量壓縮法 * @param image 目標原圖 * @param maxSize 最大的圖片大小 * @return bitmap,注意可以測試以下壓縮前後bitmap的大小值 */ public static Bitmap compressImage(Bitmap image , long maxSize) { int byteCount = image.getByteCount(); Log.i("yc壓縮圖片","壓縮前大小"+byteCount); ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 把ByteArrayInputStream數據生成圖片 Bitmap bitmap = null; // 質量壓縮方法,options的值是0-100,這裏100表示原來圖片的質量,不壓縮,把壓縮後的數據存放到baos中 image.compress(Bitmap.CompressFormat.JPEG, 100, baos); int options = 90; // 循環判斷如果壓縮後圖片是否大於maxSize,大於繼續壓縮 while (baos.toByteArray().length > maxSize) { // 重置baos即清空baos baos.reset(); // 這裏壓縮options%,把壓縮後的數據存放到baos中 image.compress(Bitmap.CompressFormat.JPEG, options, baos); // 每次都減少10,當爲1的時候停止,options<10的時候,遞減1 if(options == 1){ break; }else if (options <= 10) { options -= 1; } else { options -= 10; } } byte[] bytes = baos.toByteArray(); if (bytes.length != 0) { // 把壓縮後的數據baos存放到bytes中 bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); int byteCount1 = bitmap.getByteCount(); Log.i("yc壓縮圖片","壓縮後大小"+byteCount1); } return bitmap; }
/**
- 第一種:質量壓縮法
- @param src 源圖片
- @param maxByteSize 允許最大值字節數
- @param recycle 是否回收
- @return 質量壓縮壓縮過的圖片
*/
public static Bitmap compressByQuality(final Bitmap src, final long maxByteSize, final boolean recycle) {
if (src == null || src.getWidth() == 0 || src.getHeight() == 0 || maxByteSize <= 0) {
return null;
}
Log.i("yc壓縮圖片","壓縮前大小"+src.getByteCount());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
src.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] bytes;
if (baos.size() <= maxByteSize) {// 最好質量的不大於最大字節,則返回最佳質量
bytes = baos.toByteArray();
} else {
baos.reset();
src.compress(Bitmap.CompressFormat.JPEG, 0, baos);
if (baos.size() >= maxByteSize) { // 最差質量不小於最大字節,則返回最差質量
bytes = baos.toByteArray();
} else {
// 二分法尋找最佳質量
int st = 0;
int end = 100;
int mid = 0;
while (st < end) {
mid = (st + end) / 2;
baos.reset();
src.compress(Bitmap.CompressFormat.JPEG, mid, baos);
int len = baos.size();
if (len == maxByteSize) {
break;
} else if (len > maxByteSize) {
end = mid - 1;
} else {
st = mid + 1;
}
}
if (end == mid - 1) {
baos.reset();
src.compress(Bitmap.CompressFormat.JPEG, st, baos);
}
bytes = baos.toByteArray();
}
}
if (recycle && !src.isRecycled()){
src.recycle();
}
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
Log.i("yc壓縮圖片","壓縮後大小"+bitmap.getByteCount());
return bitmap;
}
/**
- 第一種:質量壓縮法
- @param src 源圖片
- @param quality 質量
- @param recycle 是否回收
- @return 質量壓縮後的圖片
*/
public static Bitmap compressByQuality(final Bitmap src, @IntRange(from = 0, to = 100) final int quality, final boolean recycle) {
if (src == null || src.getWidth() == 0 || src.getHeight() == 0) {
return null;
}
Log.i("yc壓縮圖片","壓縮前大小"+src.getByteCount());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
src.compress(Bitmap.CompressFormat.JPEG, quality, baos);
byte[] bytes = baos.toByteArray();
if (recycle && !src.isRecycled()) {
src.recycle();
}
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
Log.i("yc壓縮圖片","壓縮後大小"+bitmap.getByteCount());
return bitmap;
}
3.2 採樣率壓縮
-
什麼是採樣率壓縮?
- 設置inSampleSize的值(int類型)後,假如設爲n,則寬和高都爲原來的1/n,寬高都減少,內存降低。上面的代碼沒用過options.inJustDecodeBounds = true;因爲我是固定來取樣的數據,爲什麼這個壓縮方法叫採樣率壓縮?是因爲配合inJustDecodeBounds,先獲取圖片的寬、高(這個過程就是取樣)。然後通過獲取的寬高,動態的設置inSampleSize的值。當inJustDecodeBounds設置爲true的時候, BitmapFactory通過decodeResource或者decodeFile解碼圖片時,將會返回空(null)的Bitmap對象,這樣可以避免Bitmap的內存分配, 但是它可以返回Bitmap的寬度、高度以及MimeType。
/** * 第二種:按採樣大小壓縮 * * @param src 源圖片 * @param sampleSize 採樣率大小 * @param recycle 是否回收 * @return 按採樣率壓縮後的圖片 */ public static Bitmap compressBySampleSize(final Bitmap src, final int sampleSize, final boolean recycle) { if (src == null || src.getWidth() == 0 || src.getHeight() == 0) { return null; } Log.i("yc壓縮圖片","壓縮前大小"+src.getByteCount()); BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = sampleSize; ByteArrayOutputStream baos = new ByteArrayOutputStream(); src.compress(Bitmap.CompressFormat.JPEG, 100, baos); byte[] bytes = baos.toByteArray(); if (recycle && !src.isRecycled()) { src.recycle(); } Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); Log.i("yc壓縮圖片","壓縮後大小"+bitmap.getByteCount()); return bitmap; }
/**
- 第二種:按採樣大小壓縮
- @param src 源圖片
- @param maxWidth 最大寬度
- @param maxHeight 最大高度
- @param recycle 是否回收
- @return 按採樣率壓縮後的圖片
*/
public static Bitmap compressBySampleSize(final Bitmap src, final int maxWidth, final int maxHeight, final boolean recycle) {
if (src == null || src.getWidth() == 0 || src.getHeight() == 0) {
return null;
}
Log.i("yc壓縮圖片","壓縮前大小"+src.getByteCount());
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
src.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] bytes = baos.toByteArray();
BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
options.inJustDecodeBounds = false;
if (recycle && !src.isRecycled()) {
src.recycle();
}
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
Log.i("yc壓縮圖片","壓縮後大小"+bitmap.getByteCount());
return bitmap;
}
/**
- 計算獲取縮放比例inSampleSize
/
private static int calculateInSampleSize(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) {
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
final float totalPixels = width height;
final float totalReqPixelsCap = reqWidth reqHeight 2;
while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
inSampleSize++;
}
return inSampleSize;
}
- 設置inSampleSize的值(int類型)後,假如設爲n,則寬和高都爲原來的1/n,寬高都減少,內存降低。上面的代碼沒用過options.inJustDecodeBounds = true;因爲我是固定來取樣的數據,爲什麼這個壓縮方法叫採樣率壓縮?是因爲配合inJustDecodeBounds,先獲取圖片的寬、高(這個過程就是取樣)。然後通過獲取的寬高,動態的設置inSampleSize的值。當inJustDecodeBounds設置爲true的時候, BitmapFactory通過decodeResource或者decodeFile解碼圖片時,將會返回空(null)的Bitmap對象,這樣可以避免Bitmap的內存分配, 但是它可以返回Bitmap的寬度、高度以及MimeType。
3.3 縮放法壓縮
-
Android中使用Matrix對圖像進行縮放、旋轉、平移、斜切等變換的。
- Matrix提供了一些方法來控制圖片變換:Matrix調用一系列set,pre,post方法時,可視爲將這些方法插入到一個隊列。當然,按照隊列中從頭至尾的順序調用執行。其中pre表示在隊頭插入一個方法,post表示在隊尾插入一個方法。而set表示把當前隊列清空,並且總是位於隊列的最中間位置。當執行了一次set後:pre方法總是插入到set前部的隊列的最前面,post方法總是插入到set後部的隊列的最後面
setTranslate(float dx,float dy):控制Matrix進行位移。 setSkew(float kx,float ky):控制Matrix進行傾斜,kx、ky爲X、Y方向上的比例。 setSkew(float kx,float ky,float px,float py):控制Matrix以px、py爲軸心進行傾斜,kx、ky爲X、Y方向上的傾斜比例。 setRotate(float degrees):控制Matrix進行depress角度的旋轉,軸心爲(0,0)。 setRotate(float degrees,float px,float py):控制Matrix進行depress角度的旋轉,軸心爲(px,py)。 setScale(float sx,float sy):設置Matrix進行縮放,sx、sy爲X、Y方向上的縮放比例。 setScale(float sx,float sy,float px,float py):設置Matrix以(px,py)爲軸心進行縮放,sx、sy爲X、Y方向上的縮放比例。
- 縮放法壓縮工具類代碼
/** * 第三種:按縮放壓縮 * * @param src 源圖片 * @param newWidth 新寬度 * @param newHeight 新高度 * @param recycle 是否回收 * @return 縮放壓縮後的圖片 */ public static Bitmap compressByScale(final Bitmap src, final int newWidth, final int newHeight, final boolean recycle) { return scale(src, newWidth, newHeight, recycle); }
public static Bitmap compressByScale(final Bitmap src, final float scaleWidth, final float scaleHeight, final boolean recycle) {
return scale(src, scaleWidth, scaleHeight, recycle);
}/**
- 縮放圖片
- @param src 源圖片
- @param scaleWidth 縮放寬度倍數
- @param scaleHeight 縮放高度倍數
- @param recycle 是否回收
- @return 縮放後的圖片
*/
private static Bitmap scale(final Bitmap src, final float scaleWidth, final float scaleHeight, final boolean recycle) {
if (src == null || src.getWidth() == 0 || src.getHeight() == 0) {
return null;
}
Matrix matrix = new Matrix();
matrix.setScale(scaleWidth, scaleHeight);
Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
if (recycle && !src.isRecycled()) {
src.recycle();
}
return ret;
}
- Matrix提供了一些方法來控制圖片變換:Matrix調用一系列set,pre,post方法時,可視爲將這些方法插入到一個隊列。當然,按照隊列中從頭至尾的順序調用執行。其中pre表示在隊頭插入一個方法,post表示在隊尾插入一個方法。而set表示把當前隊列清空,並且總是位於隊列的最中間位置。當執行了一次set後:pre方法總是插入到set前部的隊列的最前面,post方法總是插入到set後部的隊列的最後面
04.Bitmap回收問題
4.1 recycle()方法
- 如何調用這個recycle()方法
if (bitmap != null && !bitmap.isRecycled()) { bitmap.recycle(); bitmap = null; }
- 思考以下,爲何調用recycle()需要做非空判斷?這裏可以引出bitmap系統回收功能。小楊我如果分析不對,歡迎反饋。
- 首先看看源碼……順便翻一下該方法的註釋!我是用有道翻譯的,大意如下:釋放與此位圖關聯的本機對象,並清除對像素數據的引用。這將不會同步釋放像素數據;如果沒有其他引用,它只允許垃圾收集。位圖被標記爲“死”,這意味着如果調用getPixels()或setPixels(),它將拋出異常,並且不會繪製任何東西。此操作不能反轉,因此只有在確定沒有進一步使用位圖的情況下才應調用該操作。這是一個高級調用,通常不需要調用,因爲當沒有對此位圖的引用時,普通GC進程將釋放此內存。
public void recycle() { if (!mRecycled && mNativePtr != 0) { if (nativeRecycle(mNativePtr)) { // return value indicates whether native pixel object was actually recycled. // false indicates that it is still in use at the native level and these // objects should not be collected now. They will be collected later when the // Bitmap itself is collected. mNinePatchChunk = null; } mRecycled = true; } }
- 首先看看源碼……順便翻一下該方法的註釋!我是用有道翻譯的,大意如下:釋放與此位圖關聯的本機對象,並清除對像素數據的引用。這將不會同步釋放像素數據;如果沒有其他引用,它只允許垃圾收集。位圖被標記爲“死”,這意味着如果調用getPixels()或setPixels(),它將拋出異常,並且不會繪製任何東西。此操作不能反轉,因此只有在確定沒有進一步使用位圖的情況下才應調用該操作。這是一個高級調用,通常不需要調用,因爲當沒有對此位圖的引用時,普通GC進程將釋放此內存。
- 通常不需要調用?這是爲啥?
- 在Android3.0以後Bitmap是存放在堆中的,只要回收堆內存即可。官方建議我們3.0以後使用recycle()方法進行回收,該方法可以不主動調用,因爲垃圾回收器會自動收集不可用的Bitmap對象進行回收。
- 那麼何是進行回收呢?這裏面涉及到bitmap的緩存算法,還有GC回收垃圾機制。關於GC回收機制可以看我這篇博客:https://blog.csdn.net/m0_37700275/article/details/83651039
- 大概就是移除最少使用的緩存和使用最久的緩存,先說出結論,下來接着分析!
4.2 緩存原理
- LruCache原理
- LruCache是個泛型類,內部採用LinkedHashMap來實現緩存機制,它提供get方法和put方法來獲取緩存和添加緩存,其最重要的方法trimToSize是用來移除最少使用的緩存和使用最久的緩存,並添加最新的緩存到隊列中。
4.3 Bitmap的複用
- Android3.0之後,並沒有強調Bitmap.recycle();而是強調Bitmap的複用。
- 使用LruCache對Bitmap進行緩存,當再次使用到這個Bitmap的時候直接獲取,而不用重走編碼流程。
- Android3.0(API 11之後)引入了BitmapFactory.Options.inBitmap字段,設置此字段之後解碼方法會嘗試複用一張存在的Bitmap。這意味着Bitmap的內存被複用,避免了內存的回收及申請過程,顯然性能表現更佳。
- 使用這個字段有幾點限制:
- 聲明可被複用的Bitmap必須設置inMutable爲true;
- Android4.4(API 19)之前只有格式爲jpg、png,同等寬高(要求苛刻),inSampleSize爲1的Bitmap纔可以複用;
- Android4.4(API 19)之前被複用的Bitmap的inPreferredConfig會覆蓋待分配內存的Bitmap設置的inPreferredConfig;
- Android4.4(API 19)之後被複用的Bitmap的內存必須大於需要申請內存的Bitmap的內存;
- Android4.4(API 19)之前待加載Bitmap的Options.inSampleSize必須明確指定爲1。
05.Bitmap常見操作
5.1 Bitmap的壓縮方式
- 常見壓縮方法Api
- Bitmap.compress(),質量壓縮,不會對內存產生影響;
- BitmapFactory.Options.inSampleSize,內存壓縮;
- Bitmap.compress()
- 質量壓縮,不會對內存產生影響
- 它是在保持像素的前提下改變圖片的位深及透明度等,來達到壓縮圖片的目的,不會減少圖片的像素。進過它壓縮的圖片文件大小會變小,但是解碼成bitmap後佔得內存是不變的。
- BitmapFactory.Options.inSampleSize
- 內存壓縮
- 解碼圖片時,設置BitmapFactory.Options類的inJustDecodeBounds屬性爲true,可以在Bitmap不被加載到內存的前提下,獲取Bitmap的原始寬高。而設置BitmapFactory.Options的inSampleSize屬性可以真實的壓縮Bitmap佔用的內存,加載更小內存的Bitmap。
- 設置inSampleSize之後,Bitmap的寬、高都會縮小inSampleSize倍。例如:一張寬高爲2048x1536的圖片,設置inSampleSize爲4之後,實際加載到內存中的圖片寬高是512x384。佔有的內存就是0.75M而不是12M,足足節省了15倍。
- 備註:inSampleSize值的大小不是隨便設、或者越大越好,需要根據實際情況來設置。inSampleSize比1小的話會被當做1,任何inSampleSize的值會被取接近2的冪值。
5.2 Bitmap如何複用
-
Bitmap複用的實驗,代碼如下所示,然後看打印的日誌信息
- 從內存地址的打印可以看出,兩個對象其實是一個對象,Bitmap複用成功;
- bitmapReuse佔用的內存(4346880)正好是bitmap佔用內存(1228800)的四分之一;
-
getByteCount()獲取到的是當前圖片應當所佔內存大小,getAllocationByteCount()獲取到的是被複用Bitmap真實佔用內存大小。雖然bitmapReuse的內存只有4346880,但是因爲是複用的bitmap的內存,因而其真實佔用的內存大小是被複用的bitmap的內存大小(1228800)。這也是getAllocationByteCount()可能比getByteCount()大的原因。
@RequiresApi(api = Build.VERSION_CODES.KITKAT) private void initBitmap() { BitmapFactory.Options options = new BitmapFactory.Options(); // 圖片複用,這個屬性必須設置; options.inMutable = true; // 手動設置縮放比例,使其取整數,方便計算、觀察數據; options.inDensity = 320; options.inTargetDensity = 320; Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bg_autumn_tree_min, options); // 對象內存地址; Log.i("ycBitmap", "bitmap = " + bitmap); Log.i("ycBitmap", "ByteCount = " + bitmap.getByteCount() + ":::bitmap:AllocationByteCount = " + bitmap.getAllocationByteCount()); // 使用inBitmap屬性,這個屬性必須設置; options.inBitmap = bitmap; options.inDensity = 320; // 設置縮放寬高爲原始寬高一半; options.inTargetDensity = 160; options.inMutable = true; Bitmap bitmapReuse = BitmapFactory.decodeResource(getResources(), R.drawable.bg_kites_min, options); // 複用對象的內存地址; Log.i("ycBitmap", "bitmapReuse = " + bitmapReuse); Log.i("ycBitmap", "bitmap:ByteCount = " + bitmap.getByteCount() + ":::bitmap:AllocationByteCount = " + bitmap.getAllocationByteCount()); Log.i("ycBitmap", "bitmapReuse:ByteCount = " + bitmapReuse.getByteCount() + ":::bitmapReuse:AllocationByteCount = " + bitmapReuse.getAllocationByteCount()); //11-26 18:24:07.971 15470-15470/com.yc.cn.ycbanner I/ycBitmap: bitmap = android.graphics.Bitmap@9739bff //11-26 18:24:07.972 15470-15470/com.yc.cn.ycbanner I/ycBitmap: bitmap:ByteCount = 4346880:::bitmap:AllocationByteCount = 4346880 //11-26 18:24:07.994 15470-15470/com.yc.cn.ycbanner I/ycBitmap: bitmapReuse = android.graphics.Bitmap@9739bff //11-26 18:24:07.994 15470-15470/com.yc.cn.ycbanner I/ycBitmap: bitmap:ByteCount = 1228800:::bitmap:AllocationByteCount = 4346880 //11-26 18:24:07.994 15470-15470/com.yc.cn.ycbanner I/ycBitmap: bitmapReuse:ByteCount = 1228800:::bitmapReuse:AllocationByteCount = 4346880 }
5.3 Bitmap使用API獲取內存
- getByteCount()
- getByteCount()方法是在API12加入的,代表存儲Bitmap的色素需要的最少內存。API19開始getAllocationByteCount()方法代替了getByteCount()。
- getAllocationByteCount()
- API19之後,Bitmap加了一個Api:getAllocationByteCount();代表在內存中爲Bitmap分配的內存大小。
public final int getAllocationByteCount() { if (mRecycled) { Log.w(TAG, "Called getAllocationByteCount() on a recycle()'d bitmap! " + "This is undefined behavior!"); return 0; } return nativeGetAllocationByteCount(mNativePtr); }
- API19之後,Bitmap加了一個Api:getAllocationByteCount();代表在內存中爲Bitmap分配的內存大小。
- 思考: getByteCount()與getAllocationByteCount()的區別?
- 一般情況下兩者是相等的;
- 通過複用Bitmap來解碼圖片,如果被複用的Bitmap的內存比待分配內存的Bitmap大,那麼getByteCount()表示新解碼圖片佔用內存的大小(並非實際內存大小,實際大小是複用的那個Bitmap的大小),getAllocationByteCount()表示被複用Bitmap真實佔用的內存大小(即mBuffer的長度)。
- 在複用Bitmap的情況下,getAllocationByteCount()可能會比getByteCount()大。
5.4 該博客對應測試項目地址
- 歡迎直接查看demo的壓縮效果,https://github.com/yangchong211/YCBanner
關於其他內容介紹
01.關於博客彙總鏈接
02.關於我的博客
- 我的個人站點:www.yczbj.org,www.ycbjie.cn
- github:https://github.com/yangchong211
- 知乎:https://www.zhihu.com/people/yczbj/activities
- 簡書:http://www.jianshu.com/u/b7b2c6ed9284
- csdn:http://my.csdn.net/m0_37700275
- 喜馬拉雅聽書:http://www.ximalaya.com/zhubo/71989305/
- 開源中國:https://my.oschina.net/zbj1618/blog
- 泡在網上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
- 郵箱:[email protected]
- 阿里雲博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
- segmentfault頭條:https://segmentfault.com/u/xiangjianyu/articles
- 掘金:https://juejin.im/user/5939433efe88c2006afa0c6e