在我的应用中,我正在使用Glide处理/显示图像,并使用ExoPlayer显示视频。两者都可以直接指向文件的URI链接,并且将处理内容的流式传输,对其进行缓冲,调整其大小,处理缓存管理等。换句话说,所有的难题!
此URI的格式以及使用Android File Chooser和自定义文档提供程序时幕后发生的事情是导致此问题的关键。
由于我想要一个无缝的用户体验来选择要在应用程序中使用的图像,视频和音频(无论所需文件是移动设备本地还是Internet上的本地存储),所以我决定使用Android File Chooser。这意味着我必须为每个尚不支持SAF的在线资源(即DropBox,SnapChat,Instagram等)编写或提供一个DocumentsProvider
实例,以符合存储访问框架的要求。
尽管要花些时间才能掌握,但编写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);
接下来发生的是与该URI关联的DocumentsProvider
实例最终得到一个openDocument()
调用。因此,例如,我编写了一个自定义的DropboxProvider。从我存储在应用程序数据库中的选配器返回的URI类似于:
content://authority_string/document/XXX-YYY-ZZZ
。
将该字符串传递给Glide的load()
方法时,将调用我的DropboxProvider类的openDocument()
方法,然后在其中解析文档ID,登录到DropBox并使用它们可以在本地捕获文件并返回ParcelFileDescriptor
并包装指向该下载文件的File
对象的API。然后,调用者可以适当地解析此描述符对象,并且Glide可以正确加载它。
一切正常。
但是,令我惊讶的是,这种方法并不是最佳的几种原因,其主要原因是,尽管Glide / ExoPlayer不知道如何在DropBox上定位文件,但是一旦它们直接链接到它们所支持的提取,缓冲,流和缓存管理等可能比我做的要好得多。
在我看来,最好遵循“关注点分离”方法,而不要让我的应用程序“拥有”下载/流和缓存管理职责。只需拥有“文件标识”责任。
这真的是我的问题的关键-就谁负责什么而言,最好的“截止”线在哪里?
选择器的结果是一个content://
链接,这使我无法回避。这就是存储访问框架的构建方式。
但是我能做的是扩充我的提供程序以提前识别该直接链接,并在用户最初选择文件时将其返回到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完全处理获取这些文件的操作(不再调用我的自定义提供程序来解析获取文件的操作)。
还是我在对Glide / ExoPlayer的调用中继续使用content://
类型的URI,并研究如何创建将管道包装到实际文件并从{{1 }}?在Ian的fine article的 获取文档的要点:字节! 中,这似乎暗示了这是一种可能的方法。
但是再次,在这种情况下,这样做确实可以确证这两个工具中已经存在的工作(并且我还无法找到以这种方式使用ParcelFileDescriptor的示例)。