Android - 将URI转换为Lollipop上的文件路径

3

我目前正在尝试制作一个音频媒体播放器,我的设备正在运行lollipop系统。在设置媒体播放器的数据源时遇到了问题。以下是我设置数据源的方法:

public void playSong() {
    player.reset();
    Song selSong = songs.get(songPos);
    long currSong = selSong.getId();
    //Get Uri of song
    Uri trackUri = ContentUris.withAppendedId(
            MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, currSong);
    try {
        //Tell the player the song to play
        player.setDataSource(getApplicationContext(), trackUri);
    } catch (Exception e) {
        Log.e("MUSIC SERVICE", "Error setting data source", e);
        Log.d("URI Path", trackUri.toString());
    }

    //Will prepare the song and call onPrepare of player
    player.prepareAsync();
}

最终的 Uri 是这样的:

content://media/external/audio/media/22

我做了一些研究,据我理解,在 Android 4.1 之后,你不能再使用 URI 作为数据源。当我运行上述代码的应用程序时,会得到以下错误:

E/MediaPlayer﹕ Unable to create media player
E/MUSIC SERVICE﹕ Error setting data source
    java.io.IOException: setDataSource failed.: status=0x80000000
            at android.media.MediaPlayer.nativeSetDataSource(Native Method)

现在我需要将URI转换为文件路径并将其作为数据源提供。经过更多研究,似乎kitkat改变了提供URI的方式,因此很难从URI获取文件路径。但是,我不确定这个变化是否持续到Android Lollipop 5.0.2。基本上,我有歌曲的URI,但我需要提供除URI之外的东西作为数据源。在Lollipop上有没有办法转换URI?如果没有,只知道歌曲ID又该如何提供数据源?谢谢。

1
我做了一些研究,据我所知,在Android 4.1之后,您不能再使用URI作为数据源。请引用相关资料。如果有的话,恰恰相反——您不能假设文件路径可行。当我使用上述代码运行此应用程序时,会出现以下错误——如果您在Android 4.4+上进行测试,请添加“READ_EXTERNAL_STORAGE”权限。似乎KitKat改变了提供URI的方式,因此很难从URI获取文件路径——没有要求Uri是一个文件,更不用说您可以访问它了。 - CommonsWare
好的,我会看看能否找到我找到这个声明的地方。因为我的URI无法工作,所以我只是假设它是正确的。我确实有READ_EXTERNAL_STORAGE权限和INTERNET权限。 - josephoneill
这是我读到URI无法工作的地方。我可能误解了。他说在Android 4.1.1上,如果使用传递字符串的构造函数,则需要使用本地路径。https://dev59.com/omQo5IYBdhLWcg3wBLYo#22152628 - josephoneill
这仅适用于接受String参数的setDataSource()。如文档所述,它需要“文件路径或要播放的http / rtsp流的URL”。接受Uri值的setDataSource()版本应该可以正常使用content:// Uri值。无论您的特定Uri是否有效,因为您自己从各个部分组装它,这是另一回事。 - CommonsWare
抱歉,我没有注意到构造函数只接受一个字符串。那么我想我的URI是不正确的,因此无法设置数据源。谢谢。 - josephoneill
是的,我是个白痴。我把专辑ID放到了歌曲ID里。这样就解决了。再次感谢。 - josephoneill
1个回答

0

Lollipop决定采取另一种方式从系统中获取文件。(有人说这是从KitKat开始的,但我在KitKat上还没有遇到过它)。下面的代码是用于在Lollipop上获取文件路径。

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && isMediaDocument(uri))
    {
        final String docId = DocumentsContract.getDocumentId(uri);
        final String[] split = docId.split(":");
        final String type = split[0];

        Uri contentUri = null;
        if ("audio".equals(type)) 
        {
            contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
        }

        final String selection = "_id=?";
        final String[] selectionArgs = new String[] {
                split[1]
        };

        String filePath = getDataColumn(context, contentUri, selection, selectionArgs);
    }

isMediaDocument:

public static boolean isMediaDocument(Uri uri)
{
    return "com.android.providers.media.documents".equals(uri.getAuthority());
}

getDataColumn:

private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs)
{
    Cursor cursor = null;
    final String column = "_data";
    final String[] projection = {
            column
    };

    try {
        cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
        if (cursor != null && cursor.moveToFirst())
        {
            final int column_index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(column_index);
        }
    } finally {
        if (cursor != null)
            cursor.close();
    }
    return null;
}

如果您仍然遇到问题,this 是完整的答案,可以检查图像、音频、视频、文件等。


您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - android_student
@hhoang 嗯,这很奇怪,如果我这周有时间,我会寻找答案。 - Kevin van Mierlo
只是为了澄清,当发送的内容uri实际上没有存储在设备上时,似乎_data将为空。例如,如果我尝试从存储在云中的Google照片应用程序共享照片。_data列将为空。我找到的唯一解决方法是复制文件,然后将新文件的绝对路径传回我的活动。但是,我不确定这是否是可用的最佳方法。我在这里写了一个SO问题,如果您可以看一下。https://dev59.com/LInca4cB1Zd3GeqP8kRW - android_student
@hhoang 我会在有时间的时候研究一下,我大多数时候都是在工作期间发布答案,因为我需要它。我现在非常忙。但是一个提示:也许你可以检查一下你得到的 uri 是否以 http 或其他什么开头,然后发出下载请求来下载它。 - Kevin van Mierlo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接