AndroidQ(10)分区存储完美适配方法
前言
最近时间在做androidq的适配,截止到今天androidq分区存储适配完成,期间出现很多坑,目前网上的帖子大部分都是概述变更内容,接下来的几篇帖子都是对分区存储实际经验代码总结,填坑经验,特此记录一下,也为大家提供帮助。
本篇主要是对androidq(10)分区存储适配具体实现
- 要点:
- android q文件存储机制修改成了沙盒模式
- app只能访问自己目录下的文件和公共媒体文件
- 对于androidq以下,还是使用老的文件存储方式
这里需要注意:在适配androidq的时候还要兼容q系统版本以下的,使用sdk_version区分
背景
存储权限
android q仍然使用read_external_storage和write_external_storage作为存储相关运行时权限,但现在即使获取了这些权限,访问外部存储也受到了限制,只能访问自身目录下的文件和公共内体文件。
外部存储结构划分
公有目录:downloads、documents、pictures 、dcim、movies、music、ringtones等
地址:/storage/emulated/0/downloads(pictures)等
公有目录下的文件不会跟随app卸载而删除。
app私有目录
地址:/storage/emulated/0/android/data/包名/files
私有目录存放app的私有文件,会随着app的卸载而删除。
适配指导
androidq中使用contentresolver进行文件的增删改查
1、获取(创建)自身目录下的文件夹
获取及创建,如果手机中没有对应的文件夹,则系统会自动生成
//在自身目录下创建apk文件夹 file apkfile = context.getexternalfilesdir("apk");
2、创建自身目录下的文件
生成需要下载的路径,通过输入输出流读取写入
string apkfilepath = context.getexternalfilesdir("apk").getabsolutepath(); file newfile = new file(apkfilepath + file.separator + "temp.apk"); outputstream os = null; try { os = new fileoutputstream(newfile); if (os != null) { os.write("file is created".getbytes(standardcharsets.utf_8)); os.flush(); } } catch (ioexception e) { } finally { try { if (os != null) { os.close(); } } catch (ioexception e1) { } }
3、创建公共目录下的文件夹
通过mediastore.insert写入
if (build.version.sdk_int < build.version_codes.q) { return null; } contentresolver resolver = context.getcontentresolver(); contentvalues values = new contentvalues(); values.put(mediastore.downloads.display_name, filename); values.put(mediastore.downloads.description, filename); //设置文件类型 values.put(mediastore.downloads.mime_type, "application/vnd.android.package-archive"); //注意mediastore.downloads.relative_path需要targetversion=29, //故该方法只可在android10的手机上执行 values.put(mediastore.downloads.relative_path, "download" + file.separator + "apk"); uri external = mediastore.downloads.external_content_uri; uri inserturi = resolver.insert(external, values); return inserturi;
4、公共目录下的指定文件夹下创建文件
结合上面代码,我们主要是在公共目录下创建文件或文件夹拿到本地路径uri,不同的uri,可以保存到不同的公共目录中。接下来使用输入输出流就可以写入文件
重点:androidq中不支持file://类型访问文件,只能通过uri方式访问
contentresolver resolver = context.getcontentresolver(); uri inserturi = resolver.insert(external, values); if(inserturi == null) { return; } string mfilepath = inserturi.tostring(); inputstream is = null; outputstream os = null; try { os = resolver.openoutputstream(inserturi); if(os == null){ return; } int read; file sourcefile = new file(sourcepath); if (sourcefile.exists()) { // 文件存在时 is = new fileinputstream(sourcefile); // 读入原文件 byte[] buffer = new byte[1024]; while ((read = is.read(buffer)) != -1) { os.write(buffer, 0, read); } } } catch (exception e) { e.printstacktrace(); }finally { try { if (is != null) { is.close(); } if (os != null) { os.close(); } } catch (ioexception e) { e.printstacktrace(); } }
5、通过mediastore读取公共目录下的文件
parcelfiledescriptor parcelfiledescriptor = null; filedescriptor filedescriptor = null; bitmap tagbitmap = null; try { parcelfiledescriptor = context.getcontentresolver().openfiledescriptor(uri, "r"); if (parcelfiledescriptor != null && parcelfiledescriptor.getfiledescriptor() != null) { filedescriptor = parcelfiledescriptor.getfiledescriptor(); //转换uri为bitmap类型 tagbitmap = bitmapfactory.decodefiledescriptor(filedescriptor); } } catch (filenotfoundexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } finally { try { if (parcelfiledescriptor != null) { parcelfiledescriptor.close(); } } catch (ioexception e) { } }
6、使用mediastore删除文件
context.getcontentresolver().delete(fileuri, null, null);
7、app通过mediastore访问文件所需要的权限
header 1 | 无权限 | read_external |
---|---|---|
audio | 可读写app自己创建的文件,但不可直接使用路径访问 | 可以读其他app创建的媒体类文件,删改操作需要用户授权 |
image | 可读写app自己创建的文件,但不可直接使用路径访问 | 可以读其他app创建的媒体类文件,删改操作需要用户授权 |
file | 可读写app自己创建的文件,但不可直接使用路径访问 | 不可读写其他app创建的非媒体类文件 |
downloads | 可读写app自己创建的文件,但不可直接使用路径访问 | 不可读写其他app创建的非媒体类文件 |
后续对androidq存储针对具体功能做介绍,欢迎关注~
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: vue 开发之路由配置方法详解
下一篇: Flutter Dio二次封装的实现