前言
前段时间写了一个调用Camera拍照,并从图库中选取图片的小程序,但是当它在7.0的系统上运行时,直接崩溃,根本不能使用。
原来Android5.0、6.0、7.0增加了很多特性,我并没有对此进行适配,从而导致了很多错误。
android.os.FileUriExposedException:
file:///storage/emulated/0/camera/1513393885728.jgp
exposed beyond app through ClipData.Item.getUri()
java.lang.SecurityException: Permission Denial:
reading android.support.v4.content.FileProvider uri content://com.zxl.test_picture_camera/camera_gallery/camera/1514101205911.jgp
from pid=5847, uid=10048 requires the provider be exported, or grantUriPermission()
java.lang.SecurityException:
Permission Denial: writing android.support.v4.content.FileProvider uri content://com.zxl.test_picture_camera/camera_gallery/camera/1514101317846.jgp
from pid=8336, uid=10048 requires the provider be exported, or grantUriPermission()
-权限校验
-权限申请
-赋予第三方应用Uri权限
-打开照相机
-打开相册
-裁剪图片
public static boolean checkPermission(Context context, String permissionStr){ boolean result = true; if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){ result = context.checkSelfPermission(permissionStr) == PackageManager.PERMISSION_GRANTED; }else{ result = PermissionChecker.checkSelfPermission(context,permissionStr) == PackageManager.PERMISSION_GRANTED; } return result;}
Manifest.permission.CAMERA
public static boolean checkCameraPermission(Context context){ return checkPermission(context, Manifest.permission.CAMERA);}
Manifest.permission.READ_EXTERNAL_STORAGE
Manifest.permission.WRITE_EXTERNAL_STORAGE
public static boolean checkSDCardPermission(Context context){ return checkPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) && checkPermission(context,Manifest.permission.WRITE_EXTERNAL_STORAGE);}
AndroidManifest.xml添加权限:
android.permission.CAMERA
android.permission.READ_EXTERNAL_STORAGE
android.permission.WRITE_EXTERNAL_STORAGE
在Android6.0以后有的权限需要主动去申请
requestPermissions
@RequiresApi(api = Build.VERSION_CODES.M)public static void requestPermission(Activity activity, String[] permissionStrs, int requestCode){ activity.requestPermissions(permissionStrs,requestCode);}
requestPermissions Manifest.permission.CAMERA
@RequiresApi(api = Build.VERSION_CODES.M)public static void requestCameraPermission(Activity activity, int requestCode){ requestPermission(activity,new String[]{Manifest.permission.CAMERA},requestCode);}
requestPermissions Manifest.permission.READ_EXTERNAL_STORAGE
Manifest.permission.WRITE_EXTERNAL_STORAGE
@RequiresApi(api = Build.VERSION_CODES.M)public static void requestSDCardPermission(Activity activity, int requestCode){ requestPermission(activity,new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE},requestCode);}
通过Activity回调接口获取结果
onRequestPermissionsResult
@RequiresApi(api = Build.VERSION_CODES.M)@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults);}
PackageManager.PERMISSION_GRANTED
public static boolean checkRequestPermissionsResult(int[] grantResults){ if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ return true; } return false;}
当获取到图片后,需要进行裁剪,这需要将原图片、裁剪后到图片的保存路径都要给裁剪应用,并且要封装成Uri通过Intent传递,在Android7.0增加了第三方应用读取Uri的权限校验
在res/xml下新建资源文件file_path.xml
files-path与Context.getFilesDir()相同的目录
external-path与Environment.getExternalStorageDirectory()相同的目录
cache-path与getCacheDir()相同的目录
name为别名
path为表示对应类型的根目录
<resources> <paths> <files-path name="camera_gallery" path=""/> <external-path name="camera_gallery" path=""/> <cache-path name="camera_gallery" path=""/> paths> resources>
AndroidManifest.xml添加provider
<providerandroid:authorities="com.zxl.test_picture_camera"android:name="android.support.v4.content.FileProvider"android:exported="false"android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_path"/>provider>
FileProvider.getUriForFile
private static Uri getFileUri(Context context,String filePath){ Uri mUri = null; File mFile = new File(filePath); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){ mUri = FileProvider.getUriForFile(context,"com.zxl.test_picture_camera",mFile); }else{ mUri = Uri.fromFile(mFile); } return mUri;}
Intent.FLAG_GRANT_READ_URI_PERMISSION
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){ mIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); }
通过Intent找到符合的要打开的Activity
activity.grantUriPermission
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
List resInfoList = queryActivityByIntent(activity,mIntent);if (resInfoList.size() == 0) { showMsg(activity, "没有合适的应用程序"); return;}Iterator resInfoIterator = resInfoList.iterator();while (resInfoIterator.hasNext()) { ResolveInfo resolveInfo = (ResolveInfo) resInfoIterator.next(); String packageName = resolveInfo.activityInfo.packageName; activity.grantUriPermission(packageName, desUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);}private static List<ResolveInfo> queryActivityByIntent(Activity activity, Intent intent){ List resInfoList = activity.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); return resInfoList;}
public static void startCamera(Activity activity,int requestCode,String filePath){ if(hasSdcard()){ Intent mOpenCameraIntent = new Intent(); mOpenCameraIntent.setAction(MediaStore.ACTION_IMAGE_CAPTURE); Uri desUri = getFileUri(activity,filePath); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){ //已申请camera权限 //mOpenCameraIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } mOpenCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT,desUri); activity.startActivityForResult(mOpenCameraIntent,requestCode); }else{ showMsg(activity,"设备没有SD卡!"); }}
public static void startGallery(Activity activity,int requestCode){ if(hasSdcard()){ Intent mOpenGalleryIntent = new Intent(Intent.ACTION_GET_CONTENT); mOpenGalleryIntent.setType("image/*"); activity.startActivityForResult(mOpenGalleryIntent,requestCode); }else{ showMsg(activity,"设备没有SD卡!"); }}
public static void startCropImage(Activity activity, Uri originUri, Uri desUri, int aspectX, int aspectY, int outputX, int outputY, int requestCode){ Intent mIntent = new Intent(); mIntent.setAction("com.android.camera.action.CROP"); mIntent.setDataAndType(originUri,"image/*"); List resInfoList = queryActivityByIntent(activity,mIntent); if (resInfoList.size() == 0) { showMsg(activity, "没有合适的应用程序"); return; } Iterator resInfoIterator = resInfoList.iterator(); while (resInfoIterator.hasNext()) { ResolveInfo resolveInfo = (ResolveInfo) resInfoIterator.next(); String packageName = resolveInfo.activityInfo.packageName; activity.grantUriPermission(packageName, desUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); } if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){ mIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } mIntent.putExtra("crop","true"); mIntent.putExtra("aspectX",aspectX); mIntent.putExtra("aspectY",aspectY); mIntent.putExtra("outputX",outputX); mIntent.putExtra("outputY",outputY); mIntent.putExtra("scale",true); mIntent.putExtra(MediaStore.EXTRA_OUTPUT, desUri); mIntent.putExtra("return-data",false); mIntent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); mIntent.putExtra("noFaceDetection",true); activity.startActivityForResult(mIntent,requestCode);}