替代方案 #1:使用Resources.getIdentifier()
为什么不使用getResources().getIdentifier()来获取资源的id,然后像往常一样使用静态的MediaPlayer.create()呢?
public int getIdentifier (String name, String defType, String defPackage)
getIdentifier() 方法可以将你的资源名称(test0),资源类型(raw)和包名作为参数,返回实际的资源id。
MediaPlayer mp;
mp=MediaPlayer.create(getApplicationContext(), getResources().getIdentifier("test0","raw",getPackageName()));
mp.start();
我已经测试了这段代码,它是可行的。
更新 #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();
更新 #2:答案是否定的
关于 setDataSource(String) 调用
看到您的评论后,似乎您确切地希望使用 setDataSource(string) 来实现您的目的。我不明白为什么。但是,我认为您是因为某种原因想要避免使用 "context"。如果不是这种情况,那么上述两个解决方案应该完全适合您,或者如果您正在尝试避免 context,则恐怕无法使用具有签名 setDataSource(String) 的函数。原因如下:
MediaPlayer setDataSource() 函数具有以下选项,其中您只对 setDataSource(String) 感兴趣:
![setDataSource Functions](https://istack.dev59.com/fkYMA.webp)
setDataSource(String) 在内部调用 setDataSource(String path, String[] keys, String[] values) 函数。如果您可以检查其源代码,
public void setDataSource(String path)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
setDataSource(path, null, null);
}
如果您检查setDataSource(String path, String[] keys, String[] values)代码,您将看到以下条件根据其方案过滤路径,特别是如果它是“file”方案,则调用setDataSource(FileDescriptor),如果方案不是“file”,则调用本机JNI媒体函数。
{
final Uri uri = Uri.parse(path);
final String scheme = uri.getScheme();
if ("file".equals(scheme)) {
path = uri.getPath();
} else if (scheme != null) {
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。如果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 MediaPlayer
更新 #3:
openFrameworks setDataSource(String):
如我在下面的评论中所述,openFrameworks使用android MediaPlayer代码,就是原样使用。如果您能参考第4行:
import android.media.MediaPlayer;
在第26、27、28和218行
player = new MediaPlayer()
player.setDataSource(fileName)
player.prepare()
private MediaPlayer player
如果您尝试在openFrameworks中使用setDataSource()传递ardroid.resource//+ this.getPackageName() + "raw/test0",您仍将得到与我在更新#2中解释的相同异常。话虽如此,我刚刚尝试了一下通过谷歌搜索来确保我的说法,并找到了这个openFrameworks论坛链接,其中一个核心开发者arturo说:
不知道mediaPlayer的工作原理,但是res/raw或bin/data中的所有内容都会被复制到/sdcard/cc.openframeworks.packagename。
基于这个评论,您可以尝试在setDataSource()中使用复制后的路径。在MediaPlayer的setDataSource(String)中使用资源文件是不可能的,因为它无法接受资源文件路径。请注意,“资源文件路径”以方案android.resource//开头,实际上是一个jar位置(在您的apk内部),而不是物理位置。以file://开头的本地文件可以在setDataSource(String)中正常工作。
为了让您清楚地了解您正在尝试做什么,请执行以下代码并在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
这是为了向您展示,您尝试访问的资源文件实际上不是物理路径中的文件,而是一个jar位置(在apk内部),您无法使用setDataSource(String)方法来访问它。(尝试使用7zip提取您的apk文件,您将在其中看到res/raw/test0)。
希望有所帮助。
PS: 我知道这是一个有点冗长的答案,但我希望这样可以详细解释。如果这能帮助其他人,就把替代方案放在前面。