如何在牛轧糖中为 ACTION_VIEW 使用内容 URI?

SVM*_*SVM 5 android uri android-fileprovider android-7.0-nougat

我有一个生成视频文件的应用程序,它可以放在内部存储或 SD 卡中,需要通过 Intent.ACTION_VIEW 由用户首选的视频播放器应用程序打开。该应用程序在面向 API 22 时按预期工作,但是当我尝试将其升级到 27 时遇到了问题。

该应用程序根据存储位置生成不同类型的 URI。对于内部存储,它使用文件 URI,如下所示:

file:///storage/emulated/0/VideoRecorder/2018_06_22_12_25_50.mp4
Run Code Online (Sandbox Code Playgroud)

对于放置在 SD 卡中的视频,它使用内容 URI:

content://com.android.externalstorage.documents/tree/72D1-C625%3Avideostest%2Ftest2/document/72D1-C625%3Avideostest%2Ftest2%2F2018_06_22_12_41_27.mp4
Run Code Online (Sandbox Code Playgroud)

Nougat 不喜欢 file:// URI,所以我使用 FileProvider(下面的代码)来解决这个问题,将文件 URI 转换为其他应用程序可以使用 ACTION_VIEW 打开的内容 URI。我认为 SD 卡 URI 不需要任何更改,因为它已经是内容 URI,但似乎只有默认照片应用程序可以使用 ACTION_VIEW 意图打开该 URI,而用户安装的应用程序(如 VLC Player)则不能,失败,但出现以下异常:

java.lang.SecurityException: Permission Denial: reading com.android.externalstorage.ExternalStorageProvider uri content://com.android.externalstorage.documents/tree/72D1-C625:videostest/test2/document/72D1-C625:videostest/test2/2018_06_22_12_27_45.mp4 from pid=7809, uid=10022 requires android.permission.MANAGE_DOCUMENTS, or grantUriPermission()
Run Code Online (Sandbox Code Playgroud)

检查了 FileProvider 生成的 URI 后,我想我明白了这个问题。它生成的内容 URI 的格式与我用于 SD 卡视频的内容 URI 的格式完全不同:

content://io.github.androidtests.videorecorder.videosfileprovider/external_files/VideoRecorder/2018_06_22_12_25_50.mp4
Run Code Online (Sandbox Code Playgroud)

我的问题是,SD 卡视频是否也需要通过 FileProvider 共享?我该怎么做,因为我只有内容 URI,没有文件,而且 FileProvider 似乎专门设计用于将文件转换为 URI?

编辑:在测试了更多视频播放器应用程序之后,其中一些似乎能够使用内容 URI,就像照片应用程序一样。这只是一些应用程序不支持内容 URI 的情况吗?

代码:

Uri uri = adapter.mDataset.get(position).videoUri;

if (uri.toString().contains("file://")) {
    try {
        ParcelFileDescriptor pFD = ctx.getContentResolver().openFileDescriptor(uri, "r");
        String truePath = Utils.getFdPath(pFD);
        uri = FileProvider.getUriForFile(ctx, "io.github.androidtests.videorecorder.videosfileprovider", new File(truePath));
    } catch (FileNotFoundException e) {
        StaticMethodsAndValues.launchFabricException("RecyclerViewFragment caught FileNotFoundException when opening video.", e);
        return;
    }
}

Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.setDataAndType(uri, "video/mp4");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
try {
    ctx.startActivity(intent);
}
catch (ActivityNotFoundException e) {
    Toast.makeText(mContext.get(), mContext.get().getString(R.string.toast_no_video_player_found), Toast.LENGTH_LONG).show();
    StaticMethodsAndValues.launchFabricException("Device does not have app to handle intent action.VIEW for video/mp4.", e);
}
Run Code Online (Sandbox Code Playgroud)

提供者清单定义:

<provider
    android:name=".VideosFileProvider"
    android:authorities="io.github.androidtests.videorecorder.videosfileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/provider_paths"/>
</provider>
Run Code Online (Sandbox Code Playgroud)

提供者路径:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>
Run Code Online (Sandbox Code Playgroud)