Android_存储之scoped storage&媒体文件

摘要:
作用域存储文件存储描述与内部存储和外部存储相关的内容。AndroidQ开始添加作用域存储功能,以更好地限制应用程序对外部存储的访问/在存储/模拟/0/和/storage/3B80-11D/下创建失败,导致权限问题。注意:使用作用域存储的应用程序无法直接访问sdcard/DCIM/IMG1024.JPG等路径。作用域存储还添加了对媒体相关数据的限制:除非应用程序已获得ACCESS_media_Location权限,否则图像文件中的Exif元数据将被删除。MediaStore。文件表已过滤,仅显示照片、视频和音频文件。

Scoped storage

文件存储介绍了内部存储和外部存储相关的内容。因为外部存储容易读写,所以在手机中经常看到很多“乱七八糟”的文件或文件夹,这些就是应用肆意创建的。

Android Q(10)开始添加了scoped storage的功能,更好的限制了应用访问外部存储。

先见个例子,下面代码运行在Android Q上会有什么现象呢:

AndroidManifest.xml中权限声明:

 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

执行代码:

File[] externalFiles = context.getExternalFilesDirs( null );
for (File file : externalFiles) {
    try {
        File fileA = new File( file, "aaaa.txt" );
        FileOutputStream fosA = new FileOutputStream( fileA );
        fosA.close();

        File fileB = new File( file.getParentFile().getParentFile().getParentFile().getParentFile(), "bbbb.txt" );
        Log.d( TAG, "fileA="+fileA+";
fileB="+fileB);
        FileOutputStream fosB = new FileOutputStream( fileB );
        fosB.close();
    } catch (IOException e) {
        Log.d( TAG, "exception: "+e.getMessage() );
        e.printStackTrace();
    }
}

执行的结果:

log的结果如下,实际与log是符合的。上述代码在四个 位置各创建一个文件,2个创建成功了2个fail了。/storage/emulated/0/和/storage/3B80-111D/下创建失败,提示权限问题。

2019-12-13 10:52:43.541 3973-3973/com.flx.testfilestorage D/flx_storage: fileA=/storage/emulated/0/Android/data/com.flx.testfilestorage/files/aaaa.txt;
    fileB=/storage/emulated/0/bbbb.txt
2019-12-13 10:52:43.543 3973-3973/com.flx.testfilestorage D/flx_storage: exception: /storage/emulated/0/bbbb.txt: open failed: EACCES (Permission denied)
2019-12-13 10:52:43.554 3973-3973/com.flx.testfilestorage D/flx_storage: fileA=/storage/3B80-111D/Android/data/com.flx.testfilestorage/files/aaaa.txt;
    fileB=/storage/3B80-111D/bbbb.txt
2019-12-13 10:52:43.556 3973-3973/com.flx.testfilestorage D/flx_storage: exception: /storage/3B80-111D/bbbb.txt: open failed: EACCES (Permission denied)

  

scoped storage在Android 10及更高版本默认开启。若之前的应用不满足这一功能,而运行在Android 10上 则需要将下面的属性设置成true,  关闭这一功能

<application android:requestLegacyExternalStorage="true" ... > 

上述代码修改后就能执行完成了。

注:后续版本可能强制要求开启scoped storage功能,上述关闭属性方法可能只是一个过渡。

以前的读写外部存储的权限:READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE。只要设置了这个就能够很容易的读写外部存储上的文件。

当scoped storage功能添加后,对权限和路径 具体有如下表格:

File locationPermissions neededMethod of accessing (*)Files removed when app uninstalled?
App-specific directoryNonegetExternalFilesDir()Yes
Media collections
(photos, videos, audio)
READ_EXTERNAL_STORAGE
only when
accessing other apps' files
MediaStoreNo
Downloads
(documents and
e-books)
NoneStorage Access Framework
(loads system's file picker)
No

从上表中看到,只是访问其他应用的媒体文件 才需要READ_EXTERNAL_STORAGE权限,其他访问方式都不需要任何权限。(Storage Access Framework不了解可以点击链接 了解下)

开启scoped storage后,访问自身应用创建的文件都不需要任何权限(不管文件时创建在内部存储还是外部存储中)。而访问其他应用创建的文件,需要满足

1.需要READ_EXTERNAL_STORAGE权限;

2.该文件需要在下列某个媒体集合中:

  照片:存储在 MediaStore.Images 中。(image/*)
  视频:存储在 MediaStore.Video 中。(video/*)
  音乐文件:存储在 MediaStore.Audio 中。(audio/*)

为了访问另一应用创建的文件(包括“downloads”目录下的文件),您的应用必须使用存储访问框架(Storage Access Framework),用户可以通过该框架选择特定文件。

注意:使用scoped storage的应用无法直接访问类似 sdcard/DCIM/IMG1024.JPG 的路径。 要访问此类文件,必须使用MediaStore,并调用openFile()之类的方法。


scoped storage还添加了媒体相关数据限制

除非您的应用已获得 ACCESS_MEDIA_LOCATION 权限,否则图片文件中的 Exif 元数据会被删除。
MediaStore.Files 表已经过滤,仅显示照片、视频和音频文件。例如,该表格不会再显示 PDF 文件。(下面媒体文件部分也说到的)

媒体文件

MediaStore提供api接口 来访问下面定义良好的的媒体文件:

照片:存储在 MediaStore.Images(image/*) 中。
视频:存储在 MediaStore.Video(video/*) 中。
音频:存储在 MediaStore.Audio(audio/*) 中。

 MediaStore.Files包含了所有media类型的文件集合。如果使用了scoped storage,则 MediaStore.Files仅仅包含上面3个类型(Images,Video,Audio)。

访问媒体文件

加载媒体文件,调用ContentResolver的方法:

  • 单个媒体文件,调用openFileDescriptor()。
  • 单个媒体文件的缩略图,调用loadThumbnail()。
  • 获取媒体文件集合,调用query()。

如下面一段代码 已Images为例,手机中只有拍摄的两张图片。

     ContentResolver contentResolver = this.getContentResolver();
        Uri imgUri =  MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        Uri firstImgUri = null;
        Cursor cursor = contentResolver.query( imgUri, null, null, null );
        if (cursor != null && cursor.getCount() > 0) {
            cursor.moveToFirst();
            firstImgUri = Uri.fromFile( new File( cursor.getString( cursor.getColumnIndex( MediaStore.Images.Media.DATA ) ) ) );
            do {
                Log.d( TAG, "img cursor data=" + cursor.getString( cursor.getColumnIndex( MediaStore.Images.Media.DATA ) )
                    +";
img cursor type=" + cursor.getString( cursor.getColumnIndex( MediaStore.Images.Media.MIME_TYPE ) ));
            } while (cursor.moveToNext());
        }
        Log.d( TAG, "firstImgUri="+firstImgUri );
        try {
            ParcelFileDescriptor parcelFileDescriptor = contentResolver.openFileDescriptor( firstImgUri, "r" );

            if (Build.VERSION.SDK_INT >= 29) {
                Bitmap bitmap = contentResolver.loadThumbnail( firstImgUri, new Size( 200,200 ), null );
            }
        } catch (Exception e) {
            Log.d( TAG, "exception: "+e.getMessage() );
        }

执行后的结果:

2019-01-02 11:31:00.937 15513-15513/com.flx.testfilestorage D/flx_storage: img cursor data=/storage/emulated/0/DCIM/Camera/IMG_20190102_031552_3.jpg;
    img cursor type=image/jpeg
2019-01-02 11:31:00.937 15513-15513/com.flx.testfilestorage D/flx_storage: img cursor data=/storage/emulated/0/DCIM/Camera/IMG_20190102_031909_3.jpg;
    img cursor type=image/jpeg
2019-01-02 11:31:00.938 15513-15513/com.flx.testfilestorage D/flx_storage: firstImgUri=file:///storage/emulated/0/DCIM/Camera/IMG_20190102_031552_3.jpg

  

IS_PENDING独占

Android 10以后,当写入磁盘时 应用可以通过IS_PENDING标志实现对媒体文件的独占访问。

如:

     ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.DISPLAY_NAME, "TEST.jpg");
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
        values.put(MediaStore.Images.Media.IS_PENDING, 1);

        Uri collection = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
        Uri item = contentResolver.insert(collection, values);

        try (ParcelFileDescriptor pfd = contentResolver.openFileDescriptor(item, "w", null)) {
            Parcel out = Parcel.obtain();
            pfd.writeToParcel( out, Parcelable.PARCELABLE_WRITE_RETURN_VALUE );
        } catch (IOException e) {
            Log.d( TAG, "e:"+e.getMessage() );
        }

        values.clear();
        values.put(MediaStore.Images.Media.IS_PENDING, 0);//释放,使其他应用可以访问
        contentResolver.update(item, values, null, null);

免责声明:文章转载自《Android_存储之scoped storage&amp;amp;媒体文件》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇opnecv日记_GaussianBlur函数——高斯滤波 中文解释参数含义hey-cli初使用下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

08 Tomcat+Java Web项目的创建和War的生成

1.web服务器软件:服务器:安装了服务器软件的计算机服务器软件:接收用户的请求,处理请求,做出响应 * web服务器软件:接收用户的请求,处理请求,做出响应。在web服务器软件中,可以部署web项目,让用户通过浏览器来访问这些项目 常见的java相关的web服务器软件:webLogic:oracle公司,大型的JavaEE服务器,支持所有的JavaEE规...

Money工具 压力稳定性测试

1.Money工具压力稳定性测试2.monkey是Android 平台自带的测试小工具3.原理:模拟用户的操作,点击,滑动,发送按键操作4.环境安装/部署-之前APP 自动化配置已经搞定5.链接Android设备1)模拟器 夜神cmd->adb connect 127.0.0.1:62001adb devices127.0.0.1 IP addre...

iOS自动化环境搭建(超详细)

1.macOS相关库安装 libimobiledevice > brew install libimobiledevice 使用本机与苹果iOS设备的服务进行通信的库。 ideviceinstaller brew install ideviceinstaller 获取设备udid、安装app、卸载app、获取bundleid carthage &...

uniapp APP端使用指纹

使用插件指纹模板: https://ext.dcloud.net.cn/plugin?id=358 Fingerprint模块管理指纹识别 要使用指纹识别功能需要具备条件: 确认当前设备环境是否支持指纹识别, 当前设备是否设置密码锁屏, 当前设备是否已经录入指纹。 (Android平台6.0及以上系统支持,只识别标准Android的指纹API,仅适配G...

H5与native 普及

H5与native 普及: H5是基于web,native基于客户端native是使用原生系统内核的,相当于直接在系统上操作。,是我们传统意义上的软件,更加稳定。但是H5的APP先得调用系统的浏览器内核,相当于是在网页中进行操作,较原生APP稳定性稍差。APP。但是h5最大的优点是可以跨平台,开发容易。native的话需要用ANDROID的语言和IOS的语...

python2.7+RobotFramework的UI自动化环境搭建

robotFramework是一种比较常见的自动化测试框架,此篇记录环境搭建 目录 1、软件准备 2、执行安装 1、软件准备 python-2.7.15.amd64.msi 链接:https://pan.baidu.com/s/15yS3-wTq7PDzI0rFhkDNLA 密码:hg2n wxPython2.8-win64-unicode-2.8.12....