首页 > 解决方案 > 我的自定义 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 的示例)。

标签: androidstreaminguriandroid-contentprovider

解决方案


推荐阅读