首页 > 其他分享 >安卓之DocumentsProvider应用场景以及优劣分析

安卓之DocumentsProvider应用场景以及优劣分析

时间:2023-12-23 16:01:49浏览次数:37  
标签:文件 String Nullable 优劣 安卓 add DocumentsProvider public

文章摘要

本文深入探讨了安卓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 提供了一套简单易用的 API,使得开发人员可以轻松地实现文档浏览、编辑、存储等功能。

跨应用文件共享

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

不足分析

版本兼容性

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

实现复杂性

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

性能考虑

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

兼容性问题

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

缺乏对自定义文档的支持

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

代码实现(示例)

文件浏览

以下是一个简单的使用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 onl oadComplete(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为安卓应用开发者提供了一种强大而灵活的文件管理方式。通过了解其应用场景、优势与不足,并结合实际的代码实现,开发者可以更有效地利用这一工具来增强应用的文件管理功能。


标签:文件,String,Nullable,优劣,安卓,add,DocumentsProvider,public
From: https://blog.51cto.com/u_16423321/8945323

相关文章

  • 安卓之DocumentsProvider应用场景以及优劣分析
    文章摘要本文深入探讨了安卓DocumentsProvider的应用场景,分析了其优势与不足,并提供了简单的代码实现。DocumentsProvider是安卓系统中用于文件存储与访问的关键组件,为应用开发者提供了强大的文件管理能力。正文DocumentsProvider概述DocumentsProvider是安卓系统中的......
  • AC1005 安卓图案解锁
    链接:https://ac.nowcoder.com/acm/contest/20960/1005来源:牛客网题目大意:给定一串序列,判定是不是合法的Android密码.安卓图案解锁的密码有这样的一些特点:1.每个数字最多只会被使用一次。2.如果想直接连接两个数字,但是线段中会经过另一个数字,当且仅有那个数字已经在之前就......
  • MT6739_MTK6739安卓核心板_mtk核心板定制
    安卓核心板采用MTK方案,基于联发科MT6739平台开发设计,集成GPUPowerVRGE8100570MHz,搭载开放的智能Android操作系统,集成了BASEBAND、UMCP、PMU等核心器件,支持2.4G+5G双频WIFI(可支持1*1MIMO)、BLUETOOTH近距离无线传输技术,支持GNSS无线定位技术,是卓越的全球......
  • 安卓手机如何打开ics文件?ics格式文件用什么软件打开?
    什么是ics格式文件?Ics格式文件是什么呢?其实ics格式文件是一种用于保存和交换日历信息的标准格式,它通常可以保存事件的名称、时间等信息,有不少日历、待办软件在导出数据的时候,都是以ics文件导出的。有不少网友目前使用的是小米、OPPO等安卓手机,于是就问,安卓手机如何打开ics文件呢?I......
  • Android安卓打包app应用程序编译shrinkResources true报错解决方案
    Hello各位同学们好,我是咕噜铁蛋!在Android开发中,当我们需要打包应用程序时,经常会遇到一些问题。其中一个常见的问题是在使用shrinkResources设置为true时出现的编译错误。这个选项是在AndroidGradle插件的build.gradle文件中设置的,用于减少最终APK文件的大小。然而,如果设置不正确,可......
  • 安卓之技术架构优劣分析
    文章摘要安卓架构技术主要包括MVC、MVP、MVVM等。下面分别对这些架构技术进行分析优劣势,并附上代码示例。正文MVC(Model-View-Controller)架构MVC是一种常用的软件架构,它将应用程序分为三个主要组成部分:Model(模型)、View(视图)和Controller(控制器)。MVC架构可以通过将UI组件......
  • 安卓逆向工具
    源码地址https://github.com/topjohnwu/Magisk给小米/红米手机roothttps://blog.csdn.net/weixin_73636162/article/details/134043402小米手机的,Magisk面具教https://blog.csdn.net/weixin_73636162/article/details/134043402常用的安卓逆向工具安装https://github.com/c......
  • MT6785(MTK6785)_Helio G95安卓核心板规格参数
    MTK6785安卓核心板是一种工业级高性能的4G智能模块,它采用Android9.0操作系统,具备了蓝牙、FM、WLAN和GPS模块的集成。该核心板采用了两个工作频率高达2.0GHz的ARMCortex-A76内核和六个工作频率高达2.0GHz的ARMCortex-A55内核,以及强大的多标准视频编解码器。此外,它还具备广......
  • 腾讯QQ9正式发布!4年来最大更新 安卓/iOS/Windows都能下载了
    12月20日消息,今日,腾讯宣布,QQ9正式上线。距离上一次QQ8版本已经过去了4年。据官方介绍,本次版本更新,QQ9采用了全新的QQNT技术架构驱动,性能升级,交互体验更加流畅。全新界面,流畅社交。首先是UI界面全面优化,QQ启动页、登录页、消息列表页、关于页等页面UI焕彩上线。同时,聊天、设置......
  • uniapp app安卓、ios文件选择 (上传pdf word video img )等
    1、hybrid 必须放在项目根目录下,不然会调用失效:如图 2、建立nvue 子窗体  代码:1<template>2<viewclass="nvue">3<textclass="popup-item"@click="clickfun">选择文件</text>4<textclass="ddddd......