Android--從系統Camera和Gallery獲取圖片優化

前言 html

  以前有兩篇博客講解了如何從系統內已有的Camera和Gallery應用中獲取圖片的例子,看到評論裏有朋友說有時候會報錯,致使程序崩潰的 問題。本篇博客主要就這個問題分析講解一下,最後將以一個簡單的Demo演示。關於從系統內已有的Camera和Gallery應用中獲取圖片還不瞭解的 朋友,能夠先看看另外兩篇博客:Android--調用系統照相機拍照與攝像Android--從系統Gallery獲取圖片java

 

分析出錯緣由 android

  以前講到的從系統現有的Camera和Gallery應用中獲取圖片的Demo中,均直接使用系統應用返回的Uri,經過 ImageView.setImageURI(Uri)方法顯示在界面上。而對於Android設備來講,向內存中加載一張圖片,消耗的內存並不受圖片的 大小而影響,影響它的是圖片的分辨率,圖片的分辨率越大加載到內存所佔用的內存將越多。使用ImageView.setImageURI(Uri)方法將 致使了一個嚴重的錯誤,雖然ImageView直接引用圖片的Uri,它會對圖片進行一部分優化,使得它能夠正常顯示,可是這種辦法不利於Bitmap資 源的回收。因此在重複操做以後,經歷過屢次的GC,也沒有辦法回收出足夠加載圖片的內存,致使應用崩潰。 canvas

 

解決方案 app

  既然已經知道致使程序崩潰的緣由是內存溢出致使的,那麼只須要維護好Uri所表明的圖片內存便可。具體優化流程以下: ide

  一、系統中現有的Camera和Gallery應用獲取圖片返回的都是一個Uri類型的數據,它是一個內容提供者的路徑,能夠使用ContentResolver獲取它,這個之前有講過,不瞭解的朋友能夠看看另一篇博客:Android--ContentProvider。而在Context中,能夠使用getContentResolver()方法獲取到當前的內容解析者,並經過它的openInputStream()方法獲取到圖片的輸入流,經過輸入流能夠獲取到一個Bitmap對象。 優化

  二、上面提到,Android中加載圖片到內存中所佔內存的大小取決於圖片的分辨率,全部獲得Bitmap還不能直接使用它,必須對其進行優 化,以最大適應當前設備的屏幕分辯率又不會致使加載過多像素而致使內存不足的狀況。關於加載大分辨率到內存還不瞭解的朋友能夠參見另一篇博客:Android--加載大分辨率圖片到內存this

  三、獲得了優化事後的圖片還須要在使用事後進行回收,Bitmap提供了兩個方法用於判斷是否已經回收它以及強制Bitmap回收本身。如下是它們的完整簽名: .net

  •  boolean isRecycled():返回Bitmap對象是否已經被回收。
  •  void recycle():強制一個Bitmap對象回收本身。

 

優化後的Demo code

  上面講到的兩個demo,從Gallery中獲取圖片比較簡單,代碼量小,那麼就在這個基礎之上進行代碼的優化。從Gallery中獲取圖片的Uri並不直接使用,而是把它轉化爲一個Bitmap,而且優化它以達到適應屏幕分辨率的效果。


package cn.bgxt.sysgallerydemo;

import java.io.InputStream;

import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.BitmapFactory.Options;
import android.graphics.Matrix;
import android.graphics.Paint;

public class MainActivity extends Activity {
    private Button btn_getImage;
    private ImageView iv_image;
    private final static String TAG = "main";
    private WindowManager wm;
    private Bitmap bitmap;
    private Bitmap blankBitmap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 獲得應用窗口管理器
        wm = getWindowManager();
        btn_getImage = (Button) findViewById(R.id.btn_getImage);
        iv_image = (ImageView) findViewById(R.id.iv_image);

        btn_getImage.setOnClickListener(getImage);

    }

    private View.OnClickListener getImage = new OnClickListener() {

        @Override
        public void onClick(View v) {
            // 設定action和miniType
            Intent intent = new Intent();
            intent.setAction(Intent.ACTION_PICK);
            intent.setType("image/*");
            // 以須要返回值的模式開啓一個Activity
            startActivityForResult(intent, 0);
        }
    };

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // 若是獲取成功,resultCode爲-1
        Log.i(TAG, "resultCode:" + resultCode);
        if (requestCode == 0 && resultCode == -1) {
            // 獲取原圖的Uri,它是一個內容提供者的地址
            Uri uri = data.getData();
            Log.i(TAG, "uri:" + data.getData().toString());
            try {
                // 從ContentResolver中獲取到Uri的輸入流
                InputStream is = getContentResolver().openInputStream(uri);

                // 獲得屏幕的寬和高
                int windowWidth = wm.getDefaultDisplay().getWidth();
                int windowHeight = wm.getDefaultDisplay().getHeight();

                // 實例化一個Options對象
                BitmapFactory.Options opts = new BitmapFactory.Options();
                // 指定它只讀取圖片的信息而不加載整個圖片
                opts.inJustDecodeBounds = true;
                // 經過這個Options對象,從輸入流中讀取圖片的信息
                BitmapFactory.decodeStream(is, null, opts);

                // 獲得Uri地址的圖片的寬和高
                int bitmapWidth = opts.outWidth;
                int bitmapHeight = opts.outHeight;
                // 分析圖片的寬高比,用於進行優化
                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);

                // 若是沒有被系統回收,就強制回收它
                if (blankBitmap != null && !bitmap.isRecycled()) {
                    bitmap.recycle();
                }
                bitmap = BitmapFactory.decodeStream(is, null, opts);

                // 若是沒有被系統回收,就強制回收它
                if (blankBitmap != null && !blankBitmap.isRecycled()) {
                    blankBitmap.recycle();
                }
                // 在內存中建立一個能夠操做的Bitmap對象
                blankBitmap = Bitmap.createBitmap(bitmap.getWidth(),
                        bitmap.getHeight(), Bitmap.Config.ARGB_8888);
                // 爲圖片添加一個畫板
                Canvas canvas = new Canvas(blankBitmap);
                // 把讀取的圖片畫到新建立的Bitmap對象中
                canvas.drawBitmap(bitmap, new Matrix(), new Paint());
                Paint paint = new Paint();
                paint.setColor(Color.RED);
                paint.setTextSize(30);
                // 經過建立的畫筆,在Bitmap上寫入水印
                canvas.drawText("我是水印", 10, 50, paint);

                iv_image.setImageBitmap(blankBitmap);
            } catch (Exception e) {
                Toast.makeText(MainActivity.this, "獲取圖片失敗", 0).show();
            }
        }
        super.onActivityResult(requestCode, resultCode, data);
    }
}

效果展現:

 

  源碼下載

 

總結

  其實對於這兩個簡單的Demo而言,只須要針對分辨率進行優化便可,通常而言由於功能簡單,系統配置只要還過的去,都是能夠被正常GC的,可是 對於一些常常操做圖片的應用來講,仍是顯式的經過代碼的方式來管理Bitmap的內存。最後加入Canvas進行渲染水印,不是必須的,只是加了個功能而 已,直接使用bitmap對象也能夠。

相關文章
相關標籤/搜索