美文网首页Android知识Android开发Android技术知识
Java - Android N中Uri的坑,注意别掉下去了!

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

作者: Cosecant | 来源:发表于2017-04-19 10:31 被阅读234次

    <p>        如今,安卓的老东家谷歌开始慢慢的对安卓的文件管理权限越来越重视了,从Android M开始,一些涉及到个人隐私的权限都需要显示的进行声明,那么AndroidN又做了哪些变化呢?<ol><li>APP应用程序的私有文件不再向使用者放宽</li><li>Intent组件传递 file://URI 的方式可能给接收器留下无法访问的路径,触发 FileUriExposedException 异常,推荐使用FileProvider</li><li>DownloadManager不再按文件名分享私人存储的文件。旧版应用在访COLUMN_LOCAL_FILENAME时可能出现无法访问的路径。面向 Android 7.0 或更高版本的应用在尝试访问 COLUMN_LOCAL_FILENAME 时会触发 SecurityException</li></ol></p>
    <p>        在苹果上面,个人隐私权限一直都是备受重视的,尤其是文件的读写。至此,谷歌也开始对Android N的Uri进行了一些列的管理。下面我们来看看,Uri上到底发生了什么变化?
    </p>
    <p>        在Android中,我们最常用到的就是图片截图的功能,往往我们最常使用的就是系统提供的截图功能,如下:
    </p>

    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);
    

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

    <application ...>
       ...
       <!-- 针对android N配置文件夹读取 -->
            <provider
                android:name="android.support.v4.content.FileProvider"
                android:authorities="${application_id}.fileProvider"
                android:grantUriPermissions="true"
                android:exported="false">
                <meta-data
                    android:name="android.support.FILE_PROVIDER_PATHS"
                    android:resource="@xml/file_paths" />
            </provider>
            ...
    </application>
    

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

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

    <?xml version="1.0" encoding="utf-8"?>
    <paths>
        <!--<external-path path="Android/data/app的包名/" name="root_path" /> -->
        <external-path path="." name="external_storage_root" />
    </paths>
    

    其他说明:
    path:需要临时授权访问的路径(.代表所有路径)
    name:就是你给这个访问路径起个名字
    <paths>根标签下可以添加的子标签也是有限的,参考官网的开发文档,除了上述的提到的<files-path>这个子标签外,还包括下面几个:<ol><li><cache-path>,表示应用默认缓存根目录,对应根目录等价于getCacheDir(),查看完整路径:/data/user/0/cn.teachcourse.demos/cache</li><li><external-path>,表示外部内存卡根目录,对应根目录等价于
    Environment.getExternalStorageDirectory(),
    查看完整路径:/storage/emulated/0</li><li><external-files-path>,表示外部内存卡根目录下的APP公共目录,对应根目录等价于
    Context#getExternalFilesDir(String) Context.getExternalFilesDir(null),
    查看完整路径:
    /storage/emulated/0/Android/data/cn.teachcourse.demos/files/Download</li><li><external-cache-path>,表示外部内存卡根目录下的APP缓存目录,对应根目录等价于Context.getExternalCacheDir(),查看完整路径:
    /storage/emulated/0/Android/data/cn.teachcourse.demos/cache</li></ol>最终,在file_provider.xml文件中,添加上述5种类型的临时访问权限的文件目录,代码如下:

    <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <!--
        1、name对应的属性值,开发者可以自由定义;
        2、path对应的属性值,当前external-path标签下的相对路径
        比如:/storage/emulated/0/92Recycle-release.apk
        sdcard路径:/storage/emulated/0(WriteToReadActivity.java:176)
                          at cn.teachcourse.nougat.WriteToReadActivity.onClick(WriteToReadActivity.java:97)
                          at android.view.View.performClick(View.java:5610)
                          at android.view.View$PerformClick.run(View.java:22265)
        相对路径:/
        -->
        <!--1、对应内部内存卡根目录:Context.getFileDir()-->
        <files-path
            name="int_root"
            path="/" />
        <!--2、对应应用默认缓存根目录:Context.getCacheDir()-->
        <cache-path
            name="app_cache"
            path="/" />
        <!--3、对应外部内存卡根目录:Environment.getExternalStorageDirectory()-->
        <external-path
            name="ext_root"
            path="pictures/" />
        <!--4、对应外部内存卡根目录下的APP公共目录:Context.getExternalFileDir(String)-->
        <external-files-path
            name="ext_pub"
            path="/" />
        <!--5、对应外部内存卡根目录下的APP缓存目录:Context.getExternalCacheDir()-->
        <external-cache-path
            name="ext_cache"
            path="/" />
    </paths>
    

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

     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的坑,注意别掉下去了!

        本文链接:https://www.haomeiwen.com/subject/bssczttx.html