標籤:
Bitmap最佳化
一個進程的記憶體可以由2個部分組成:native和dalvik,dalvik就是我們平常說的java堆,我們建立的對象是在這裡面分配的,而bitmap是直 接在native上分配的。
一旦記憶體配置給Java後,以後這塊記憶體即使釋放後,也只能給Java使用,所以如果Java突然佔用了一個大塊記憶體,即使很快釋放了,C能用的記憶體也是16M減去Java最大佔用的記憶體數。而Bitmap的產生是通過malloc進行記憶體配置的,佔用的是C的記憶體,這個也就說明了,假設13M記憶體被Java用過後,剩下C能用的只有3M了,所以如果有一個4M的Bitmap那肯定無法產生了。
在Android應用裡,最耗費記憶體的就是圖片資源。而且在Android系統中,讀取位元影像Bitmap時,分給虛擬機器中的圖片的堆棧大小隻有8M,如果超 出了,就會出現OutOfMemory異常。
及時回收Bitmap的記憶體
// 先判斷是否已經回收if(bitmap != null && !bitmap.isRecycled()){ // 回收並且置為null bitmap.recycle(); bitmap = null;}System.gc();
捕獲異常
在執行個體化Bitmap的代碼中,一定要對OutOfMemory異常進行捕獲。下面對初始化Bitmap對象過程中可能發生的OutOfMemory異常進行了捕獲。如果發生了異常,應用不會崩潰,而是得到了一個預設的圖片。
Bitmap bitmap = null;try { // 執行個體化Bitmap bitmap = BitmapFactory.decodeFile(path);} catch (OutOfMemoryError e) {//}if (bitmap == null) { // 如果執行個體化失敗 返回預設的Bitmap對象 return defaultBitmapMap;}
緩衝通用的Bitmap對象
壓縮圖片
如果圖片像素過大可以將圖片縮小,以減少載入圖片過程中的記憶體的使用,避免異常發生。
使用BitmapFactory.Options.inSampleSize就可以縮小圖片。屬性值inSampleSize表示縮圖大小為原始圖片大小的幾分之一。即如果這個值為2,則取出的縮圖的寬和高都是原始圖片的1/2,圖片的大小就為原始大小的1/4。
如果知道圖片的像素過大,就可以對其進行縮小。那麼如何才知道圖片過大呢?
使用BitmapFactory.Options設定inJustDecodeBounds為true後,並不會真正的分配空間,即解碼出來的Bitmap為null,但是可計算出原始圖片的寬度和高度,即options.outWidth和options.outHeight。通過這兩個值,就可以知道圖片是否過大了。
BitmapFactory.Options opts = new BitmapFactory.Options();// 設定inJustDecodeBounds為trueopts.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。
以從Gallery擷取一個圖片為例講解縮放:
public class MainActivity extends Activity { private ImageView iv; private WindowManager wm; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); wm = getWindowManager(); iv = (ImageView) findViewById(R.id.iv); } // 從系統的圖庫裡面 擷取一張照片 public void click(View view) { Intent intent = new Intent(); intent.setAction("android.intent.action.PICK"); intent.addCategory("android.intent.category.DEFAULT"); intent.setType("image/*"); startActivityForResult(intent, 0); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (data != null) { // 擷取到系統圖庫返回回來圖片的uri Uri uri = data.getData(); System.out.println(uri.toString()); try { InputStream is = getContentResolver().openInputStream(uri); // 1.計算出來螢幕的寬高. int windowWidth = wm.getDefaultDisplay().getWidth(); int windowHeight = wm.getDefaultDisplay().getHeight(); //2. 計算圖片的寬高. // 2.計算出來圖片的寬高. BitmapFactory.Options opts = new Options(); // 設定 不去真正的解析位元影像 不把他載入到記憶體 只是擷取這個圖片的寬高資訊 opts.inJustDecodeBounds = true; BitmapFactory.decodeStream(is, null, opts); int bitmapHeight = opts.outHeight; int bitmapWidth = opts.outWidth; if (bitmapHeight > windowHeight || bitmapWidth > windowWidth) { int scaleX = bitmapWidth/windowWidth; int scaleY = bitmapHeight/windowHeight; if(scaleX>scaleY){//按照水平方向的比例縮放 opts.inSampleSize = scaleX; }else{//按照豎直方向的比例縮放 opts.inSampleSize = scaleY; } }else{//如果圖片比手機螢幕小 不去縮放了. opts.inSampleSize = 1; } //讓位元影像工廠真正的去解析圖片 opts.inJustDecodeBounds = false; //注意: 流的操作 is = getContentResolver().openInputStream(uri); Bitmap bitmap = BitmapFactory.decodeStream(is, null, opts); iv.setImageBitmap(bitmap); } catch (Exception e) { e.printStackTrace(); } } super.onActivityResult(requestCode, resultCode, data); }}
歡迎androider關注公眾號:愛安卓
【Android】Bitmap最佳化