1. 程式人生 > >Android 7.0使用相機拍照,出現FileUriExposedException異常

Android 7.0使用相機拍照,出現FileUriExposedException異常

以前寫的相機模組功能,在7.0手機上遇到錯誤,記錄一下。

Android 7.0以下 呼叫方式

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
 + "/test/" + System.currentTimeMillis() + ".jpg");
file.getParentFile().mkdirs();
Uri uri = Uri.fromFile(file);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
7.0以後再使用上面的方式會出現FileUriExposedException異常錯誤。

Android 7.0以上呼叫方式

使用FileProvider來解決,FileProvider是ContentProvider的一個子類。

1.res目錄新建xml資料夾,並新建xml\file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="image_file"
        path="Android/data/你的包名/files/Pictures"/>
</paths>
  • <files-path/>代表的根目錄: Context.getFilesDir()
  • <external-path/>代表的根目錄: Environment.getExternalStorageDirectory()
  • <cache-path/>代表的根目錄: getCacheDir()

2.註冊FileProvider

 <!-- 自定義URI 需要 provider -->
 <provider
   android:name="android.support.v4.content.FileProvider"
   android:authorities="你的包名.fileprovider"
   android:exported="false"
   android:grantUriPermissions="true">
   <meta-data
     android:name="android.support.FILE_PROVIDER_PATHS"
     android:resource="@xml/file_paths">
   </meta-data>
 </provider>
3. 詳細程式碼
/**
 * 拍照獲取圖片
 **/
public void take_photo() {
    //建立File物件,用於儲存拍照後的圖片
    File outputImage = null;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
         //external-path == Environment.getExternalStorageDirectory()
        outputImage = new File(Environment.getExternalStorageDirectory(), "output_image.jpg");
        try {
            if (outputImage.exists()) {
                outputImage.delete();
            }
            outputImage.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        uri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", outputImage);
    } else {
        outputImage = new File(getExternalCacheDir(), "output_image.jpg");
        try {
            if (outputImage.exists()) {
                outputImage.delete();
            }
            outputImage.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        uri = Uri.fromFile(outputImage);
    }
    //啟動相機程式
    Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
    intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
    startActivityForResult(intent, Constant.REQUEST_CODE_CAPTURE_CAMEIA);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == Constant.REQUEST_CODE_CAPTURE_CAMEIA && resultCode == RESULT_OK && uri != null) {
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
              //建立File物件,用於儲存拍照後的圖片
              File outputImage = new File(Environment.getExternalStorageDirectory(), "output_image.jpg");
              filePath = outputImage.getAbsolutePath();
          }else{
              filePath = FileUtils.getRealFilePath(this, uri);
          }
              ImageUtils.getInstance().uploadImageToQiniu(filePath,QINIU_TOKEN,this);
    }
}

getRealFilePath的方法
public static String getRealFilePath(final Context context, final Uri uri) {
    if (null == uri) return null;
    final String scheme = uri.getScheme();
    String data = null;
    if (scheme == null)
        data = uri.getPath();
    else if (ContentResolver.SCHEME_FILE.equals(scheme)) {
        data = uri.getPath();
    } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
        Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.ImageColumns.DATA}, null, null, null);
        if (null != cursor) {
            if (cursor.moveToFirst()) {
                int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
                if (index > -1) {
                    data = cursor.getString(index);
                }
            }
            cursor.close();
        }
    }
    return data;
}