千家信息网

Android存储访问框架怎么使用

发表于:2024-11-18 作者:千家信息网编辑
千家信息网最后更新 2024年11月18日,这篇文章主要讲解了"Android存储访问框架怎么使用",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"Android存储访问框架怎么使用"吧!存储访问框
千家信息网最后更新 2024年11月18日Android存储访问框架怎么使用

这篇文章主要讲解了"Android存储访问框架怎么使用",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"Android存储访问框架怎么使用"吧!

存储访问框架,简称:SAF, 就是系统文件选择器+文件操作API。先选择文件,在用文件操作API处理文件。系统文件选择器,就和Windows的文件选择框一样。

其实绝大多数app,都不会使用这个东西,因为太不方便了。图片,视频,普通文件,需要用户去翻文件夹找,这样的用户体验实在太差了。所以大家都是用第三方的或者自己写一个文件选择器。

之所以讲SAF,一,是因为Android11以后,使用MediaStore无法访问到非多媒体文件了,需要依赖SAF了。二,外卡和SD卡的操作依赖于存储访问框架授权。

打开系统文件选择器与文件过滤

 Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);        intent.addCategory(Intent.CATEGORY_OPENABLE);        intent.setType("application/*");         startActivityForResult(intent, REQUEST_CODE)

setType的值是mime type, 可以是"image/*", "*/*", 其中*是通配符。"image/*"代码所有类型的图片。"*/*"代表所有类型的文件。

当只需要打开几种文件类型时,可以用Intent.EXTRA_MIME_TYPES。同时setType设成"*/*"。

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);        intent.addCategory(Intent.CATEGORY_OPENABLE);        intent.setType("*/*");        intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {                "application/pdf", // .pdf                "application/vnd.oasis.opendocument.text", // .odt                "text/plain" // .txt        });          startActivityForResult(intent, REQUEST_CODE)

Intent.ACTION_PICK和ACTION_GET_CONTENT,也可以打开文件选择框。ACTION_GET_CONTENT更加宽泛,除了文件其他类型的内容还可以取。

 Intent intent = new Intent(Intent.ACTION_PICK,                      android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);    intent.setType("image/*");
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);        intent.setType("image/*")

下面列举了所有的mime type:

private static final String[][] MIME_TYPES = new String[][]{            {"3gp", "video/3gpp"},            {"apk", "application/vnd.android.package-archive"},            {"asf", "video/x-ms-asf"},            {"avi", "video/x-msvideo"},            {"bin", "application/octet-stream"},            {"bmp", "image/bmp"},            {"c", "text/plain"},            {"class", "application/octet-stream"},            {"conf", "text/plain"},            {"cpp", "text/plain"},            {"doc", "application/msword"},            {"docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},            {"xls", "application/vnd.ms-excel"},            {"xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},            {"exe", "application/octet-stream"},            {"gif", "image/gif"},            {"gtar", "application/x-gtar"},            {"gz", "application/x-gzip"},            {"h", "text/plain"},            {"htm", "text/html"},            {"html", "text/html"},            {"jar", "application/java-archive"},            {"java", "text/plain"},            {"jpeg", "image/jpeg"},            {"jpg", "image/jpeg"},            {"js", "application/x-JavaScript"},            {"log", "text/plain"},            {"m3u", "audio/x-mpegurl"},            {"m4a", "audio/mp4a-latm"},            {"m4b", "audio/mp4a-latm"},            {"m4p", "audio/mp4a-latm"},            {"ape", "audio/ape"},            {"flac", "audio/flac"},            {"m4u", "video/vnd.mpegurl"},            {"m4v", "video/x-m4v"},            {"mov", "video/quicktime"},            {"mp2", "audio/x-mpeg"},            {"mp3", "audio/x-mpeg"},            {"mp4", "video/mp4"},            {"mkv", "video/x-matroska"},            {"flv", "video/x-flv"},            {"divx", "video/x-divx"},            {"mpa", "video/mpeg"},            {"mpc", "application/vnd.mpohun.certificate"},            {"mpe", "video/mpeg"},            {"mpeg", "video/mpeg"},            {"mpg", "video/mpeg"},            {"mpg4", "video/mp4"},            {"mpga", "audio/mpeg"},            {"msg", "application/vnd.ms-outlook"},            {"ogg", "audio/ogg"},            {"pdf", "application/pdf"},            {"png", "image/png"},            {"pps", "application/vnd.ms-powerpoint"},            {"ppt", "application/vnd.ms-powerpoint"},            {"pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},            {"prop", "text/plain"},            {"rc", "text/plain"},            {"rmvb", "audio/x-pn-realaudio"},            {"rtf", "application/rtf"},            {"sh", "text/plain"},            {"tar", "application/x-tar"},            {"tgz", "application/x-compressed"},            {"txt", "text/plain"},            {"wav", "audio/x-wav"},            {"wma", "audio/x-ms-wma"},            {"wmv", "audio/x-ms-wmv"},            {"wps", "application/vnd.ms-works"},            {"xml", "text/plain"},            {"z", "application/x-compress"},            {"zip", "application/x-zip-compressed"},            {"rar", "application/x-rar"},            {"", "*/*"}    };

打开指定文件夹

利用DocumentsContract.EXTRA_INITIAL_URI,在打开文件选择器的时候,跳转到指定文件夹。只有android 8以上才行。

Uri uri = Uri.parse("content://com.android.externalstorage.documents/document/primary:Download");Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);intent.addCategory(Intent.CATEGORY_OPENABLE);intent.setType("*/*");intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, uri);startActivityForResult(intent, 1);

文件夹权限申请

当需要读取非公共文件夹里面的文件时,可以申请授权,授权后保存Uri,之后可以拼接这个Uri操作文件夹里的所有文件。

尤其是SD卡,从Android 5 开始文件的修改删除必须先授权,且必须通过SVF框架接口才能操作。

可以使用EXTRA_INITIAL_URI,打开指定文件夹,让用户授权

  Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);       Uri uri = Uri.parse("content://com.android.externalstorage.documents/document/primary:Download");        intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, uri);         startActivityForResult(intent)

需要注意的是,Android 11以后,无法授权访问存储根目录,以及Download/,Android/, 这两个文件夹也无法授权。

创建文件夹

创建文件夹有两个情况,一个是在已授权的文件夹下,可以使用SVF框架API。

DocumentsContract.createDocument()

还有一种是在无授权的文件夹下创建,那么可以直接指定类型和名字,通过跳系统选择框创建。

 Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);        intent.addCategory(Intent.CATEGORY_OPENABLE);        intent.setType("application/txt");        intent.putExtra(Intent.EXTRA_TITLE, "testfile.txt");         startActivityForResult(intent)

存储访问框架API

存储访问框架API,都在DocumentsContract里面,典型的有:

public static @Nullable Uri renameDocument(@NonNull ContentResolver content,            @NonNull Uri documentUri, @NonNull String displayName) throws FileNotFoundException {     }     /**     * Delete the given document.     *     * @param documentUri document with {@link Document#FLAG_SUPPORTS_DELETE}     * @return if the document was deleted successfully.     */    public static boolean deleteDocument(@NonNull ContentResolver content, @NonNull Uri documentUri)            throws FileNotFoundException {     }     /**     * Copies the given document.     *     * @param sourceDocumentUri document with {@link Document#FLAG_SUPPORTS_COPY}     * @param targetParentDocumentUri document which will become a parent of the source     *         document's copy.     * @return the copied document, or {@code null} if failed.     */    public static @Nullable Uri copyDocument(@NonNull ContentResolver content,            @NonNull Uri sourceDocumentUri, @NonNull Uri targetParentDocumentUri)            throws FileNotFoundException {     }     /**     * Moves the given document under a new parent.     *     * @param sourceDocumentUri document with {@link Document#FLAG_SUPPORTS_MOVE}     * @param sourceParentDocumentUri parent document of the document to move.     * @param targetParentDocumentUri document which will become a new parent of the source     *         document.     * @return the moved document, or {@code null} if failed.     */    public static @Nullable Uri moveDocument(@NonNull ContentResolver content,            @NonNull Uri sourceDocumentUri, @NonNull Uri sourceParentDocumentUri,            @NonNull Uri targetParentDocumentUri) throws FileNotFoundException {     }     /**     * Removes the given document from a parent directory.     *     * 

In contrast to {@link #deleteDocument} it requires specifying the parent. * This method is especially useful if the document can be in multiple parents. * * @param documentUri document with {@link Document#FLAG_SUPPORTS_REMOVE} * @param parentDocumentUri parent document of the document to remove. * @return true if the document was removed successfully. */ public static boolean removeDocument(@NonNull ContentResolver content, @NonNull Uri documentUri, @NonNull Uri parentDocumentUri) throws FileNotFoundException { }

获取文件夹文件

使用DocumentFile类获取文件夹里文件列表。

private ActivityResultLauncher openFile() {        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);        Uri uri = Uri.parse("content://com.android.externalstorage.documents/document/primary:AuthSDK");        intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, uri);        return startActivityForResult(intent, new ActivityResultCallback() {            @Override            public void onActivityResult(Intent result) {                 for (DocumentFile documentFile : DocumentFile.fromTreeUri(BaseApplication.getInstance().getApplicationContext(), Uri.parse(result.getData().toString())).listFiles()) {                      Log.i("", documentFile.getUri());                }            }        });    }

下面的代码演示了,使用SVF读取文件内容,写内容,通过MediaStore查询文件属性。

private ActivityResultLauncher openFile() {        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);        Uri uri = Uri.parse("content://com.android.externalstorage.documents/document/primary:AuthSDK");        intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, uri);         return startActivityForResult(intent, new ActivityResultCallback() {            @Override            public void onActivityResult(Intent result) {                 for (DocumentFile documentFile : DocumentFile.fromTreeUri(BaseApplication.getInstance().getApplicationContext(), Uri.parse(result.getData().toString())).listFiles()) {                    try {                        InputStream inputStream = BaseApplication.getInstance().getContentResolver().openInputStream(documentFile.getUri());                        byte[] readData = new byte[1024];                        inputStream.read(readData);                         OutputStream outputStream = BaseApplication.getInstance().getContentResolver().openOutputStream(documentFile.getUri());                        byte[] writeData = "alan gong".getBytes(StandardCharsets.UTF_8);                        outputStream.write(writeData, 0, 9);                        outputStream.close();                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {                            Uri mediaUri = MediaStore.getMediaUri(BaseApplication.getInstance().getApplicationContext(), documentFile.getUri());                            long fileId = ContentUris.parseId(mediaUri);                            Cursor query = BaseApplication.getInstance().getContentResolver().query(documentFile.getUri(), null, MediaStore.MediaColumns._ID + "=" + fileId, null, null);                            int columnIndex = query.getColumnIndexOrThrow(MediaStore.MediaColumns.MIME_TYPE);                            String mimeType = query.getString(columnIndex);                            Log.i("", "");                        }                     } catch (FileNotFoundException e) {                        e.printStackTrace();                    } catch (IOException e) {                        e.printStackTrace();                    }                }            }        });    }

使用MediaStore.getMediaUri(documentUri)可以转换,MediaStore Uri 和 Document Uri。通过MediaStore Uri中的数据库id,就可以查询文件的所有属性了。

MediaStore Uri:content://media/external_primary/file/101750

Document Uri: content://com.android.externalstorage.documents/tree/primary%3AAuthSDK

另外,

非公共目录下不能用File API操作的,即使通过SVF授权了, READ_EXTRNAL_PERMISSION的权限也给了。还是会抛出FileNotFoundException, 并且显示permission deny。

和MediaStore API的不同

存储访问框架API和MediaStore API的差异,在于存储访问框架API,是基于系统文件选择框的,用户选择了文件,那么相当于授权了, 可以访问所有类型的文件。而MediaStore的特点是可以查询出所有文件,但是开启分区存储后,只能查处多媒体文件,其他类型文件是不可以的。

感谢各位的阅读,以上就是"Android存储访问框架怎么使用"的内容了,经过本文的学习后,相信大家对Android存储访问框架怎么使用这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

文件 文件夹 框架 存储 选择 类型 内容 系统 用户 学习 查询 两个 代码 图片 多媒体 就是 属性 情况 是在 权限 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 js如何对数据库修改 适合软件开发的人 汽车软件开发体系认证 软件开发公司产品部门职责 网络安全法制什么时候起实行 兴安盟网络安全局 小程序获取不到服务器数据 远程网关服务器的设置 游戏服务器租用福州 计算机网络技术开设专业 三星970evo 服务器硬盘 pg数据库视图依赖的表 考研有没有软件开发 金融科技与互联网金融课程 广州拉博夫互联网科技有限公司 安徽省税务局税控盘服务器地址 南京网站服务器租用 软件开发的收入和成本 如何破解手机app数据库 数据库安全研究成果 信都区网络安全责任制实施细则 数据库系统表在但查询不了 无锡凯尚互联网科技有限公司 软件开发营业执照需要什么材料 软件开发 火车模型 智能自动化软件开发工程师 非计算机专业数据库技术应用 宝鸡誉恒软件开发公司 戴尔R730服务器远程管理 软件开发创新小组职责
0