学习http://blog.csdn.net/lmj623565791/article/details/72859156
Android7.0之前的拍照常规实现代码
public void takePhoto() { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (takePictureIntent.resolveActivity(getPackageManager()) != null) { String filename = new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.CHINA) .format(new Date()) + ".png"; File file = new File(Environment.getExternalStorageDirectory() + "/zz/", filename); mCurrentPhotoPath = file.getAbsolutePath(); Uri fileUri = Uri.fromFile(file); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); startActivityForResult(takePictureIntent, REQUEST_CODE_TAKE_PHOTO); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK && requestCode == REQUEST_CODE_TAKE_PHOTO) { image.setImageBitmap(BitmapFactory.decodeFile(mCurrentPhotoPath)); } }
Uri.fromFile(file)//为file:///storage/emulated/0/zz/20180607-163315.png 此代码在android7.0一下的系统运行正常,但是运行在android7.0上就会崩溃 原因是android7.0禁止应用对外公开;如果一项包含文件 URI 的 intent 离开您的应用,则应用出现故障, 并出现 FileUriExposedException 异常。 google给出的解决方案是:要在应用间共享文件,您应发送一项 content:// URI,并授予 URI 临时访问权限。 进行此授权的最简单方式是使用 FileProvider 类。 于是代码改为:
public void takePhoto() { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (takePictureIntent.resolveActivity(getPackageManager()) != null) { String filename = new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.CHINA) .format(new Date()) + ".png"; //对于面向 Android 7.0 的应用,Android 框架执行的 StrictMode API 政策禁止在您的应用外部公开 file:// URI。 // 如果一项包含文件 URI 的 intent 离开您的应用,则应用出现故障,并出现 FileUriExposedException 异常。 //官方解决方案:要在应用间共享文件,您应发送一项 content:// URI,并授予 URI 临时访问权限。 // 进行此授权的最简单方式是使用 FileProvider 类。如需了解有关权限和共享文件的详细信息,请参阅共享文件。 File file = new File(Environment.getExternalStorageDirectory() + "/zz/", filename); mCurrentPhotoPath = file.getAbsolutePath(); Uri fileUri = null; if (Build.VERSION.SDK_INT > 23) { /**Android 7.0以上的方式**/ fileUri = FileProvider.getUriForFile(this, "com.cyz.android7.fileprovider", file); } else { fileUri = Uri.fromFile(file); } takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); startActivityForResult(takePictureIntent, REQUEST_CODE_TAKE_PHOTO); System.out.println(fileUri+" "+Uri.fromFile(file)); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK && requestCode == REQUEST_CODE_TAKE_PHOTO) { image.setImageBitmap(BitmapFactory.decodeFile(mCurrentPhotoPath)); } }清单文件添加
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.cyz.android7.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>添加xml文件file_paths.xml
<?xml version="1.0" encoding="utf-8"?> <!-- 代表的目录为:Environment.getExternalStorageDirectory()/zz--> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="external" path="/zz/" /> </paths><!-- <paths xmlns:android="http://schemas.android.com/apk/res/android"> <root-path name="root" path="" /> <files-path name="files" path="" /> <cache-path name="cache" path="" /> <external-path name="external" path="" /> <external-files-path name="name" path="path" /> <external-cache-path name="name" path="path" /> </paths> <root-path/> 代表设备的根目录new File("/"); <files-path/> 代表context.getFilesDir() <cache-path/> 代表context.getCacheDir() <external-path/> 代表Environment.getExternalStorageDirectory() <external-files-path>代表context.getExternalFilesDirs() <external-cache-path>代表getExternalCacheDirs() -->这样得到的fileUrl为
content://com.cyz.android7.fileprovider/external/20180607-163315.png
对比之前的
file:///storage/emulated/0/zz/20180607-163315.png
对比发现android7.0为了文件安全将提供给别的app的路径进行了隐藏。 /** * 申请权限 */ public void requestPower() { //判断是否已经赋予权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { //如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true。 if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { //这里可以写个对话框之类的项向用户解释为什么要申请权限,并在对话框的确认键后续再次申请权限 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE_REQUEST_PERMISSION); } else { //申请权限,字符串数组内是一个或多个要申请的权限,REQUEST_CODE_REQUEST_PERMISSION是申请权限结果的返回参数, // 在onRequestPermissionsResult可以得知申请结果 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,}, REQUEST_CODE_REQUEST_PERMISSION); } } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_CODE_REQUEST_PERMISSION) { for (int i = 0; i < permissions.length; i++) { if (grantResults[i] == PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, "" + "权限" + permissions[i] + "申请成功", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "" + "权限" + permissions[i] + "申请失败", Toast.LENGTH_SHORT).show(); } } } }代码 点击打开链接