文章摘要

  本文深入探讨了安卓DocumentsProvider的应用场景,分析了其优势与不足,并提供了简单的代码实现。DocumentsProvider是安卓系统中用于文件存储与访问的关键组件,为应用开发者提供了强大的文件管理能力。

正文

DocumentsProvider概述

  DocumentsProvider是安卓系统中的一个组件,允许应用以统一的方式访问和管理文件。它作为存储访问框架(Storage Access Framework, SAF)的一部分,为开发者提供了一种简便、统一的方式来浏览和操作用户的文件,无需直接访问文件系统。

应用场景

文件浏览器

  文件管理器应用可以使用DocumentsProvider来访问和管理设备上的各种文件系统,包括内部存储、外部SD卡、云存储等。如Google的文件应用,

云服务集成

  云存储服务如Google Drive、Dropbox等可以通过实现DocumentsProvider来将其云存储空间集成到Android的文件选择器中,使得其他应用可以轻松地访问和操作云端文件。

自定义文件源

  对于需要展示非传统文件系统的应用(如网络文件、数据库内容等),可以通过实现自定义的DocumentsProvider来实现。

跨应用文件共享

  应用可以使用DocumentsProvider与其他应用共享文件,例如一个图片编辑应用可能需要通过DocumentsProvider来获取用户从图库或文件管理器中选择的图片。

备份和恢复功能

  应用可以使用DocumentsProvider来实现数据的备份和恢复功能,将用户数据保存到特定的位置,以便在需要时恢复。

优势分析

统一接口

  DocumentsProvider提供了标准化的接口来访问和管理文件,使得不同应用之间的文件交互更加简单和一致。

安全性

  通过SAF,应用可以请求用户授权以访问特定文件或文件夹,增强了用户隐私保护。

灵活性

  支持自定义的DocumentsProvider,可以扩展以支持各种非标准的文件源。

兼容性

  DocumentsProvider是Android系统的一部分,因此在大多数Android设备上都能得到良好的支持。

支持多种文档类型

  DocumentsProvider 支持多种文档类型,如图片、视频、音频等,这使得开发人员可以更轻松地处理不同的文档类型。

遵循沙箱模型

  DocumentsProvider 遵循沙箱模型,这意味着每个应用程序只能访问其自己的文档,而不能访问其他应用程序的文档。这有助于保护用户的数据隐私。

易于使用

  DocumentsProvider 提供了一套简单易用的 API,使得开发人员可以轻松地实现文档浏览、编辑、存储等功能。

跨应用文件共享

  通过DocumentsProvider,应用可以方便地与其他应用共享文件,增强了用户体验和应用间的协作能力。

不足分析

版本兼容性

  早期版本的安卓可能不支持SAF和DocumentsProvider。

实现复杂性

  实现一个自定义的DocumentsProvider需要对内容提供者和文件系统有深入的理解,这可能会增加开发的复杂性和难度。

性能考虑

  对于大量文件的操作,如果不进行优化,可能会影响性能。

兼容性问题

  虽然DocumentsProvider是Android系统的一部分,但在某些老旧或者定制的Android系统上可能存在兼容性问题。

缺乏对自定义文档的支持

  DocumentsProvider 不支持自定义文档类型,这意味着如果你的应用程序需要处理特定的文档类型,你可能需要实现自己的文档访问机制。

代码实现(示例)

文件浏览

  以下是一个简单的使用DocumentsProvider生成文件浏览器的代码实例。这个示例将展示如何创建一个基本的文件浏览Activity,该Activity可以列出由DocumentsProvider提供的文件和目录。

  首先,我们需要在AndroidManifest.xml中声明和注册我们的DocumentsProvider。

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

<provider

    android:name=".FileBrowserDocumentsProvider"

    android:authorities="com.example.filebrowser.documentsprovider"

    android:exported="true"

android:grantUriPermissions="true"

    android:permission="android.permission.MANAGE_DOCUMENTS">

          <intent-filter>

              <action android:name="android.content.action.DOCUMENTS_PROVIDER" />

            </intent-filter>

            <meta-data

                android:name="android.content.extra.AUTHORITY"

                android:value="com.example.documentsprovider" />

</provider>

  然后,我们创建一个实现DocumentsProvider的类:

public class FileBrowserDocumentsProvider extends DocumentsProvider {

    private static final String ROOT_ID = "root";

    @Override

    public boolean onCreate() {

        return true;

    }

    @Nullable

    @Override

    public Cursor queryRoots(@Nullable String[] projection) throws FileNotFoundException {

        MatrixCursor cursor = new MatrixCursor(resolveRootProjection(projection));

        // 添加一个虚拟的根目录

        cursor.newRow()

                .add(ROOT_ID) // _id

                .add("Internal Storage") // document_id

                .add(null) // parent_document_id

                .add("internal_storage") // mime_type

                .add(R.drawable.ic_folder) // icon

                .add(true) // is_directory

                .add(false) // is_root

                .add(true) // is_virtual

                .add("") // display_name

                .add(getPathForDocId(ROOT_ID)) // summary

                .add(null); // capabilities

        return cursor;

    }

    @Nullable

    @Override

    public Cursor queryDocument(@NonNull String docId, @Nullable String[] projection) throws FileNotFoundException {

        // 实现查询单个文件或目录的逻辑

        // ...

    }

    @Nullable

    @Override

    public Cursor queryChildDocuments(@NonNull String parentDocId, @Nullable String[] projection, @Nullable String sortOrder) throws FileNotFoundException {

        // 实现查询子文件或子目录的逻辑

        // ...

    }

    @Nullable

    @Override

    public ParcelFileDescriptor openDocument(@NonNull String docId, @NonNull String mode, @Nullable CancellationSignal signal) throws FileNotFoundException {

        // 打开指定文档并返回ParcelFileDescriptor

        // ...

    }

    private String getPathForDocId(String docId) {

        // 根据docId获取对应的文件路径

        // ...

    }

    // 其他需要重写的方法...

}

  接下来,我们创建一个Activity来显示文件浏览器。

public class FileBrowserActivity extends AppCompatActivity {

    private RecyclerView recyclerView;

    private FileBrowserAdapter adapter;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_file_browser);

        recyclerView = findViewById(R.id.recycler_view);

        recyclerView.setLayoutManager(new LinearLayoutManager(this));

        adapter = new FileBrowserAdapter();

        recyclerView.setAdapter(adapter);

        loadFiles();

    }

    private void loadFiles() {

        Uri uri = DocumentsContract.buildRootsUri(FileBrowserDocumentsProvider.AUTHORITY);

        CursorLoader cursorLoader = new CursorLoader(this, uri, null, null, null, null);

        cursorLoader.registerListener(0, new Loader.OnLoadCompleteListener<Cursor>() {

            @Override

            public void onLoadComplete(Loader<Cursor> loader, Cursor data) {

                adapter.swapCursor(data);

            }

        });

        cursorLoader.startLoading();

    }

    private class FileBrowserAdapter extends CursorAdapter {

        public FileBrowserAdapter() {

            super(FileBrowserActivity.this, null, 0);

        }

        @Override

        public View newView(Context context, Cursor cursor, ViewGroup parent) {

            View itemView = LayoutInflater.from(context).inflate(R.layout.item_file_browser, parent, false);

            return itemView;

        }

        @Override

        public void bindView(View view, Context context, Cursor cursor) {

            TextView textView = view.findViewById(R.id.text_view);

            textView.setText(cursor.getString(cursor.getColumnIndex(DocumentsContract.Document.COLUMN_DISPLAY_NAME)));

            ImageView imageView = view.findViewById(R.id.image_view);

            int iconResId = cursor.getInt(cursor.getColumnIndex(DocumentsContract.Root.COLUMN_ICON));

            imageView.setImageResource(iconResId);

        }

    }

}

  在实际应用中,开发者需要根据自己的需求来扩展这些方法,以支持特定的文件操作和管理功能。

跨应用文件共享

  以下是一个使用DocumentsProvider实现跨应用文件共享的Java代码实例。这个示例将展示如何创建一个简单的DocumentsProvider,该提供者可以共享一个特定的文件夹给其他应用。

  首先,我们需要在AndroidManifest.xml中声明和注册我们的DocumentsProvider。

<provider

    android:name=".FileSharingDocumentsProvider"

    android:authorities="com.example.filesharing.documentsprovider"

    android:exported="true" />

  然后,我们创建一个实现DocumentsProvider的类。

public class FileSharingDocumentsProvider extends DocumentsProvider {

    private static final String AUTHORITY = "com.example.filesharing.documentsprovider";

    private static final String ROOT_ID = "root";

    private static final String SHARED_FOLDER_PATH = "/sdcard/shared_files";

    @Override

    public boolean onCreate() {

        return true;

    }

    @Nullable

    @Override

    public Cursor queryRoots(@Nullable String[] projection) throws FileNotFoundException {

        MatrixCursor cursor = new MatrixCursor(resolveRootProjection(projection));

        // 添加一个虚拟的根目录,指向我们要共享的文件夹

        cursor.newRow()

                .add(ROOT_ID) // _id

                .add("Shared Files") // document_id

                .add(null) // parent_document_id

                .add("vnd.android.document/directory") // mime_type

                .add(R.drawable.ic_folder) // icon

                .add(true) // is_directory

                .add(false) // is_root

                .add(true) // is_virtual

                .add("Shared Files") // display_name

                .add(SHARED_FOLDER_PATH) // summary

                .add(DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD); // capabilities

        return cursor;

    }

    @Nullable

    @Override

    public Cursor queryDocument(@NonNull String docId, @Nullable String[] projection) throws FileNotFoundException {

        // 实现查询单个文件或目录的逻辑

        // ...

    }

    @Nullable

    @Override

    public Cursor queryChildDocuments(@NonNull String parentDocId, @Nullable String[] projection, @Nullable String sortOrder) throws FileNotFoundException {

        if (ROOT_ID.equals(parentDocId)) {

            File sharedFolder = new File(SHARED_FOLDER_PATH);

            List<String> filesList = new ArrayList<>();

            for (File file : sharedFolder.listFiles()) {

                filesList.add(file.getName());

            }

            MatrixCursor cursor = new MatrixCursor(resolveDocumentProjection(projection));

            for (String fileName : filesList) {

                cursor.newRow()

                        .add(fileName) // _id

                        .add(fileName) // document_id

                        .add(ROOT_ID) // parent_document_id

                        .add(getMimeTypeForFile(fileName)) // mime_type

                        .add(0) // flags

                        .add(fileName) // display_name

                        .add("") // summary

                        .add(0); // size

            }

            return cursor;

        } else {

            throw new FileNotFoundException("Invalid parent document ID: " + parentDocId);

        }

    }

    @Nullable

    @Override

    public ParcelFileDescriptor openDocument(@NonNull String docId, @NonNull String mode, @Nullable CancellationSignal signal) throws FileNotFoundException {

        File file = new File(SHARED_FOLDER_PATH, docId);

        if (!file.exists()) {

            throw new FileNotFoundException("File not found: " + docId);

        }

        int fileMode = parseMode(mode);

        return ParcelFileDescriptor.open(file, fileMode);

    }

    private String getMimeTypeForFile(String fileName) {

        // 根据文件名获取对应的MIME类型

        // ...

    }

    private int parseMode(String mode) {

        // 将mode字符串(如"r"、"w"、"rw")转换为相应的ParcelFileDescriptor打开模式(如MODE_READ_ONLY、MODE_WRITE_ONLY、MODE_READ_WRITE)

        // ...

    }

    // 其他需要重写的方法...

}

  在这个示例中,我们创建了一个名为FileSharingDocumentsProvider的类,它继承自DocumentsProvider并实现了几个核心方法。这个提供者提供了一个虚拟的根目录,指向我们想要共享的文件夹(在这个例子中是"/sdcard/shared_files")。当我们查询这个根目录的子文档时,提供者会列出该文件夹中的所有文件,并返回它们的信息。

  现在,其他应用可以通过以下方式访问到这个共享文件夹。

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);

intent.addCategory(Intent.CATEGORY_OPENABLE);

intent.setType("*/*");

startActivityForResult(intent, REQUEST_CODE_OPEN_DOCUMENT);

在onActivityResult()方法中,你可以获取到用户选择的文件的Uri:

@Override

protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {

    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == REQUEST_CODE_OPEN_DOCUMENT && resultCode == RESULT_OK && data != null) {

        Uri uri = data.getData();

        // 使用获取到的Uri进行文件操作

        // ...

    }

}

  请注意,这只是一个基础的示例,实际的DocumentsProvider可能需要处理更多的细节,例如权限控制、错误处理、文件操作等。此外,这个示例假设你已经有一个名为"/sdcard/shared_files"的文件夹,并且你的应用有读取和写入该文件夹的权限。在实际应用中,你需要根据你的需求和目标文件系统的特性来实现DocumentsProvider的相应方法。

总结

  DocumentsProvider为安卓应用开发者提供了一种强大而灵活的文件管理方式。通过了解其应用场景、优势与不足,并结合实际的代码实现,开发者可以更有效地利用这一工具来增强应用的文件管理功能。

安卓之DocumentsProvider应用场景以及优劣分析的更多相关文章

  1. 理解JWT的使用场景和优劣

    理解JWT的使用场景和优劣 淘楼小能手 百家号04-2816:20 经过前面两篇文章<JSON Web Token - 在Web应用间安全地传递信息><八幅漫画理解使用JSON We ...

  2. spark和strom优劣分析

    对于Storm来说:1.建议在那种需要纯实时,不能忍受1秒以上延迟的场景下使用,比如实时金融系统,要求纯实时进行金融交易和分析2.此外,如果对于实时计算的功能中,要求可靠的事务机制和可靠性机制,即数据 ...

  3. Java 集合系列08之 List总结(LinkedList, ArrayList等使用场景和性能分析)

    概要 前面,我们学完了List的全部内容(ArrayList, LinkedList, Vector, Stack). Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例 Ja ...

  4. Java 集合系列 07 List总结(LinkedList, ArrayList等使用场景和性能分析)

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  5. 【性能诊断】五、并发场景的性能分析(windbg简介及dump抓取)

    windbg简介 Windbg是在windows平台下,强大的用户态和内核态调试工具.相比较于Visual Studio,它是一个轻量级的调试工具,所谓轻量级指的是它的安装文件大小较小,但是其调试功能 ...

  6. Java集合系列:-----------06List的总结(LinkedList,ArrayList等使用场景和性能分析)

    现在,我们再回头看看总结一下List.内容包括:第1部分 List概括第2部分 List使用场景第3部分 LinkedList和ArrayList性能差异分析第4部分 Vector和ArrayList ...

  7. 小白学习mysql之存储过程的优劣分析以及接入控制

    存储过程的优劣 存储过程是一组实现特定功能的SQL语句集合,存储过程一经编译便存储在了服务器上,可以通过调用存储过程的名字以及传入相应的参数来使用存储过程.要高层次的掌握存储过程,不能觉得依葫芦画瓢, ...

  8. WCF技术的不同应用场景及其实现分析

    这一篇文章,是总结一下WCF技术,以及基于这个技术发展出来的几个典型应用场景,并且我将尝试对这些不同的WCF实现的原理进行一些比较分析. 关于WCF这个技术的基本概念,如果你不是很清楚,可以参考一下有 ...

  9. 【性能诊断】二、单功能场景的性能分析(fiddler、SQL Profiler)

    Fiddler fiddler是最强大最好用的Web调试工具之一,它能记录所有客户端和服务器的http和https请求,允许你监视,设置断点,甚至修改输入输出数据. 使用Fiddler无论对开发还是测 ...

  10. 深入理解JWT的使用场景和优劣

    前面简单介绍了JWT的基础只是和简单的小Demo,但是对于JWT的应用场景和优缺点掌握的还够,这些东西只有自己实践过才能搞清楚其中的细节.在网上看到一个大佬对这块讲的比较好,就转载过来一起学习下. 原 ...

随机推荐

  1. 2022最新 Navicat Premium 16中文软件激活安装永久使用正版(支持MAC+win)

    Navicat Premium 16中文正版永久使用,下载地址: 关注我的wx公众号"奋斗在IT"回复1015获取下载地址

  2. Python初步了解装饰器

    Python初步了解装饰器 装饰器的概念 装饰器的简单使用 装饰器的进阶 装饰器的练习 装饰器的固定模块 装饰器的语法糖 装饰器的概念 装饰器它不是一个新的知识点,它是有之前我们学习的名称空间.函数嵌 ...

  3. Nginx map 实现时间格式转换

    哈喽大家好,我是咸鱼 最近我们需要把 Nginx 的日志接入到自研的日志采集平台上,但是这个平台只支持 JSON 格式,所以需要把 Nginx 日志格式改成 JSON 格式 例如下面这样的效果 刚开始 ...

  4. redis基本数据类型 SortedSet

    SortedSet命令练习 将班级的下列学生得分存入Redis的SortedSet中:Jack 85, Lucy 89, Rose 82, Tom 95,Jerry 78, Amy 92, Miles ...

  5. bootstrap响应式原理

    Bootstrap 框架的网格系统工作原理如下:1 .数据行 (.row) 必须包含在容器( .container )中,以便为其赋予合适的对齐方式和内距 (padding) . 如: <div ...

  6. Android dumpsys介绍

    目录 一.需求 二.环境 三.相关概念 3.1 dumpsys 3.2 Binder 3.3 管道 四.dumpsys指令的使用 4.1 dumpsys使用 4.2 dumpsys指令语法 五.详细设 ...

  7. 大语言模型基础-Transformer模型详解和训练

    一.Transformer概述 Transformer是由谷歌在17年提出并应用于神经机器翻译的seq2seq模型,其结构完全通过自注意力机制完成对源语言序列和目标语言序列的全局依赖建模. Trans ...

  8. Godot - 创建翻译文件(常量表)

    版本 Godot 3.1.2 背景 Godot的UI系统封装的很难受, 一些东西很难改动, 比如这个AcceptDialog的"确定""取消"按钮, 特别是在编 ...

  9. 空地一体化网络综述_Space-Air-Ground Integrated Network: A Survey

    摘要 空地一体化网络(SAGIN)主要解决的是单一网络下的局限性问题,此综述文章从网络设计.资源分配.到性能的优化,对近几年SAGIN的总结. 引言 受限于网络容量和覆盖范围,仅依靠地面通信系统无法在 ...

  10. Python JSON 使用指南:解析和转换数据

    JSON 是一种用于存储和交换数据的语法.JSON 是文本,使用 JavaScript 对象表示法编写. Python 中的 JSON Python 有一个内置的 json 包,可用于处理 JSON ...