1、在Android7.0上调用系统相机拍照,裁切照片的适配

在Android7.0以前,若是你想调用系统相机拍照能够经过如下代码来进行:java
File file = new File(Environment.getExternalStorageDirectory(), "/pic/" + System.currentTimeMillis() + ".jpg");
if (!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
Uri imageUri = Uri.fromFile(file);
Intent intent = new Intent();
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);//设置Action为拍照
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//将拍取的照片保存到指定URI
startActivityForResult(intent, 1);
在Android7.0上使用上述方式调用系统相拍照会抛出以下异常:android
android.os.FileUriExposedException: file:storage/emulated/0/temp/1474956193735.jpg exposed beyond app through Intent.getData()
at android.os.StrictMode.onFileUriExposed(StrictMode.java:1799)
at android.net.Uri.checkFileUriExposed(Uri.java:2346)
at android.content.Intent.prepareToLeaveProcess(Intent.java:8933)
at android.content.Intent.prepareToLeaveProcess(Intent.java:8894)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1517)
at android.app.Activity.startActivityForResult(Activity.java:4223)
at android.app.Activity.startActivityForResult(Activity.java:4182)
这是因为Android7.0执行了“StrictMode API 政策禁”的缘由,不过能够用FileProvider来解决这一问题,
如今咱们就来一步一步的解决这个问题。web
使用FileProvidertomcat
一、在manifest清单文件中注册provider安全
android:authorities="com.bj.bs.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
android:resource="@xml/file_paths" />
心得:exported:要求必须为false,为true则会报安全异常。grantUriPermissions:true,表示授予 URI 临时访问权限。服务器
二、指定共享的目录app
为了指定共享的目录咱们须要在资源(res)目录下建立一个xml目录,而后建立一个名为“file_paths”(名字能够随便起,只要和在manifest注册的provider所引用的resource保持一致便可)的资源文件,内容以下:ide
files-path 表明的根目录: Context.getFilesDir()
external-path 表明的根目录: Environment.getExternalStorageDirectory()
cache-path 表明的根目录: getCacheDir()svg
心得:上述代码中path=”“,是有特殊意义的,它代码根目录,也就是说你能够向其它的应用共享根目录及其子目录下任何一个文件了,若是你将path设为path=”pictures”,
那么它表明着根目录下的pictures目录(eg:/storage/emulated/0/pictures),若是你向其它应用分享pictures目录范围以外的文件是不行的。网站
三、使用FileProvider
上述准备工做作完以后,如今咱们就能够使用FileProvider了。
仍是以调用系统相机拍照为例,咱们须要将上述拍照代码修改成以下:
File file=new File(Environment.getExternalStorageDirectory(), "/temp/"+System.currentTimeMillis() + ".jpg");
if (!file.getParentFile().exists())file.getParentFile().mkdirs();
Uri imageUri = FileProvider.getUriForFile(context, "com.bj.bs.fileprovider", file); // 经过FileProvider建立一个content类型的Uri
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //添加这一句表示对目标应用临时受权该Uri所表明的文件
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);//设置Action为拍照
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//将拍取的照片保存到指定URI
startActivityForResult(intent, 1);
上述代码中主要有两处改变:
将以前Uri的scheme类型为file的Uri改为了有FileProvider建立一个content类型的Uri。
添加了intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);来对目标应用临时受权该Uri所表明的文件。
心得:上述代码经过FileProvider的Uri getUriForFile (Context context, String authority, File file)
静态方法来获取Uri,该方法中authority参数就是清单文件中注册provider的android:authorities=”com.jph.takephoto.fileprovider”。
对Web服务器如tomcat,IIS比较熟悉的小伙伴,都只知道为了网站内容的安全和高效,Web服务器都支持为网站内容设置一个虚拟目录,其实FileProvider也有殊途同归之处。
将getUriForFile方法获取的Uri打印出来以下:
content://com.bj.bs.fileprovider/camera_photos/temp/1474960080319.jpg`。
其中camera_photos就是file_paths.xml中paths的name。
由于上述指定的path为path=”“,因此content://com.bj.bs.fileprovider/camera_photos/表明的真实路径就是根目录,即:/storage/emulated/0/。
content://com.bj.bs.fileprovider/camera_photos/temp/1474960080319.jpg表明的真实路径是:/storage/emulated/0/temp/1474960080319.jpg。
裁切照片
在Android7.0以前,你能够经过以下方法来裁切照片:
File file=new File(Environment.getExternalStorageDirectory(), "/temp/"+System.currentTimeMillis() + ".jpg");
if (!file.getParentFile().exists())file.getParentFile().mkdirs();
Uri outputUri = Uri.fromFile(file);
Uri imageUri=Uri.fromFile(new File("/storage/emulated/0/temp/1474960080319.jpg"));
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(imageUri, "image/*");
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("scale", true);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("noFaceDetection", true); // no face detection
startActivityForResult(intent, 2);
和拍照同样,上述代码在Android7.0上一样会引发android.os.FileUriExposedException异常,解决办法就是上文说说的使用FileProvider。
而后,将上述代码改成以下便可:
File file=new File(Environment.getExternalStorageDirectory(), "/temp/"+System.currentTimeMillis() + ".jpg");
if (!file.getParentFile().exists())file.getParentFile().mkdirs();
Uri outputUri = FileProvider.getUriForFile(context, "com.jph.takephoto.fileprovider",file);
Uri imageUri=FileProvider.getUriForFile(context, "com.jph.takephoto.fileprovider", new File("/storage/emulated/0/temp/1474960080319.jpg");//经过FileProvider建立一个content类型的Uri
Intent intent = new Intent("com.android.camera.action.CROP");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(imageUri, "image/*");
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("scale", true);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("noFaceDetection", true); // no face detection
startActivityForResult(intent, 2);
源码