Android 7.0系統發布后,拿到能升級的nexus 6P,就開始了7.0的適配。發現Android7.0在修改頭像時候進行拍照并裁剪圖片時會出現photos app崩潰。仔細分析操作步驟和流程,發現照片拍照是成功的,SD卡也能保存相關的圖片信息,但是在對拍照的圖片進行裁剪時候出現了photos app崩潰;如下圖:
同時發現通過選擇相冊進行選中圖片后才進行裁剪就沒有問題。看一下代碼:
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(Uri.fromFile(inputfile), IMAGE_UNSPECIFIED);//主要問題就在這個File Uri上面 ————代碼語句A
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", 180);
intent.putExtra("outputY", 180);
intent.putExtra("scale", true);
intent.putExtra("return-data", false);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(outputfile));//定義輸出的File Uri,之后根據這個Uri去拿裁剪好的圖片信息 ————代碼B
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("noFaceDetection", true);
startActivityForResult(intent, RequestCode);
上網查找了一些資料都是說Android7.0后,發現Android7.0在這個方面最大變化就在于以前適用的File Uri需要更改為Content Uri,詳情可以看這里。但是這個更改是需要編譯的targetSdkVersion是24的時候才有效,我們的apptargetSdkVersion是23。所以問題上面的鏈接解決辦法應該不是這個原因(這個解決在后文解決targetSdkVersion=24的時候需要用到);
但是從文章中知道了File Uri和Content Uri的區別,然后再去分析一下為什么從相冊選擇圖片進行裁剪是生效的?通過代碼分析和debug后發現,從相冊選取圖片得到的Uri是Content Uri而拍照后使用的是文件路徑生成的File Uri,看來問題就是出在這里,并不是說我們app的targetSDKVersion不是24就可以使用File Uri,但是photos app的targetSdkVersion可能是24導致了它接受了File Uri而崩潰,那么我們需要做的就是把File Uri換成Content Uri。這里需要提的是,直接按照這里的做法去更換Content Uri并不能生效,會提示“Can not edit image under 50*50 pixels”的錯誤toast提示,其實是photos app找不到Content Uri傳進去的圖片文件。那么我們需要換一種方式去更換Content Uri,我們在stackoverflow上面找到更換Content Uri的方法,需要注意的是不是所有的File Uri都可以轉換成Content Uri,應該是多媒體相關的文件才可以。下面是代碼:
public static Uri getImageContentUri(Context context, File imageFile) {
String filePath = imageFile.getAbsolutePath();
Cursor cursor = context.getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[] { MediaStore.Images.Media._ID },
MediaStore.Images.Media.DATA + "=? ",
new String[] { filePath }, null);
if (cursor != null && cursor.moveToFirst()) {
int id = cursor.getInt(cursor
.getColumnIndex(MediaStore.MediaColumns._ID));
Uri baseUri = Uri.parse("content://media/external/images/media");
return Uri.withAppendedPath(baseUri, "" + id);
} else {
if (imageFile.exists()) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DATA, filePath);
return context.getContentResolver().insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
} else {
return null;
}
}
}
我們需要把上文第一處代碼的Uri.fromFile(inputfile)更換成getImageContentUri生成的Uri。替換上去后運行程序試試,這樣修改就可以了。
更進一步:要是我們把上文的代碼,把app的targetSdkVersion改成24之后,在啟動相機進行拍照時候,會發現這樣的錯誤
android.os.FileUriExposedException: file:///storage/emulated/0/DCIM/Camera/TEMP_IMAGE1474468182889.jpg exposed beyond app through ClipData.Item.getUri();
不能拍照成功;這個時候我們就要用上文介紹的文章file:// scheme is now not allowed to be attached with Intent on targetSdkVersion 24 (Android Nougat).使用FileProvider來產生Content Uri代替File Uri,按照上面網址介紹方法替換掉就可以;
然而我們在用FileProvider.getUriForFile替換掉所有的Uri.fromFile時候,可以拍照成功了,但是在剪裁圖片時候還是會出現之前的“Can not edit image under 50*50 pixels”沒有辦法,只能把上文的代碼語句A重新更改為getImageContentUri生成Content Uri,重新運行程序;這個時候可以拍照成功,進入圖片裁剪photos app里面,但是裁剪完成Save的時候photos app又崩潰了:
應該是FileProvider的屬性為android:exported="false"的原因,但是這里不能改為true(會報另一個錯誤:java.lang.RuntimeException: Unable to get provider android.support.v4.content.FileProvider: java.lang.SecurityException: Provider must not be exported)。
這個時候我們需要把代碼語句B里面outputUri改為File Uri就可以了,最終的代碼如下,調用相機拍照的代碼自己替換成FileProvider就可以了:
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(getImageContentUri(context , inputfile), IMAGE_UNSPECIFIED);//自己使用Content Uri替換File Uri
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", 180);
intent.putExtra("outputY", 180);
intent.putExtra("scale", true);
intent.putExtra("return-data", false);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(outputfile));//定義輸出的File Uri
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("noFaceDetection", true);
startActivityForResult(intent, RequestCode);
綜上,我們完成了targetSdkVersion=24和小于24兩種情況的圖片裁剪適配;之后還是采用自己app內程序進行圖片裁剪適配性比較好。