前段时间写了一个调用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)
@Override
public 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
<provider
android: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);
}