Java - Android N中Uri的坑,注意别掉下去了!

        如今,安卓的老东家谷歌开始慢慢的对安卓的文件管理权限越来越重视了,从Android M开始,一些涉及到个人隐私的权限都需要显示的进行声明,那么AndroidN又做了哪些变化呢?

  1. APP应用程序的私有文件不再向使用者放宽
  2. Intent组件传递 file://URI 的方式可能给接收器留下无法访问的路径,触发 FileUriExposedException 异常,推荐使用FileProvider
  3. DownloadManager不再按文件名分享私人存储的文件。旧版应用在访COLUMN_LOCAL_FILENAME时可能出现无法访问的路径。面向 Android 7.0 或更高版本的应用在尝试访问 COLUMN_LOCAL_FILENAME 时会触发 SecurityException


        在苹果上面,个人隐私权限一直都是备受重视的,尤其是文件的读写。至此,谷歌也开始对Android N的Uri进行了一些列的管理。下面我们来看看,Uri上到底发生了什么变化?


        在Android中,我们最常用到的就是图片截图的功能,往往我们最常使用的就是系统提供的截图功能,如下:

Android裁图代码

 Intent intent = new Intent("com.android.camera.action.CROP");
 intent.setDataAndType(uri, "image/*");
 intent.putExtra("crop", "true");
 intent.putExtra("aspectX", 2);
 intent.putExtra("aspectY", 1);
 intent.putExtra("outputX", outputX);
 intent.putExtra("outputY", outputY);
 intent.putExtra("scale", true);
 intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
 intent.putExtra("return-data", false);
 intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
 intent.putExtra("noFaceDetection", true); // no face detection
 startActivityForResult(intent, requestCode);

        但是在Android N中,我们需要对Uri路径,尤其是带 file:// 的Uri要进行处理。
首先,我们需要在App的 Manifest.xml 中声明权限的 Provider


Manifest.xml


   ...
   
        
            
        
        ...

特别说明
authorities:app的包名.fileProvider
grantUriPermissions:必须是true,表示授予 URI 临时访问权限
exported:必须是false
resource:中的@xml/file_paths是我们接下来要添加的文件

        注意到配置中的一个xml文件 file_paths 了吗?这里是我们需要分配给应用读取文件路径的配置文件。首先,我们应该在Res目录下创建xml文件夹,然后创建file_paths.xml文件。


file_paths.xml 配置如下:



    
    

其他说明:
path:需要临时授权访问的路径(.代表所有路径)
name:就是你给这个访问路径起个名字
根标签下可以添加的子标签也是有限的,参考官网的开发文档,除了上述的提到的这个子标签外,还包括下面几个:

  1. ,表示应用默认缓存根目录,对应根目录等价于getCacheDir(),查看完整路径:/data/user/0/cn.teachcourse.demos/cache
  2. ,表示外部内存卡根目录,对应根目录等价于
    Environment.getExternalStorageDirectory(),
    查看完整路径:/storage/emulated/0
  3. ,表示外部内存卡根目录下的APP公共目录,对应根目录等价于
    Context#getExternalFilesDir(String) Context.getExternalFilesDir(null),
    查看完整路径:
    /storage/emulated/0/Android/data/cn.teachcourse.demos/files/Download
  4. ,表示外部内存卡根目录下的APP缓存目录,对应根目录等价于Context.getExternalCacheDir(),查看完整路径:
    /storage/emulated/0/Android/data/cn.teachcourse.demos/cache
最终,在file_provider.xml文件中,添加上述5种类型的临时访问权限的文件目录,代码如下:



    
    
    
    
    
    
    
    
    
    
    

        配置说明完了,那么我们应该如何使用这一临时权限呢?请继续往下翻页。。。

 Uri uri;
 bool isSDKIntBigThanN = Build.VERSION.SDK_INIT >= Build.VERSION_CODES.N;
 if(isSDKIntBigThanN){
      uri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", file);
 }else{
      uri = Uri.from(file);
 }
 Intent intent = new Intent("com.android.camera.action.CROP");
 if(isSDKIntBiggerThanN){            
     intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION|Intent.FLAG_GRANT_READ_URI_PERMISSION); //添加相对应的读写权限的标识
 }
 intent.setDataAndType(uri, "image/*");
 intent.putExtra("crop", "true");
 intent.putExtra("aspectX", 2);
 intent.putExtra("aspectY", 1);
 intent.putExtra("outputX", outputX);
 intent.putExtra("outputY", outputY);
 intent.putExtra("scale", true);
 intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
 intent.putExtra("return-data", false);
 intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
 intent.putExtra("noFaceDetection", true); // no face detection
 startActivityForResult(intent, requestCode);

Ps: 裁图如果需要裁剪更清晰的图片,需要注释一上多个参数:
outputX, outputY, scale。因为需要更清晰的图片,又因考虑到图片占用内存较大,所以return-data必须写为false。

大致的问题都可以解决了,注意Uri的使用需要临时访问权限。如还不够明白,请参考:更详细的文档

你可能感兴趣的:(Java - Android N中Uri的坑,注意别掉下去了!)