1. 內存管理基礎知識html
http://www.cnblogs.com/xingfuzzhd/p/3485924.htmlandroid
mImageView.setImageResource(R.drawable.my_image);
這段代碼會調用 BitmapFactory.decodeStream() 生成一個 Bitmap。因此不要覺得它比本身建立 Bitmap 節省內存。
3. 實際測試:git
我使用了多種調用圖片的方法來測試:github
第一種:函數
// 直接載入資源 id
ImageView image = (ImageView) findViewById(R.id.imageView1)
第二種:測試
建立一個函數,根據 id 載入圖片。殺死 Activity 時回收 Bitmap優化
public BitmapDrawable getImageBitmap(int idImage) { Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), idImage); images.add(bitmap); return new BitmapDrawable(context.getResources(), bitmap); }
第三種:spa
建立一個類code
/** * solution for OOM ------> compress bitmap */ public class BitmapUtils { private static final String TAG = "BitmapUtils"; /** * calulate the compress rate */ private static int calculateInSampleSize(BitmapFactory.Options opts,int reqHeight,int reqWidth) { if(opts == null) { return -1; } int width = opts.outWidth; int height = opts.outHeight; int sampleSize = 1; if(width > reqWidth || height > reqHeight) { int heightRatio = (int) (height/(float)reqHeight); int widthRatio = (int) (width/(float)reqWidth); sampleSize = (heightRatio > widthRatio) ? widthRatio : heightRatio; } return sampleSize; } /** * compress an image refer to the goal dimension * @param res * @param resId * @param reqWidth * @param reqHeight * @return Bitmap */ public static Bitmap decodeSampledBitmapFromResource(Resources res,int resId,int reqWidth,int reqHeight) { BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, opts); int sampleSize = calculateInSampleSize(opts, reqHeight, reqWidth); Log.i(TAG,"before[width:"+opts.outWidth+",height:"+opts.outHeight+"]"); opts.inJustDecodeBounds = false; opts.inSampleSize = sampleSize; /* newly added */ opts.inPreferredConfig = Bitmap.Config.RGB_565; opts.inPurgeable = true; opts.inInputShareable = true; /* newly added */ Log.i(TAG,"insamplesize="+sampleSize); Bitmap bitmap = BitmapFactory.decodeResource(res, resId, opts); Log.i(TAG,"after[width:"+bitmap.getWidth()+",height:"+bitmap.getHeight()+"]"); return bitmap; } /** * compress an image refer to the goal dimension * * @param data image byte array * @param reqWidth * @param reqHeight * @return */ public static Bitmap decodeSampledBitmapFromByteArray(byte[] data,int reqWidth,int reqHeight) { BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds = true; BitmapFactory.decodeByteArray(data, 0, data.length, opts); Log.i(TAG,"before[width:"+opts.outWidth+",height:"+opts.outHeight+"]"); opts.inSampleSize = calculateInSampleSize(opts, reqHeight, reqWidth); Log.i(TAG,"insamplesize="+opts.inSampleSize); opts.inJustDecodeBounds = false; Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, opts); Log.i(TAG,"after[width:"+bitmap.getWidth()+",height:"+bitmap.getHeight()+"]"); return bitmap; } public static Bitmap decodeResourceWithLowMemory(Context context, int resId) { BitmapFactory.Options opt = new BitmapFactory.Options(); // Bitmap.Config.ALPHA_8,Bitmap.Config.ARGB_4444,Bitmap.Config.RGB_565 // 設置這幾個參數效果都差很少,確實至關省內存啊,並且還跟原圖清晰度至關 opt.inPreferredConfig = Bitmap.Config.RGB_565; opt.inPurgeable = true; opt.inInputShareable = true; return BitmapFactory.decodeResource(context.getResources(), resId, opt); } public static Bitmap loadToImageViewFromResource(Resources res,int resId,ImageView imageView) { BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, opts); int reqHeight = imageView.getLayoutParams().height; int reqWidth = imageView.getLayoutParams().width; int sampleSize = calculateInSampleSize(opts, reqHeight, reqWidth); Log.i(TAG,"before[width:"+opts.outWidth+",height:"+opts.outHeight+"]"); opts.inJustDecodeBounds = false; opts.inSampleSize = sampleSize; /* newly added */ opts.inPreferredConfig = Bitmap.Config.RGB_565; opts.inPurgeable = true; opts.inInputShareable = true; /* newly added */ Log.i(TAG,"insamplesize="+sampleSize); Bitmap bitmap = BitmapFactory.decodeResource(res, resId, opts); Log.i(TAG,"after[width:"+bitmap.getWidth()+",height:"+bitmap.getHeight()+"]"); return bitmap; } }
4. 測試過程:orm
建立兩個 Activity,每一個都載入幾個大圖片,而後兩個 Activity 之間不停的切換。
第一種方法,切換屢次後 OOM (out of memory)。 第二種方法,更快的 OOM。
第三種,若是壓縮率夠高的話,幾乎不會 OOM。好比原始圖片爲 2048 * 1600,而後目標 ImageView 的大小爲 200* 150。最後內存消耗將大大減少。而後若是
二者分辨率差距不大,最後內存消耗將不會被優化,最後也將 OOM。
5. 原理
一般,咱們使用BitmapFactory.decodeResource()方法來從資源文件中讀取一張 圖片並生成一個 Bitmap。但若是使用一個BitmapFactory.Options對象,並把該對象的inJustDecodeBounds屬性設 置爲true,decodeResource()方法就不會生成 Bitmap 對象,而僅僅是讀取該圖片的尺寸和類型信息。因此在前面的類的一些方法中,咱們用 BitmapFactory decode了兩次圖片,但第一次只是獲取信息而沒有生成 Bitmap 對象,因此沒有未回收的 Bitmap 出現。
6. 一些推薦的解決方案
弱引用,LruCache,或者開源項目 :Android-Universal-Image-Loader
延伸:一個Bitmap到底佔用多大內存?系統給每一個應用程序分配多大內存?
· Bitmap佔用的內存爲:像素總數 * 每一個像素佔用的內存。在Android中,Bitmap有四種像素類型:ARGB_888八、ARGB_444四、ARGB_56五、ALPHA_8,他 們每一個像素佔用的字節數分別爲四、二、二、1。所以,一個2000*1000的ARGB_8888類型的Bitmap佔用的內存爲 2000*1000*4=8000000B=8MB。
· Android根據設備屏幕尺寸和dpi的不一樣,給系統分配的單應用程序內存大小也不一樣,具體以下表(表格取自Android 4.4 Compatibility Definition Document (CDD)):
屏幕尺寸 | DPI | 應用內存 |
small / normal / large | ldpi / mdpi | 16MB |
small / normal / large | tvdpi / hdpi | 32MB |
small / normal / large | xhdpi | 64MB |
small / normal / large | 400dpi | 96MB |
small / normal / large | xxhdpi | 128MB |
xlarge | mdpi | 32MB |
xlarge | tvdpi / hdpi | 64MB |
xlarge | xhdpi | 128MB |
xlarge | 400dpi | 192MB |
xlarge | xxhdpi | 256MB |