Bitmap具體解釋與Bitmap的內存優化

感受這裏的排版看着更舒服些 Bitmap具體解釋與Bitmap的內存優化java

1、Bitmap:

Bitmap是Android系統中的圖像處理的最重要類之中的一個。用它可以獲取圖像文件信息,進行圖像剪切、旋轉、縮放等操做。並可以指定格式保存圖像文件。android


經常用法:
+ public void recycle()  // 回收位圖佔用的內存空間。把位圖標記爲Dead
+ public final boolean isRecycled()  //推斷位圖內存是否已釋放
+ public final int getWidth() //獲取位圖的寬度
+ public final int getHeight() //獲取位圖的高度
+ public final boolean isMutable() //圖片是否可改動
+ public int getScaledWidth(Canvas canvas) //獲取指定密度轉換後的圖像的寬度
+ public int getScaledHeight(Canvas canvas) //獲取指定密度轉換後的圖像的高度
+ public boolean compress(CompressFormat format, int quality, OutputStream stream) //按指定的圖片格式以及畫質,將圖片轉換爲輸出流。canvas


format:壓縮圖像的格式,如Bitmap.CompressFormat.PNG或Bitmap.CompressFormat.JPEG
quality:畫質,0-100.0表示最低畫質壓縮。100以最高畫質壓縮。數組

對於PNG等無損格式的圖片,會忽略此項設置。
stream: OutputStream中寫入壓縮數據。緩存


return: 是否成功壓縮到指定的流。服務器


+ public static Bitmap createBitmap(Bitmap src)  //以src爲原圖生成不可變得新圖像
+ public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) //以src爲原圖,建立新的圖像。指定新圖像的高寬以及是否可變。
+ public static Bitmap createBitmap(int width, int height, Config config) //建立指定格式、大小的位圖
+ public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) //以source爲原圖,建立新的圖片,指定起始座標以及新圖像的高寬。markdown


2、BitmapFactory工廠類:

Option 參數類:
+ public boolean inJustDecodeBounds //假設設置爲true。不獲取圖片。不分配內存,但會返回圖片的高度寬度信息。網絡


假設將這個值置爲true,那麼在解碼的時候將不會返回bitmap,僅僅會返回這個bitmap的尺寸。這個屬性的目的是,假設你僅僅想知道一個bitmap的尺寸,但又不想將其載入到內存時。app

這是一個很實用的屬性。
+ public int inSampleSize //圖片縮放的倍數
這個值是一個int,當它小於1的時候,將會被當作1處理。假設大於1。那麼就會依照比例(1 / inSampleSize)縮小bitmap的寬和高、下降分辨率。大於1時這個值將會被處置爲2的倍數。好比,width=100。height=100,inSampleSize=2。那麼就會將bitmap處理爲,width=50。height=50。寬高降爲1 / 2,像素數降爲1 / 4。函數


+ public int outWidth //獲取圖片的寬度值
+ public int outHeight //獲取圖片的高度值
表示這個Bitmap的寬和高。通常和inJustDecodeBounds一塊兒使用來得到Bitmap的寬高。但是不載入到內存。
+ public int inDensity //用於位圖的像素壓縮比
+ public int inTargetDensity //用於目標位圖的像素壓縮比(要生成的位圖)
+ public byte[] inTempStorage  //建立暫時文件。將圖片存儲
+ public boolean inScaled //設置爲true時進行圖片壓縮,從inDensity到inTargetDensity
+ public boolean inDither  //假設爲true,解碼器嘗試抖動解碼
+ public Bitmap.Config inPreferredConfig  //設置解碼器
這個值是設置色彩模式。默認值是ARGB_8888。在這個模式下。一個像素點佔用4bytes空間,通常對透明度不作要求的話。通常採用RGB_565模式,這個模式下一個像素點佔用2bytes。
+ public String outMimeType  //設置解碼圖像
+ public boolean inPurgeable //當存儲Pixel的內存空間在系統內存不足時可否夠被回收
+ public boolean inInputShareable  //inPurgeable爲true狀況下才生效,可否夠共享一個InputStream
+ public boolean inPreferQualityOverSpeed  //爲true則優先保證Bitmap質量其次是解碼速度
+ public boolean inMutable  //配置Bitmap可否夠更改,比方:在Bitmap上隔幾個像素加一條線段
+ public int inScreenDensity  //當前屏幕的像素密度

工廠方法:
+ public static Bitmap decodeFile(String pathName, Options opts)  //從文件讀取圖片
+ public static Bitmap decodeFile(String pathName)
+ public static Bitmap decodeStream(InputStream is)  //從輸入流讀取圖片
+ public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts)
+ public static Bitmap decodeResource(Resources res, int id)  //從資源文件讀取圖片
+ public static Bitmap decodeResource(Resources res, int id, Options opts)
+ public static Bitmap decodeByteArray(byte[] data, int offset, int length)  //從數組讀取圖片
+ public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts)
+ public static Bitmap decodeFileDescriptor(FileDescriptor fd) //從文件讀取文件 與decodeFile不一樣的是這個直接調用JNI函數進行讀取 效率比較高
+ public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)

* Bitmap.Config inPreferredConfig :*
枚舉變量 (位圖位數越高表明其可以存儲的顏色信息越多,圖像越逼真,佔用內存越大)
+ public static final Bitmap.Config ALPHA_8  //表明8位Alpha位圖 每個像素佔用1byte內存
+ public static final Bitmap.Config ARGB_4444  //表明16位ARGB位圖 每個像素佔用2byte內存
+ public static final Bitmap.Config ARGB_8888  //表明32位ARGB位圖 每個像素佔用4byte內存
+ public static final Bitmap.Config RGB_565  //表明8位RGB位圖 每個像素佔用2byte內存

Android中一張圖片(BitMap)佔用的內存主要和下面幾個因數有關:圖片長度,圖片寬度,單位像素佔用的字節數。一張圖片(BitMap)佔用的內存=圖片長度*圖片寬度*單位像素佔用的字節數。


3、Bitmap載入方式

Bitmap的載入方式有Resource資源載入、本地(SDcard)載入、網絡載入等載入方式。

1. 從本地(SDcard)文件讀取

  • 方式一
/** * 獲取縮放後的本地圖片 * * @param filePath 文件路徑 * @param width 寬 * @param height 高 * @return */
    public static Bitmap readBitmapFromFile(String filePath, int width, int height) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(filePath, options);
        float srcWidth = options.outWidth;
        float srcHeight = options.outHeight;
        int inSampleSize = 1;

        if (srcHeight > height || srcWidth > width) {
            if (srcWidth > srcHeight) {
                inSampleSize = Math.round(srcHeight / height);
            } else {
                inSampleSize = Math.round(srcWidth / width);
            }
        }

        options.inJustDecodeBounds = false;
        options.inSampleSize = inSampleSize;

        return BitmapFactory.decodeFile(filePath, options);
    }
  • 方式二 (效率高於方式一)
/** * 獲取縮放後的本地圖片 * * @param filePath 文件路徑 * @param width 寬 * @param height 高 * @return */
    public static Bitmap readBitmapFromFileDescriptor(String filePath, int width, int height) {
        try {
            FileInputStream fis = new FileInputStream(filePath);
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFileDescriptor(fis.getFD(), null, options);
            float srcWidth = options.outWidth;
            float srcHeight = options.outHeight;
            int inSampleSize = 1;

            if (srcHeight > height || srcWidth > width) {
                if (srcWidth > srcHeight) {
                    inSampleSize = Math.round(srcHeight / height);
                } else {
                    inSampleSize = Math.round(srcWidth / width);
                }
            }

            options.inJustDecodeBounds = false;
            options.inSampleSize = inSampleSize;

            return BitmapFactory.decodeFileDescriptor(fis.getFD(), null, options);
        } catch (Exception ex) {
        }
        return null;
    }

2. 從輸入流中讀取文件(網絡載入)

/** * 獲取縮放後的本地圖片 * * @param ins 輸入流 * @param width 寬 * @param height 高 * @return */
    public static Bitmap readBitmapFromInputStream(InputStream ins, int width, int height) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(ins, null, options);
        float srcWidth = options.outWidth;
        float srcHeight = options.outHeight;
        int inSampleSize = 1;

        if (srcHeight > height || srcWidth > width) {
            if (srcWidth > srcHeight) {
                inSampleSize = Math.round(srcHeight / height);
            } else {
                inSampleSize = Math.round(srcWidth / width);
            }
        }

        options.inJustDecodeBounds = false;
        options.inSampleSize = inSampleSize;

        return BitmapFactory.decodeStream(ins, null, options);
    }

3.Resource資源載入

  1. Res資源載入方式:
public static Bitmap readBitmapFromResource(Resources resources, int resourcesId, int width, int height) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(resources, resourcesId, options);
        float srcWidth = options.outWidth;
        float srcHeight = options.outHeight;
        int inSampleSize = 1;

        if (srcHeight > height || srcWidth > width) {
            if (srcWidth > srcHeight) {
                inSampleSize = Math.round(srcHeight / height);
            } else {
                inSampleSize = Math.round(srcWidth / width);
            }
        }

        options.inJustDecodeBounds = false;
        options.inSampleSize = inSampleSize;

        return BitmapFactory.decodeResource(resources, resourcesId, options);
    }

此種方式至關的耗費內存 建議採用decodeStream取代decodeResource 可以例如如下形式:

public static Bitmap readBitmapFromResource(Resources resources, int resourcesId, int width, int height) {
        InputStream ins = resources.openRawResource(resourcesId);
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(ins, null, options);
        float srcWidth = options.outWidth;
        float srcHeight = options.outHeight;
        int inSampleSize = 1;

        if (srcHeight > height || srcWidth > width) {
            if (srcWidth > srcHeight) {
                inSampleSize = Math.round(srcHeight / height);
            } else {
                inSampleSize = Math.round(srcWidth / width);
            }
        }

        options.inJustDecodeBounds = false;
        options.inSampleSize = inSampleSize;

        return BitmapFactory.decodeStream(ins, null, options);
    }

BitmapFactory.decodeResource 載入的圖片可能會通過縮放,該縮放眼下是放在 java 層作的,效率比較低,而且需要消耗 java 層的內存。

所以。假設大量使用該接口載入圖片,easy致使OOM錯誤
BitmapFactory.decodeStream 不會對所載入的圖片進行縮放,相比之下佔用內存少。效率更高。


這兩個接口各實用處,假設對性能要求較高,則應該使用 decodeStream;假設對性能要求不高,且需要 Android 自帶的圖片自適應縮放功能。則可以使用 decodeResource。
2. Assets資源載入方式:

/** * 獲取縮放後的本地圖片 * * @param filePath 文件路徑,即文件名 * @return */
    public static Bitmap readBitmapFromAssetsFile(Context context, String filePath) {
        Bitmap image = null;
        AssetManager am = context.getResources().getAssets();
        try {
            InputStream is = am.open(filePath);
            image = BitmapFactory.decodeStream(is);
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return image;
    }

4.從二進制數據讀取圖片

public static Bitmap readBitmapFromByteArray(byte[] data, int width, int height) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeByteArray(data, 0, data.length, options);
        float srcWidth = options.outWidth;
        float srcHeight = options.outHeight;
        int inSampleSize = 1;

        if (srcHeight > height || srcWidth > width) {
            if (srcWidth > srcHeight) {
                inSampleSize = Math.round(srcHeight / height);
            } else {
                inSampleSize = Math.round(srcWidth / width);
            }
        }

        options.inJustDecodeBounds = false;
        options.inSampleSize = inSampleSize;

        return BitmapFactory.decodeByteArray(data, 0, data.length, options);
    }

4、Bitmap | Drawable | InputStream | Byte[ ] 之間進行轉換

  1. Drawable轉化成Bitmap
public static Bitmap drawableToBitmap(Drawable drawable) {
        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
        drawable.draw(canvas);
        return bitmap;
    }

drawable的獲取方式:Drawable drawable = getResources().getDrawable(R.drawable.ic_launcher);

  1. Bitmap轉換成Drawable
public static Drawable bitmapToDrawable(Resources resources, Bitmap bm) {
        Drawable drawable = new BitmapDrawable(resources, bm);
        return drawable;
    }
  1. Bitmap轉換成byte[]
public byte[] bitmap2Bytes(Bitmap bm) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
        return baos.toByteArray();
    }
  1. byte[]轉換成Bitmap
    Bitmap bitmap = BitmapFactory.decodeByteArray(byte, 0, b.length);
  2. InputStream轉換成Bitmap
InputStream is  = getResources().openRawResource(id);  
Bitmap bitmap = BitmaoFactory.decodeStream(is);
  1. InputStream轉換成byte[]
InputStream is = getResources().openRawResource(id);//也可以經過其餘方式接收一個InputStream對象 
ByteArrayOutputStream baos = new ByteArrayOutputStream();    
byte[] b = new byte[1024*2];    
int len = 0;    
while ((len = is.read(b, 0, b.length)) != -1)    
{    
   baos.write(b, 0, len);    
   baos.flush();    
}    
byte[] bytes = baos.toByteArray();

5、Bitmap簡單操做

  1. * 將Bitmap保存爲本地文件:*
public static void writeBitmapToFile(String filePath, Bitmap b, int quality) {
        try {
            File desFile = new File(filePath);
            FileOutputStream fos = new FileOutputStream(desFile);
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            b.compress(Bitmap.CompressFormat.JPEG, quality, bos);
            bos.flush();
            bos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  1. 圖片壓縮:
private static Bitmap compressImage(Bitmap image) {
        if (image == null) {
            return null;
        }
        ByteArrayOutputStream baos = null;
        try {
            baos = new ByteArrayOutputStream();
            image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
            byte[] bytes = baos.toByteArray();
            ByteArrayInputStream isBm = new ByteArrayInputStream(bytes);
            Bitmap bitmap = BitmapFactory.decodeStream(isBm);
            return bitmap;
        } catch (OutOfMemoryError e) {
        } finally {
            try {
                if (baos != null) {
                    baos.close();
                }
            } catch (IOException e) {
            }
        }
        return null;
    }
  1. 圖片縮放:
/** * 依據scale生成一張圖片 * * @param bitmap * @param scale 等比縮放值 * @return */
    public static Bitmap bitmapScale(Bitmap bitmap, float scale) {
        Matrix matrix = new Matrix();
        matrix.postScale(scale, scale); // 長和寬放大縮小的比例
        Bitmap resizeBmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        return resizeBmp;
    }
  1. 獲取圖片旋轉角度:
/** * 讀取照片exif信息中的旋轉角度 * * @param path 照片路徑 * @return角度 */
    private static int readPictureDegree(String path) {
        if (TextUtils.isEmpty(path)) {
            return 0;
        }
        int degree = 0;
        try {
            ExifInterface exifInterface = new ExifInterface(path);
            int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
            switch (orientation) {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    degree = 90;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    degree = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    degree = 270;
                    break;
            }
        } catch (Exception e) {
        }
        return degree;
    }
  1. 設置圖片旋轉角度
private static Bitmap rotateBitmap(Bitmap b, float rotateDegree) {
        if (b == null) {
            return null;
        }
        Matrix matrix = new Matrix();
        matrix.postRotate(rotateDegree);
        Bitmap rotaBitmap = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), matrix, true);
        return rotaBitmap;
    }
  1. 經過圖片id得到Bitmap:
    Bitmap bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
  2. 經過 assest 獲取 得到Drawable bitmap:
InputStream in = this.getAssets().open("ic_launcher");
Drawable da = Drawable.createFromStream(in, null);
Bitmap mm = BitmapFactory.decodeStream(in);
  1. 經過 sdcard 得到 bitmap
    Bitmap bit = BitmapFactory.decodeFile("/sdcard/android.jpg");
  2. * view轉Bitmap*
public static Bitmap convertViewToBitmap(View view, int bitmapWidth, int bitmapHeight){
    Bitmap bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
    view.draw(new Canvas(bitmap));
    return bitmap;
}
  1. 將控件轉換爲bitmap
public static Bitmap convertViewToBitMap(View view){
    // 打開圖像緩存
    view.setDrawingCacheEnabled(true);
    // 必須調用measure和layout方法才幹成功保存可視組件的截圖到png圖像文件
    // 測量View大小
    view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
    // 發送位置和尺寸到View及其所有的子View
    view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
    // 得到可視組件的截圖
    Bitmap bitmap = view.getDrawingCache();
    return bitmap;
}
public static Bitmap getBitmapFromView(View view){
    Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(returnedBitmap);
    Drawable bgDrawable = view.getBackground();
    if (bgDrawable != null)
        bgDrawable.draw(canvas);
    else
        canvas.drawColor(Color.WHITE);
    view.draw(canvas);
    return returnedBitmap;
}
  1. * 放大縮小圖片*
public static Bitmap zoomBitmap(Bitmap bitmap,int w,int h){  
    int width = bitmap.getWidth();  
    int height = bitmap.getHeight();  
    Matrix matrix = new Matrix();  
    float scaleWidht = ((float)w / width);  
    float scaleHeight = ((float)h / height);  
    matrix.postScale(scaleWidht, scaleHeight);  
    Bitmap newbmp = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);  
    return newbmp;  
}
  1. 得到圓角圖片的方法
public static Bitmap getRoundedCornerBitmap(Bitmap bitmap,float roundPx){  

    Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap  
            .getHeight(), Config.ARGB_8888);  
    Canvas canvas = new Canvas(output);  

    final int color = 0xff424242;  
    final Paint paint = new Paint();  
    final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());  
    final RectF rectF = new RectF(rect);  

    paint.setAntiAlias(true);  
    canvas.drawARGB(0, 0, 0, 0);  
    paint.setColor(color);  
    canvas.drawRoundRect(rectF, roundPx, roundPx, paint);  

    paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));  
    canvas.drawBitmap(bitmap, rect, rect, paint);  

    return output;  
}
  1. 對 bitmap 進行裁剪
public Bitmap  bitmapClip(Context context , int id , int x , int y){
     Bitmap map = BitmapFactory.decodeResource(context.getResources(), id);
     map = Bitmap.createBitmap(map, x, y, 120, 120);
     return map;
}

6、Bitmap的內存優化具體解釋

在Android應用裏,最耗費內存的就是圖片資源。而且在Android系統中。讀取位圖Bitmap時,分給虛擬機中的圖片的堆棧大小僅僅有8M,假設超出了。就會出現OutOfMemory異常。因此。對於圖片的內存優化,是Android應用開發中比較重要的內容。
####1. 要及時回收Bitmap的內存
Bitmap類有一個方法recycle(),從方法名可以看出意思是回收。

這裏就有疑問了。Android系統有本身的垃圾回收機制,可以不按期的回收掉不使用的內存空間。固然也包含Bitmap的空間。

那爲何還需要這種方法呢?
Bitmap類的構造方法都是私有的,因此開發人員不能直接new出一個Bitmap對象。僅僅能經過BitmapFactory類的各類靜態方法來實例化一個Bitmap。

細緻查看BitmapFactory的源碼可以看到。生成Bitmap對象終於都是經過JNI調用方式實現的。因此。載入Bitmap到內存裏之後。是包含兩部份內存區域的。簡單的說,一部分是Java部分的。一部分是C部分的。

這個Bitmap對象是由Java部分分配的。不用的時候系統就會本身主動回收了。但是那個相應的C可用的內存區域,虛擬機是不能直接回收的。這個僅僅能調用底層的功能釋放。因此需要調用recycle()方法來釋放C部分的內存。

從Bitmap類的源碼也可以看到,recycle()方法裏也的確是調用了JNI方法了的。


那假設不調用recycle(),是否就必定存在內存泄露呢?也不是的。Android的每個應用都執行在獨立的進程裏,有着獨立的內存,假設整個進程被應用自己或者系統殺死了,內存也就都被釋放掉了。固然也包含C部分的內存。
Android對於進程的管理是很複雜的。簡單的說。Android系統的進程分爲幾個級別。系統會在內存不足的狀況下殺死一些低優先級的進程。以提供給其餘進程充足的內存空間。在實際項目開發過程當中。有的開發人員會在退出程序的時候使用Process.killProcess(Process.myPid())的方式將本身的進程殺死,但是有的應用僅僅會使用調用Activity.finish()方法的方式關閉掉所有的Activity。
釋放Bitmap的演示樣例代碼片斷:

// 先推斷是否已經回收
if(bitmap != null && !bitmap.isRecycled()){ 
        // 回收而且置爲null
        bitmap.recycle(); 
        bitmap = null; 
} 
System.gc();

從上面的代碼可以看到,bitmap.recycle()方法用於回收該Bitmap所佔用的內存,接着將bitmap置空,最後使用System.gc()調用一下系統的垃圾回收器進行回收,可以通知垃圾回收器儘快進行回收。這裏需要注意的是,調用System.gc()並不能保證立刻開始進行回收過程,而僅僅是爲了加快回收的到來。
怎樣調用recycle()方法進行回收已經瞭解了。那何時釋放Bitmap的內存比較合適呢?通常來講,假設代碼已經再也不需要使用Bitmap對象了,就可以釋放了。

釋放內存之後。就不能再使用該Bitmap對象了,假設再次使用,就會拋出異常。因此必定要保證再也不使用的時候釋放。比方。假設是在某個Activity中使用Bitmap。就可以在Activity的onStop()或者onDestroy()方法中進行回收。

2.捕獲異常

爲了不應用在分配Bitmap內存的時候出現OutOfMemory異常之後Crash掉,需要特別注意實例化Bitmap部分的代碼。

一般,在實例化Bitmap的代碼中,必定要對OutOfMemory異常進行捕獲。

Bitmap bitmap = null;
try {
    // 實例化Bitmap
    bitmap = BitmapFactory.decodeFile(path);
} catch (OutOfMemoryError e) {
    //
}
if (bitmap == null) {
    // 假設實例化失敗 返回默認的Bitmap對象
    return defaultBitmapMap;
}

這裏對初始化Bitmap對象過程當中可能發生的OutOfMemory異常進行了捕獲。假設發生了OutOfMemory異常,應用不會崩潰,而是獲得了一個默認的Bitmap圖。
注意:許多開發人員會習慣性的在代碼中直接捕獲Exception。但是對於OutOfMemoryError來講,這樣作是捕獲不到的。

因爲OutOfMemoryError是一種Error,而不是Exception。在此僅僅作一下提醒,避免寫錯代碼而捕獲不到OutOfMemoryError。

3.緩存通用的Bitmap對象

有時候,可能需要在一個Activity裏屢次用到同一張圖片。比方一個Activity會展現一些用戶的頭像列表,而假設用戶沒有設置頭像的話,則會顯示一個默認頭像,而這個頭像是位於應用程序自己的資源文件裏的。
假設有類似上面的場景,就可以對同一Bitmap進行緩存。假設不進行緩存,雖然看到的是同一張圖片文件,但是使用BitmapFactory類的方法來實例化出來的Bitmap,是不一樣的Bitmap對象。緩存可以避免新建多個Bitmap對象,避免內存的浪費。
在Android應用開發過程當中。也會經常使用緩存的技術。這裏所說的緩存有兩個級別,一個是硬盤緩存,一個是內存緩存。比方說。在開發網絡應用過程當中,可以將一些從網絡上獲取的數據保存到SD卡中,下次直接從SD卡讀取,而不從網絡中讀取。從而節省網絡流量。這樣的方式就是硬盤緩存。再比方。應用程序經常會使用同一對象。也可以放到內存中緩存起來,需要的時候直接從內存中讀取。這樣的方式就是內存緩存。

4.壓縮圖片

假設圖片像素過大,使用BitmapFactory類的方法實例化Bitmap的過程當中,需要大於8M的內存空間,就必然會發生OutOfMemory異常。這個時候該怎樣處理呢?假設有這樣的狀況,則可以將圖片縮小。以下降載入圖片過程當中的內存的使用,避免異常發生。
使用BitmapFactory.Options設置inSampleSize就可以縮小圖片。屬性值inSampleSize表示縮略圖大小爲原始圖片大小的幾分之中的一個。即假設這個值爲2,則取出的縮略圖的寬和高都是原始圖片的1/2。圖片的大小就爲原始大小的1/4。
假設知道圖片的像素過大,就可以對其進行縮小。

那麼怎樣才知道圖片過大呢?
使用BitmapFactory.Options設置inJustDecodeBounds爲true後,再使用decodeFile()等方法,並不會真正的分配空間。即解碼出來的Bitmap爲null,但是可計算出原始圖片的寬度和高度。即options.outWidth和options.outHeight。

經過這兩個值,就可以知道圖片是否過大了。

BitmapFactory.Options opts = new BitmapFactory.Options();
    // 設置inJustDecodeBounds爲true
    opts.inJustDecodeBounds = true;
    // 使用decodeFile方法獲得圖片的寬和高
    BitmapFactory.decodeFile(path, opts);
    // 打印出圖片的寬和高
    Log.d("example", opts.outWidth + "," + opts.outHeight);

在實際項目中。可以利用上面的代碼。先獲取圖片真實的寬度和高度,而後推斷是否需要跑縮小。假設不需要縮小,設置inSampleSize的值爲1。假設需要縮小,則動態計算並設置inSampleSize的值。對圖片進行縮小。需要注意的是,在下次使用BitmapFactory的decodeFile()等方法實例化Bitmap對象前,別忘記將opts.inJustDecodeBound設置回false。不然獲取的bitmap對象仍是null。


注意:假設程序的圖片的來源都是程序包中的資源。或者是本身服務器上的圖片,圖片的大小是開發人員可以調整的,那麼通常來講。就僅僅需要注意使用的圖片不要過大,而且注意代碼的質量,及時回收Bitmap對象,就能避免OutOfMemory異常的發生。

假設程序的圖片來自外界。這個時候就特別需要注意OutOfMemory的發生。一個是假設載入的圖片比較大。就需要先縮小。還有一個是必定要捕獲異常,避免程序Crash。

相關文章
相關標籤/搜索