原因
Android N 文件系统权限发生了变化 ,为了提高私有文件的安全性,在targetSdk版本为N或者以后版本的app中,其私有目录将会限制访问。这可以防止私有文件元数据的泄露,比如文件大小或者是文件是否存在。但这给开发者带来了很多不利的影响:
文件的owner不能放宽文件权限,如果你使用MODE_WORLD_READABLE或者 MODE_WORLD_WRITEABLE操作文件,将会触发SecurityException。
当你跨package域传递file://的URI时,接收者得到的将是一个无权访问的路径,因此,这将会触发FileUriExposedException。
解决方案
对于这类操作,官方推荐的方式是使用FileProvider,当然你也可以使用ContentProvider。
源代码
public void camera() {
String savePath = Environment.getExternalStorageDirectory();
String tempFileName = "temp.jpg"
Intent intent = null;
// 判断存储卡是否可以用,可用进行存储
if (StorageUtils.hasSdcard()) {
//设定拍照存放到自己指定的目录,可以先建好
File file = new File(savePath);
if(!file.exists()){
file.mkdirs();
}
Uri pictureUri;
File pictureFile = new File(savePath, tempFileName);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
ContentValues contentValues = new ContentValues(1);
contentValues.put(MediaStore.Images.Media.DATA, pictureFile.getAbsolutePath());
pictureUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,contentValues);
}else {
intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
pictureUri = Uri.fromFile(pictureFile);
}
if (intent != null) {
intent.putExtra(MediaStore.EXTRA_OUTPUT,
pictureUri);
startActivityForResult(intent, PHOTO_REQUEST_CAMERA);
}
}
}
/**
* 从相册获取
*/
public void gallery() {
// 激活系统图库,选择一张图片
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("image/*");
startActivityForResult(intent, PHOTO_REQUEST_GALLERY);
}
/**
*选择图片返回结果处理
*/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
if (requestCode == PHOTO_REQUEST_GALLERY) {//相册
if (data != null) {
// 得到图片的全路径
Uri uri = data.getData();
if (uri != null) {
tempPhotoPath = savePath + "/" + tempFileName;
//do something
}else {
Log.e("jaygoo","获取图片失败!");
}
}
} else if (requestCode == PHOTO_REQUEST_CAMERA) {//照相
/*
1.好些设备在调用系统相机照相后返回的 data为null
解决办法就是在调用相机之前先设定好照片的路径和名称,拍完后直接拿来用
2.如果图片地址没有图片存在,则表示没有进行照相
*/
tempPhotoPath = savePath + "/" + tempFileName;
File file = new File(tempPhotoPath);
if (file != null && file.exists()) {
Uri uri = Uri.fromFile(file);
try {
StorageUtils.saveFile(file, IMAGE_DIR);
} catch (IOException e) {
e.printStackTrace();
}
//do something
}else {
DialogToast.getInstance().showErrorToast("获取图片失败!");
}
}
} else if (resultCode == UCrop.RESULT_ERROR) {
Log.e("jaygoo","获取图片失败!");
}
super.onActivityResult(requestCode, resultCode, data);
}
扩展
这里兼容我采用的是ContentProvider,当然也可以使用Google官方推荐的FileProvider,这里我给出地址Demo,有兴趣的可以看下。FileProvider Demo