1. 程式人生 > >總結Android呼叫系統相機拍照遇到的坑

總結Android呼叫系統相機拍照遇到的坑

拍照功能在應用開發中幾乎已成為標配,例如使用者通過拍照上傳頭像。實現拍照功能的方式有兩種。第一種是使用相機API(即Camera類)來自定義拍照,第二種是使用Intent呼叫系統相機來拍照。其中最常見的方式是直接呼叫系統相機拍照來獲取照片。

呼叫系統相機獲取照片的流程如下,這裡省去了宣告許可權和動態許可權申請:
private String mFilePath;// 需要初始化,路徑自定義
private static final int REQUEST_CODE_CAMERA = 1;

// 啟動系統相機
private void startCamareActivity() {
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.parse("file://" + mFilePath));
    startActivityForResult(intent, REQUEST_CODE_CAMERA);
}

// 拿到照片
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_CODE_CAMERA && resultCode == RESULT_OK) {
        // mFilePath即是拍照完成後的圖片
	// 這裡可以進入裁剪頁面了
    }
}
通過Action為MediaStore.ACTION_IMAGE_CAPTURE的隱式Intent啟動拍照,使用MediaStore.EXTRA_OUTPUT指定照片路徑,成功返回後即可獲取到照片的完整路徑。

下面進入填坑階段。

一.拍攝的照片被旋轉了

明明在拍攝時照片是正的,進入裁剪頁面發現照片竟然是被旋轉的。這個現象在不同的裝置上,表現會不一樣,可能正常也可能被旋轉,而且旋轉的角度也不同。要解決這個問題,需要首選獲取照片的旋轉角度,再反轉回去就可以了。

ExifInterface介面提供了多媒體檔案比如JPG格式圖片的一些附加資訊,比如檔案的旋轉,gps,拍攝時間等。如下程式碼展示了使用ExifInterface獲取圖片的旋轉角度。
/**
 * 獲取圖片的旋轉角度
 * @param path 圖片的絕對路徑
 */
private int getBitmapDegree(String imagePath) {
    int degree = 0;
    try {
        // 從指定路徑下讀取圖片,並獲取其EXIF資訊
        ExifInterface exifInterface = new ExifInterface(imagePath);
        // 獲取圖片的旋轉資訊
        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 (IOException e) {
        e.printStackTrace();
    }
    return degree;
}

進行圖形變換如旋轉、縮放、移動的操作,可以使用Matrix類來完成。如下程式碼展示了使用Matrix對圖片旋轉,生成新的Bitmap。
/**
 * 將圖片按照某個角度進行旋轉
 * @param bitmap 需要旋轉的圖片
 * @param degree 旋轉角度
 */
public static Bitmap rotateBitmapByDegree(Bitmap bitmap, int degree) {
    Bitmap result = null;
    // 根據旋轉角度,生成旋轉矩陣
    Matrix matrix = new Matrix();
    matrix.postRotate(degree);
    try {
        // 將原始圖片按照旋轉矩陣進行旋轉,並得到新的圖片
        result = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    } catch (Exception e) {
    }
    if (bitmap != null && !bitmap.isRecycled()) {
        bitmap.recycle();
	bitmap = null;
    }
    return result;
}

二.拍照完成後應用閃退

這個現象相對少見,目前在廉價的低端手機上出現過。啟動拍照時正常,當在拍照介面點選確認按鈕,拍照介面消失返回我們的應用時,直接閃退。更讓人崩潰的是,Logcat裡面沒有相應的日誌資訊。無奈只能藉助搜尋,發現原來是手機廠商對系統做了修改(為了在低端硬體上能夠執行Android系統...),當我們的應用程式的Activity啟動拍照,進入系統相機時,我們的Activity被銷燬了。這個情況在測試時也不是必現。測試機在使用一段時間後很容易出現,但如果將裝置重啟後開啟我們的應用來拍照又沒有問題(這是我們遇到的情況,可能不是必現規則)。

Activity被回收時儲存資料,可以在onSaveInstanceState()生命週期方法中處理,將圖片路徑儲存到Bundle中。
@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putString("file_path", mFilePath);
    super.onSaveInstanceState(outState);
}

然後在onCreate()方法裡,判斷savedInstanceState不為空時,取出圖片路徑的值。
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    if (savedInstanceState != null) {
        mFilePath = savedInstanceState.getString("file_path");
	// your code here
    }
}

網上看到另外一種恢復的方法。Activity沒有重新建立,而是成員變數被回收了,當拍照返回時,在onActivityResult()方法中mFilePath為空。解決方法是從onRestoreInstanceState()方法恢復資料。這種情況暫時沒遇到,做個記錄。
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    if (TextUtils.isEmpty(mFilePath)) {
        mFilePath = savedInstanceState.getString("file_path");
    }
    super.onRestoreInstanceState(savedInstanceState);
}