android - 我的自定义 DocumentsProvider 是否应该使用 ParcelFileDescriptor 流式传输所选文件?
问题描述
在我的应用程序中,我使用Glide处理/显示图像,使用ExoPlayer显示视频。两者都可以直接获取文件的 URI 链接,并处理流式传输内容、缓冲内容、调整大小、处理缓存管理等。换句话说,所有困难的事情!
此 URI 的格式以及在使用 Android 文件选择器和自定义文档提供程序时会发生什么是这里问题的关键。
因为我想要一个无缝的用户体验来选择应用程序中使用的图像、视频和音频,无论所需的文件是移动设备本地还是互联网上的文件,我决定使用 Android 文件选择器。这意味着我必须DocumentsProvider
为每个不支持 SAF 的在线资源(即 DropBox、SnapChat、Instagram 等)编写或以其他方式提供与存储访问框架保持一致的实例。
尽管需要一点时间来掌握它,但编写 DocumentsProvider 类并不太难。
问题始于这样一个事实,即从选择器返回的 URI(因此我存储为持久 URI 以供我的应用程序以后使用)是content://
多种多样的 - 它不是 Glide/ExoPlayer 通常工作的http://
或https://
类型和。
例如,假设content://
字符串的类型最终用作.load()
以下方法的参数:
GlideApp.with(context)
.load(Uri.parse(media.getPathToMedia()))
.apply(RequestOptions.fitCenterTransform())
.placeholder(placeHolder)
.error(R.drawable.ic_image_error)
.into(imageView);
接下来发生的是DocumentsProvider
与该 URI 关联的实例最终获得openDocument()
调用。例如,我编写了一个自定义的 DropboxProvider。从我存储在应用程序数据库中的选择器返回的 URI 类似于:
content://authority_string/document/XXX-YYY-ZZZ
.
当将该字符串传递给load()
Glide 的方法时,我openDocument()
的 DropboxProvider 类方法被调用,在那里我解析出文档 ID,登录到 DropBox,并使用他们的 API 在本地获取文件并返回一个ParcelFileDescriptor
包装File
对象指向下载的文件。然后调用者可以适当地解析这个描述符对象并且 Glide 会正确地加载它。
这一切都很好。
然而,令我震惊的是,这种方法并不是最优的,原因有几个,其中最主要的原因是,虽然 Glide/ExoPlayer 不知道如何在 DropBox 上定位文件,但一旦它们有直接链接,它们就支持获取和缓冲、流媒体和缓存管理等可能比我做的要好得多。
在我看来,最好遵循“关注点分离”的方法,而不是让我的应用程序“拥有”下载/流和缓存管理职责。只是让它自己承担“文件识别”的责任。
所以这真的是我问题的症结所在——就谁负责什么而言,这里最好的“切断”线在哪里?
我无法摆脱选择器的结果是content://
链接的事实。这就是存储访问框架的构建方式。
但我可以做的是增加我的 Provider 以提前识别该直接链接,并在用户最初选择文件时将其返回到 Cursor 中。然后在完成文件选择后,使用标准 Resolver() 方法将该信息捕获到我的Media
对象中,然后我将其保存到我的数据库中:
public static void populateMediaFromUri(Context context, Media media, Uri uri) {
*
*
*
String path = null;
cursor = context.getContentResolver().query(uri, null, null, null, null);
try {
if ( cursor != null && cursor.moveToFirst() ) {
String directLink = cursor.getString(cursor.getColumnIndex(AbstractStorageProvider.COLUMN_DIRECT_LINK));
if ( directLink == null ) {
path = uri.toString();
} else {
path = directLink;
}
*
*
*
media.setPathToMedia(path);
}
} catch (Exception e ) {
Timber.d("Error resolving to path, requested was %s, error was %s", uri.getPath(), e.getMessage());
} finally {
if ( cursor != null ) {
cursor.close();
}
}
然后我可以稍后使用该直接链接路径传递给 Glide 或 ExoPlayer,而不是content://
我现在从文件选择器返回的基于 URI。这将允许 Glide/ExoPlayer 完全处理获取这些文件(将不再调用我的自定义提供程序来解析获取文件)。
或者我是否继续content://
在对 Glide/ExoPlayer 的调用中使用类型 URI 并研究如何创建一个ParcelFileDescriptor
将管道包装到实际文件并从中返回的openDocument()
?这似乎在 Ian 的文章深入了解文档的内容:字节!.
但同样,在这种情况下,这样做似乎确实是在重复这两个工具中已经存在的工作(而且我还没有找到以这种方式使用 ParcelFileDescriptor 的示例)。
解决方案
推荐阅读
- lua - 快速查找数组中的对象
- powershell - 有条件地替换文件 PowerShell 中的字符串
- reactjs - Ant design 4.0.1 - 从父组件访问类组件内子窗体的方法
- c++ - 如何模拟静态类模板专业化的构造函数?
- node.js - Swagger NodeJS 存根 + 现有
- javascript - 如何使一小部分div可点击
- ios - 带 AuGraph 的变速单元 [iOS Swift]
- swift - SwiftUI - 使用自定义绘图包装 UIView 时出现 UIViewRepresentable 故障
- html - 我试过调用我的 CSS 动画,但它不起作用
- c# - 添加 System.Drawing.Common 引用