MediaPlayer.setDataSource(String) 不适用于本地文件

2024-05-04

如果我使用静态方法 MediaPlayer.create(context, id),我可以播放本地 mp3,但如果我使用非静态方法 MediaPlayer.setDataSource(String),它就不起作用。发生的情况是,当我调用 MediaPlayer.prepare() 时出现同步异常:

prepare exceptionjava.io.IOException: Prepare failed.: status=0x1

这是我的代码(省略日志记录):

String filename = "android.resource://" + this.getPackageName() + "/raw/test0";

mp = new MediaPlayer();
try { mp.setDataSource(filename); } catch (Exception e) {}
try { mp.prepare(); } catch (Exception e) {}
mp.start();

请注意,我没有收到有关找不到文件或任何其他内容的错误。该文件的全名是 test0.mp3,我将其放在 Eclipse 中的 /res/raw/ 目录中。

我假设我设置的路径不正确,但我在网上找到的所有示例都使用 setDataPath 的 FileDescriptor 版本而不是 setDataPath 的 String 版本。

编辑:如果我使用方法 MediaPlayer.setDataSource(FileDescriptor) 并将文件放在 Eclipse 中的 /assets/ 目录中,我也可以播放本地 mp3。

编辑#2:我接受了这是不可能的答案,但后来意识到我正在使用的库(openFrameworks)实际上确实使用 String 方法来加载文件。看这里:

https://github.com/openframeworks/openFrameworks/blob/master/addons/ofxAndroid/ofAndroidLib/src/cc/openframeworks/OFAndroidSoundPlayer.java https://github.com/openframeworks/openFrameworks/blob/master/addons/ofxAndroid/ofAndroidLib/src/cc/openframeworks/OFAndroidSoundPlayer.java


替代解决方案 #1:使用 Resources.getIdentifier()

为什么不使用 getResources().getIdentifier() 来获取资源的 id 并像往常一样使用静态 MediaPlayer.create() ?

public int getIdentifier (String name, String defType, String defPackage)

获取标识符() http://developer.android.com/reference/android/content/res/Resources.html#getIdentifier(java.lang.String,%20java.lang.String,%20java.lang.String)获取您的资源名称 (test0)、资源类型(raw)、您的包名称并返回实际的资源 ID。

 MediaPlayer mp;
 //String filename = "android.resource://" + this.getPackageName() + "/raw/test0";
 mp=MediaPlayer.create(getApplicationContext(), getResources().getIdentifier("test0","raw",getPackageName()));
 mp.start();

我已经测试了这段代码并且它有效。


Update #1:

替代解决方案#2:使用 Uri.parse()

我也测试了这段代码并且它也有效。将您的资源路径作为 URI 传递给 setDataSource()。我刚刚对您的代码进行了更改以使其正常工作。

String filename = "android.resource://" + this.getPackageName() + "/raw/test0";

mp = new MediaPlayer();
try { mp.setDataSource(this,Uri.parse(filename)); } catch (Exception e) {}
try { mp.prepare(); } catch (Exception e) {}
mp.start();

Update #2: Answer is NO

关于 setDataSource(String) 调用

看到您的评论后,看来您确实希望将 setDataSource(string) 用于您的目的。我不明白为什么。但是,我认为,出于某种原因,您试图避免使用“上下文”。如果情况并非如此,那么上述两个解决方案应该非常适合您,或者如果您试图避免上下文,恐怕使用签名 setDataSource(String) 调用的函数是不可能的。原因如下,

MediaPlayer setDataSource() 函数具有以下选项,您只对 setDataSource(String) 感兴趣,

setDataSource(String) 内部调用 setDataSource(String path, String[]keys, String[]values) 函数。如果你可以检查它source https://github.com/android/platform_frameworks_base/blob/master/media/java/android/media/MediaPlayer.java,

public void setDataSource(String path)
            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
        setDataSource(path, null, null);
    }

如果您检查 setDataSource(String path, String[]keys, String[]values) 代码,您将看到以下条件根据其方案过滤路径,特别是如果它是“文件”方案,它会调用 setDataSource(FileDescriptor) 或如果方案不是“文件”,则调用本机 JNI 媒体函数。

{
        final Uri uri = Uri.parse(path);
        final String scheme = uri.getScheme();
        if ("file".equals(scheme)) {
            path = uri.getPath();
        } else if (scheme != null) {
            // handle non-file sources
            nativeSetDataSource(
                MediaHTTPService.createHttpServiceBinderIfNecessary(path),
                path,
                keys,
                values);
            return;
        }
        final File file = new File(path);
        if (file.exists()) {
            FileInputStream is = new FileInputStream(file);
            FileDescriptor fd = is.getFD();
            setDataSource(fd);
            is.close();
        } else {
            throw new IOException("setDataSource failed.");
        }
}

在上面的代码中,您的资源文件 URI 方案将不为 null (android.resource://) 并且setDataSource(String) 将尝试使用本机 JNI 函数 nativeSetDataSource() ,认为您的路径是 http/https/rtsp ,显然该调用也会失败而不会引发任何异常。这就是为什么您对 setDataSource(String) 的调用会毫无异常地转义,并且会在出现以下异常的情况下调用prepare() 。

Prepare failed.: status=0x1

因此 setDataSource(String) 重写无法处理您的资源文件。您需要为此选择另一个覆盖。

另一方面,检查 setDataSource(Context context, Uri uri, Map headers) 使用的 setDataSource(Context context, Uri uri),它使用上下文中的 AssetFileDescriptor、ContentResolver 和 openAssetFileDescriptor 打开 URI,该 URI 成功作为 openAssetFileDescriptor( )可以打开您的资源文件,最后生成的 fd 用于调用 setDataSource(FileDescriptor) 覆盖。

    AssetFileDescriptor fd = null;
    try {
        ContentResolver resolver = context.getContentResolver();
        fd = resolver.openAssetFileDescriptor(uri, "r");
        //  :
        //  :
        //  :
        if (fd.getDeclaredLength() < 0) {
                setDataSource(fd.getFileDescriptor());
            } else {
                setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getDeclaredLength());
            }

总之,您不能按原样使用 setDataSource(String) 覆盖来使用资源 mp3 文件。相反,如果您想使用字符串来播放资源文件,您可以使用上面给出的 MediaPlayer.create() 静态函数和 getIdentifier() 或 Update#1 中给出的 setDataSource(context,uri) 。

请参阅此处的完整源代码以进行更深入的理解:Android 媒体播放器 https://github.com/android/platform_frameworks_base/blob/master/media/java/android/media/MediaPlayer.java


Update #3:

openFrameworks setDataSource(String):

正如我在下面的评论中提到的,openFrameworks 使用 android MediaPlayer 代码 asis。如果您可以参考第4行,

import android.media.MediaPlayer;

线路号:26、27、28 和 218

        player = new MediaPlayer();       //26
        player.setDataSource(fileName);   //27
        player.prepare();                 //28

        private MediaPlayer player;       //218

所以,如果你尝试通过android.resource//+ this.getPackageName() + "raw/test0"使用 openFrameworks setDataSource(),您仍然会得到与我在 Update#2 中解释的相同的异常。话虽如此,我只是想试试运气,在谷歌上搜索一下,以双重确定我所说的内容,然后发现了这个openFrameworks 论坛链接 http://forum.openframeworks.cc/t/soundfiles-on-android/6220/9其中之一openFrameworks 核心开发人员 arturo says,

不知道 mediaPlayer 是如何工作的,但所有内容都在 res/raw 中 或 bin/data 被复制到 /sdcard/cc.openframeworks.packagename

根据该评论,您可以尝试在 setDataSource() 中使用复制的路径。无法在 MediaPlayer 的 setDataSource(String) 上使用资源文件,因为它无法接受资源文件路径。请注意,我说的“资源文件路径”以方案开头android.resource//这实际上是一个 jar 位置(在你的 apk 中),而不是物理位置。本地文件将与以方案开头的 setDataSource(String) 一起使用file://.

为了让您清楚地了解要对资源文件执行的操作,请尝试执行下面的代码并在 logcat 中查看结果,

    try{
      Log.d("RESURI", this.getClass().getClassLoader().getResource("res/raw/test0").toURI().toString());
    } 
    catch(Exception e) {

    }

你会得到如下结果:

jar:file:/data/app/<packagename>/<apkname>.apk!/res/raw/test0

这是为了向您表明您尝试访问的资源文件实际上并不是物理路径中的文件,而是您无法使用 setDataSource(String) 方法访问的 jar 位置(在 apk 内)。 (尝试使用 7zip 提取您的 apk 文件,您将在其中看到 res/raw/test0)。

希望有帮助。

PS:我知道它的答案有点冗长,但我希望这能详细解释它。如果可以帮助其他人,请将替代解决方案留在顶部。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MediaPlayer.setDataSource(String) 不适用于本地文件 的相关文章

  • Android 消费品:“已经拥有该商品”,但 inventory.hasPurchase() 为 false

    我被 Google In App v3 困住了 我测试了一次没有消费的购买 例如 当应用程序在购买和消费之间崩溃时 现在我找不到出路 如果我尝试再次购买 它会显示 您已经拥有该商品 但是当我测试所有权时 它说我不拥有它 Inventory
  • java 中的 Try-with-resources 和 return 语句

    我想知道是否放一个return里面的声明尝试资源block 防止资源自动关闭 try Connection conn return conn createStatement execute 如果我写这样的东西将会联系被关闭 Oracle 文
  • 使用java在网页中进行字符编码

    如何使用java找出网页中的字符编码类型 打开与 URL 的连接 使用URL openConnection http download oracle com javase 6 docs api java net URL html openC
  • 开发人员实际上是否使用 vim 在 Windows 操作系统上编写代码(Java)? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 如何在Android网格视图中设置单元格大小?

    我正在尝试为应用程序制作一个带有大图标的网格视图 但我找不到任何有关修改 Android 上网格布局上的单元格大小的教程 有人可以给我一个例子或相关链接吗 Thanks 就像另一个一样适配器视图 http developer android
  • EJB 中 @Stateless 相对于 @Singleton 的真正用例是什么

    如果我正确理解EJB Singleton实际上与普通Java中的Singleton相同 也是spring中的单例 gt 一个实例 每个调用同时通过同一个实例 Stateless 声明一个 bean 它可以 但不得 具有多个实例 但限制是一个
  • 如何从 Trie 中检索给定长度的随机单词

    我有一个简单的 Trie 用来存储大约 80k 长度为 2 15 的单词 它非常适合检查字符串是否是单词 但是 现在我需要一种获取给定长度的随机单词的方法 换句话说 我需要 getRandomWord 5 来返回 5 个字母的单词 所有 5
  • 如何更改 Android 12 启动屏幕中的图标形状?

    我想要矩形形状的启动屏幕图标 而不是 android 12 中的圆形形状 我不相信你可以 如果你看这里的第 3 点 https developer android com about versions 12 features splash
  • 使用后退按钮启动 Activity

    我正在 Android 中开发一个应用程序 我正在寻找解决方案 有一个活动 例如 A1 通过单击按钮 用户可以转到另一个活动 例如 A2 现在 一旦用户完成 A2 活动 他就会单击后退按钮 返回到上一个活动 A1 这是众所周知的事实 A1此
  • Java 常量枚举[重复]

    这个问题在这里已经有答案了 可能的重复 理解 Java 中的枚举 https stackoverflow com questions 1419835 understanding enums in java 为什么我们应该使用枚举而不是 Ja
  • SDK尚未初始化,请务必先调用FacebookSdk.sdkInitialize()

    我在实现 Facebook SDK 时遇到此错误 并且我tried https stackoverflow com questions 15490399 error inflating class com facebook widget l
  • 如何在 onDraw() 方法中定义与像素无关的高度

    我扩展了 View 来构建自定义小部件 我想用独立的像素单位定义小部件的高度 我认为可以通过将像素密度乘以所需的高度来完成 但我不知道该怎么做 到目前为止我所拥有的 最小化 public class Timeline extends Vie
  • Android复杂布局线性和相对

    I have to implement a layout like shown in the diagram and I do not know the best combination to achieve the required de
  • 测量 tomcat 的排队请求数

    因此 使用tomcat 您可以设置acceptCount值 默认为100 这意味着当所有工作线程都忙时 新连接被放置在队列中 直到队列满 之后它们被拒绝 我想要的是监视此队列中项目的大小 但无法确定是否有办法通过 JMX 获取此值 即不是队
  • 一个类中有多个具有相同参数类型的方法

    我知道 至少已经有了关于这个主题的一个问题 https stackoverflow com questions 5561436 can two java methods have same name with different retur
  • 改造方法调用可能会产生“java.lang.NullPointerException”

    使用 Retrofit 2 3 0 我在 Android Studio 中收到以下消息 有关如何删除此 IDE 错误消息的任何建议 谢谢 来自Response文档 http square github io retrofit 2 x ret
  • 如何从灰度字节缓冲区图像创建位图?

    我正在尝试使用新的 Android 人脸检测移动视觉 API 来处理帧图像 所以我创建了自定义检测器来获取帧并尝试调用 getBitmap 方法 但它为空 所以我访问了帧的灰度数据 有没有办法从它或类似的图像持有者类创建位图 public
  • Application.onLowMemory() 未调用

    我创建了自己的应用程序类 我尝试调试它 代码在 Application onCreate 处停止 但不会在 onLowMemory 处停止 为了测试该场景 我打开了许多其他高内存应用程序 我看到的是调试会话终止 在 Eclipse 中 并且
  • 膨胀类 android.support.design.widget.CoordinatorLayoute 时出错

    我正在尝试运行我的应用程序 但不断收到标题中列出的错误 我读过周围的内容 人们说尝试将主题更改为 AppCombat 主题 但这似乎不起作用 以下是我遇到的错误 Process com example jmeyer27 crazytiles
  • Swing:创建可拖动组件...?

    我在网上搜索了可拖动 Swing 组件的示例 但我发现示例不完整或不起作用 我需要的是一个摇摆组件那可以是dragged通过鼠标 在另一个组件内 被拖拽的时候 应该已经 改变它的位置 而不仅仅是 跳 到目的地 我很欣赏无需非标准 API 即

随机推荐