android媒体存储很大,Android_存储之scoped storage&媒体文件

Scoped storage

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

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

AndroidManifest.xml中权限声明:

执行代码:

File[] externalFiles = context.getExternalFilesDirs( null);for(File file : externalFiles) {try{

File fileA= new File( file, "aaaa.txt");

FileOutputStream fosA= newFileOutputStream( fileA );

fosA.close();

File fileB= new File( file.getParentFile().getParentFile().getParentFile().getParentFile(), "bbbb.txt");

Log.d( TAG,"fileA="+fileA+";

fileB="+fileB);

FileOutputStream fosB= newFileOutputStream( 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,  关闭这一功能。

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

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

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

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

File locationPermissions neededMethod of accessing (*)Files removed when app uninstalled?

App-specific directory

None

getExternalFilesDir()

Yes

Media collections

(photos, videos, audio)

READ_EXTERNAL_STORAGE

only when

accessing other apps' files

MediaStore

No

Downloads

(documents and

e-books)

None

Storage 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( newFile( 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 = newContentValues();

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值