前言
众所周知Android7.0提高了安全防护,不允许应用内部Uri暴露给外部,因此引入了FileProvider。有关FileProvider的使用和配置,这里就不赘述了。我这里出现的问题是选择图片在小米手机出现crash,拍照图片无法加载,而其他手机则正常。
- 使用FileProvider构建的Uri是以
Content://
开头的,而使用Uri.fromFile()创建的是以file:///
开头的;
代码出现bug
图片裁剪代码如下:
private boolean corp(Activity activity, Uri uri) {
Intent cropIntent = new Intent("com.android.camera.action.CROP");
cropIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
cropIntent.setDataAndType(uri, "image/*");
cropIntent.putExtra("crop", "true");
cropIntent.putExtra("aspectX", 1);
cropIntent.putExtra("aspectY", 1);
cropIntent.putExtra("outputX", 200);
cropIntent.putExtra("outputY", 200);
cropIntent.putExtra("noFaceDetection", true);
cropIntent.putExtra("return-data", false);
cropIntent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
File dir = new File(Environment.getExternalStorageDirectory().getPath() , "avatar_image");
if (!dir.exists() && dir.mkdirs()) ;
File fileCrop = new File(dir, CROP_FILE_NAME);
if (!fileCrop.exists()) {
try {
fileCrop.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
cropImageUri = Uri.fromFile(fileCrop);
cropIntent.putExtra(MediaStore.EXTRA_OUTPUT, cropImageUri);
activity.startActivityForResult(cropIntent, INTENT_CROP);
}
- 选择图片报错
Caused by: java.lang.SecurityException: Uid 10797 does not have permission to uri 0 @ content://com.miui.gallery.open/raw/...20180921_202403.jpg
这里的Uri已经是Gallery产生的了,为什么还是报错呢?
看有帖子说是需要去掉intent.addFlag
即可。
- 于是我照做了,但是Camera拍照又出现问题了:弹出toast
无法加载此图片
思考为什么:
Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION
这个flag为Intent赋予了临时权限,允许读写传入的uri,也就是被裁剪的图片文件;
- 拍照时Uri是FileProvider生成的:
content://com.xxx.packagename/avatar_image/photo_file.jpg
,是应用内部文件,对外开放需要临时权限; - 选择图片Uri是:
content://com.miui.gallery.open/raw/...
,是小米相册ContentProvider生成的,因此对所有应用开放,不需要临时权限。 - 裁剪是该应用调用系统裁剪接口,是从本应用去到外部应用;
因此这里的Uri需要区别对待:
private boolean corp(Activity activity, Uri uri, boolean fromCamera) {
Intent cropIntent = new Intent("com.android.camera.action.CROP");
if (fromCamera){
cropIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
...
至此,该问题解决
总结:
- 如果是App内部Uri,需要通过FileProvider构建和暴露给外部,并添加临时权限;如果不是App内部的,则不必添加临时权限;关键:明确资源来源,和资源去向;
- Android不断提升的安全和权限限制,例如 Android 9.0建议使用HTTPS访问,不然会抛出异常
java.net.UnknownServiceException: CLEARTEXT communication ** not permitted by network security policy
;因此安卓为开发者提出了更多更高要求,需要开发者不断提高安全意识,也令应用开发不断规范化,为安卓点赞!