以前调用系统相机拍照的时候,流程是这样的
private void takePhoto() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
//创建一个路径保存图片
photoFile = ImageUtil.createImageFile();
if (photoFile != null) {
photoURI = Uri.fromFile(photoFile);
//传递一个Uri
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, TAKE_PHOTO);
}
}
}
然后在onActivityResult方法中处理拍照结果。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case TAKE_PHOTO:
if (resultCode == RESULT_OK) {
//处理拍照的结果
processTakePhoto(photoFile.getPath());
}
break;
default:
break;
}
}
android.os.FileUriExposedException:
file:///storage/emulated/0/Android/data/com.hm.camerademo/files/Picture
s/20170225_140305187933259.jpg exposed beyond app through
ClipData.Item.getUri()
然后网上搜了一把,是 photoURI = Uri.fromFile(photoFile); 这种创建Uri的方式有问题了,不够安全。需要使用FileProvider来创建Uri.
###使用FileProvider四部曲
第一步,指定一个FileProvider。在AndroidManifest.xml中声明一个条目
...
com.example.myapp是你的包名
第二步,指定想分享的目录。在res目录下新建一个xml目录,在xml目录下面新建一个xml文件。我新建的文件名叫filepaths.xml
name=“name” URI 路径段,取值会隐藏你分享的目录的名字。比如下面这个
会用hm_file 替代/storage/emulated/0/Android/data/com.hm.camerademo/cache/images
path=“path” 你分享的目录的名字
###注意
java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/DCIM/camerademo/20170226_110056248725175.jpg错误产生原因。
我可以在 external-path目录下指定多个我想分享的目录,两个分享的目录的name取值不应该相同。我把上面两个的name字段都叫 hm_file,然后看看有什么问题。结果就是会报标题上的那个错误,实验一把
然后我生成一个Content URI。
File imageFile = null;
String storagePath;
File storageDir;
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
try {
//文件路径是公共的DCIM目录下的/camerademo目录
storagePath = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
.getAbsolutePath() + File.separator + "camerademo";
storageDir = new File(storagePath);
storageDir.mkdirs();
imageFile = File.createTempFile(timeStamp, ".jpg", storageDir);
Log.e(TAG, imageFile.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
return imageFile;
//文件路径
/storage/emulated/0/DCIM/camerademo/20170226_110056248725175.jpg
//生成Uri
photoFile = ImageUtil.createImageFile();
photoURI = FileProvider.getUriForFile(this, "com.hm.camerademo.fileprovider", photoFile);
但是报错了。错误如下
java.lang.IllegalArgumentException: Failed to find configured root
that contains
/storage/emulated/0/DCIM/camerademo/20170226_110056248725175.jpg
我把上面生成文件的路径改一下
//路径是公共存储路径Pictures目录下的camerademo目录
storagePath = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
.getAbsolutePath() + File.separator + "camerademo";
//生成的文件路径
/storage/emulated/0/Pictures/camerademo/20170226_1104551680202685.jpg
//可以正常生成Uri的路径
/hm_file/20170226_1104551680202685.jpg
上面的问题说明 在filePath.xml 文件中,如果要在同一个存储路径下,指定两个共享的目录,如下所示,那么两个共享路径的name字段取值不应该相同,如果两者相同,那么后面的一行指定的path(/storage/emulated/0/Pictures/camerademo)会覆盖上面一行指定的path(/storage/emulated/0/DCIM/camerademo)
// 共享目录的根目录都是 /storage/emulated/0/
###第三步 为一个文件生成 Content URI
File imageFile = null;
String storagePath;
File storageDir;
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
try {
storagePath = App.getInstance().getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath();
storageDir = new File(storagePath);
storageDir.mkdirs();
imageFile = File.createTempFile(timeStamp, ".jpg", storageDir);
} catch (IOException e) {
e.printStackTrace();
}
//创建Uri
Uri photoURI = FileProvider.getUriForFile(this, "com.hm.camerademo.fileprovider", imageFile );
com.hm.camerademo.fileprovider
要和在AndroidManifest.xml中指定的一样。不然会报错。
###第四步 分享一个 Content URI
这个例子中我们是向系统的相机传递一个Uri
photoURI = FileProvider.getUriForFile(this, "com.hm.camerademo.fileprovider", photoFile);
Log.e(TAG, photoURI.getPath());
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, TAKE_PHOTO);
最后附上两张图,图片来自参考文档2
图一:使用FileProvider.getUriForFile(this, “com.hm.camerademo.fileprovider”, photoFile);的方式生成一个Uri
参考文档
【1】https://developer.android.com/training/secure-file-sharing/setup-sharing.html
【2】https://inthecheesefactory.com/blog/how-to-share-access-to-file-with-fileprovider-on-android-nougat/en