安卓之DocumentsProvider应用场景以及优劣分析
文章摘要
本文深入探讨了安卓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应用场景以及优劣分析的更多相关文章
- 理解JWT的使用场景和优劣
理解JWT的使用场景和优劣 淘楼小能手 百家号04-2816:20 经过前面两篇文章<JSON Web Token - 在Web应用间安全地传递信息><八幅漫画理解使用JSON We ...
- spark和strom优劣分析
对于Storm来说:1.建议在那种需要纯实时,不能忍受1秒以上延迟的场景下使用,比如实时金融系统,要求纯实时进行金融交易和分析2.此外,如果对于实时计算的功能中,要求可靠的事务机制和可靠性机制,即数据 ...
- Java 集合系列08之 List总结(LinkedList, ArrayList等使用场景和性能分析)
概要 前面,我们学完了List的全部内容(ArrayList, LinkedList, Vector, Stack). Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例 Ja ...
- Java 集合系列 07 List总结(LinkedList, ArrayList等使用场景和性能分析)
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- 【性能诊断】五、并发场景的性能分析(windbg简介及dump抓取)
windbg简介 Windbg是在windows平台下,强大的用户态和内核态调试工具.相比较于Visual Studio,它是一个轻量级的调试工具,所谓轻量级指的是它的安装文件大小较小,但是其调试功能 ...
- Java集合系列:-----------06List的总结(LinkedList,ArrayList等使用场景和性能分析)
现在,我们再回头看看总结一下List.内容包括:第1部分 List概括第2部分 List使用场景第3部分 LinkedList和ArrayList性能差异分析第4部分 Vector和ArrayList ...
- 小白学习mysql之存储过程的优劣分析以及接入控制
存储过程的优劣 存储过程是一组实现特定功能的SQL语句集合,存储过程一经编译便存储在了服务器上,可以通过调用存储过程的名字以及传入相应的参数来使用存储过程.要高层次的掌握存储过程,不能觉得依葫芦画瓢, ...
- WCF技术的不同应用场景及其实现分析
这一篇文章,是总结一下WCF技术,以及基于这个技术发展出来的几个典型应用场景,并且我将尝试对这些不同的WCF实现的原理进行一些比较分析. 关于WCF这个技术的基本概念,如果你不是很清楚,可以参考一下有 ...
- 【性能诊断】二、单功能场景的性能分析(fiddler、SQL Profiler)
Fiddler fiddler是最强大最好用的Web调试工具之一,它能记录所有客户端和服务器的http和https请求,允许你监视,设置断点,甚至修改输入输出数据. 使用Fiddler无论对开发还是测 ...
- 深入理解JWT的使用场景和优劣
前面简单介绍了JWT的基础只是和简单的小Demo,但是对于JWT的应用场景和优缺点掌握的还够,这些东西只有自己实践过才能搞清楚其中的细节.在网上看到一个大佬对这块讲的比较好,就转载过来一起学习下. 原 ...
随机推荐
- C#/.NET/.NET Core优秀项目和框架8月简报
前言 公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(公众号每周至少推荐两个优秀的项目和框架当然节假日除外),公众号推文有项目和框架的介绍.功能特点以及部分截图等(打不开或 ...
- 发现了一个可以免费下载jar包的网站,所有jar包都有
苦苦找不到项目所需要的jar包?发现了一个可以免费下载jar包的网站: https://jar-download.com/ 非常好用. 每个JAR文件都将从官方Maven存储库下载.通过下载所有Mav ...
- Solution -「洛谷 P5659」「CSP-S 2019」树上的数
Description Link. 联赛原题应该都读过吧-- Solution Part 0 大致思路 主要的思路就是逐个打破,研究特殊的数据得到普通的结论. Part 1 暴力的部分分 暴力的部分分 ...
- Solution -「洛谷 P3267」「JLOI 2016」「SHOI 2016」侦察守卫
Description Link. 给你一棵树,放置守卫在某个点上面需要一定代价和一定的有效范围.让你覆盖若干指定点,求最小代价 Solution 算法标签: $\ \ \ \ \ \ \ \ \ $ ...
- Go语言常用标准库——flag
文章目录 os.Args flag包基本使用 导入flag包 flag参数类型 定义命令行flag参数 flag.Type() flag.TypeVar() flag.Parse() flag其他函数 ...
- 程序员必备:使用AI工具通义千问,没有广告没有假链接,比搜索引擎要便捷多了,回答还算满意
我提的问题是:使用Python连接Oracle,cx_Oracle和oracledb那个效率更高,功能更全,请列出他们的区别和实例代码 回复是: 以下是使用Python连接Oracle时,cx_Ora ...
- PolarD&N2023秋季个人挑战—Crypto全解
EasyRSA (额..这个题看错了不是挑战赛的.这里当做好题记录下来了) 题目信息:500 分 5 Polar币 from Crypto.Util.number import bytes_to_lo ...
- 掌握Spring事件监听器的内部逻辑与实现
本文分享自华为云社区<Spring高手之路15--掌握Spring事件监听器的内部逻辑与实现>,作者:砖业洋__ . 深入探索Spring的事件处理机制,从事件的层次传播.PayloadA ...
- 【scipy 基础】--聚类
物以类聚,聚类算法使用最优化的算法来计算数据点之间的距离,并将它们分组到最近的簇中. Scipy的聚类模块中,进一步分为两个聚类子模块: vq(vector quantization):提供了一种基于 ...
- 线程的查看方式&运行原理
观察多个线程同时运行 主要是理解 交替执行 谁先谁后,不由我们控制 查看进程线程的方法 windows 任务管理器可以查看进程和线程数,也可以用来杀死进程 tasklist 查看进程 taskkill ...