在后台服务中加载资产文件
Load asset files inside background service
我正在开发一个播放随机声音的小部件。我的问题在于获取我用来处理 MediaPlayer
和播放声音以加载存储在应用程序资产文件夹中的声音文件的服务。
我选择了 MediaPlayer
,而不是我的应用程序中的 SoundPool
,因为它有一个 onCompletionListener
,它允许我在声音完全播放后停止服务。那样我希望尽量减少小部件的资源使用。
更准确地说,在我的代码中 (checkout the [now outdated] commit on GitHub) I try to load a random sound with the MediaPlayer
's create()
convenience method using an Uri
to file:///android_asset/pathToFile
.
然而,这会在运行时抛出 IOException。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_PLAY_FART.equals(action)) {
Log.v(LOG_TAG, "Intent received");
String pathToFile = String.format(
Locale.US,
"fart%02d.ogg",
Utility.getIntBetween(1, 15)
);
MediaPlayer tempMediaPlayer = MediaPlayer.create(
this,
Uri.parse("file:///android_asset/"+pathToFile)
);
tempMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mp.release();
stopSelf();
}
});
Log.v(LOG_TAG, "Start playing");
tempMediaPlayer.start();
}
}
return START_NOT_STICKY;
}
是否有一种简单的方法(即除了内容提供商之外)从 加载 那些资产文件=]Service
?
[编辑,提供更多信息]
显然可以访问资产文件,但 MediaPlayer
无法正确加载它们。
我将代码修改为:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_PLAY_FART.equals(action)) {
Log.v(LOG_TAG, "Intent received");
String pathToFile = String.format(
Locale.US,
"fart%02d.ogg",
Utility.getIntBetween(1, 15)
);
try {
AssetFileDescriptor assetFD = this.getAssets().openFd(pathToFile);
MediaPlayer tempMediaPlayer = new MediaPlayer();
tempMediaPlayer.setDataSource(assetFD.getFileDescriptor());
tempMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mp.release();
stopSelf();
}
});
Log.v(LOG_TAG, "Start playing");
tempMediaPlayer.start();
} catch (IOException e) {
Log.e(LOG_TAG, "File "+pathToFile+" could not be loaded.", e);
}
}
}
我得到的输出是:
08-29 15:29:54.868 10191-10191/com.y0hy0h.furzknopf V/VolatileFartService﹕ Intent received
08-29 15:29:54.868 10191-10191/com.y0hy0h.furzknopf V/MediaPlayer﹕ constructor
08-29 15:29:54.868 10191-10191/com.y0hy0h.furzknopf V/MediaPlayer﹕ setListener
08-29 15:29:54.868 10191-10191/com.y0hy0h.furzknopf V/MediaPlayer﹕ setDataSource(51, 0, 576460752303423487)
08-29 15:29:54.898 10191-10191/com.y0hy0h.furzknopf E/MediaPlayer﹕ Unable to to create media player
08-29 15:29:54.908 10191-10191/com.y0hy0h.furzknopf E/VolatileFartService﹕ File fart07.ogg could not be loaded.
java.io.IOException: setDataSourceFD failed.: status=0x80000000
at android.media.MediaPlayer.setDataSource(Native Method)
at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1032)
at com.y0hy0h.furzknopf.widget.VolatileFartService.onStartCommand(VolatileFartService.java:39)
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2524)
at android.app.ActivityThread.access00(ActivityThread.java:138)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1302)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4929)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:798)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:565)
at dalvik.system.NativeStart.main(Native Method)
注意第39行是
tempMediaPlayer.setDataSource(assetFD.getFileDescriptor());
或者,有没有办法传递 FileDescriptor 或其他对象供服务加载?
或者可能有一种甚至 更好的方法 来处理可能同时播放的多个声音,而不是将该功能放在 Service
中?
试试这个;
- 在您的资源目录中创建一个原始文件夹,例如res/raw
- 将要播放的文件放在raw文件夹中
使用媒体播放器加载文件,如下所示
MediaPlayer mediaPlayer = MediaPlayer.create(serviceClass.this, R.raw.sound_file_1);
mediaPlayer.start();
阅读更多;
http://developer.android.com/guide/topics/media/mediaplayer.html#mpandservices
作为 将文件移动到 res/raw 文件夹的替代方法,可以将资产文件加载 "manually" 到 MediaPlayer。
显然资产是 not stored as would be expected。相反,资产的起始位置和长度及其 (non Asset
-)FileDescriptor
需要传递给 MediaPlayer。
工作代码:
String pathToFile = String.format(
Locale.US,
"fart%02d.ogg",
Utility.getIntBetween(1, 15)
);
try {
AssetFileDescriptor assetFD = getAssets().openFd(pathToFile);
MediaPlayer tempMediaPlayer = new MediaPlayer();
tempMediaPlayer.setDataSource(
assetFD.getFileDescriptor(),
assetFD.getStartOffset(),
assetFD.getLength()
);
assetFD.close();
tempMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mp.release();
stopSelf();
}
});
tempMediaPlayer.prepare();
Log.v(LOG_TAG, "Start playing");
tempMediaPlayer.start();
} catch (IOException e) {
Log.e(LOG_TAG, "File "+pathToFile+" could not be loaded.", e);
}
我正在开发一个播放随机声音的小部件。我的问题在于获取我用来处理 MediaPlayer
和播放声音以加载存储在应用程序资产文件夹中的声音文件的服务。
我选择了 MediaPlayer
,而不是我的应用程序中的 SoundPool
,因为它有一个 onCompletionListener
,它允许我在声音完全播放后停止服务。那样我希望尽量减少小部件的资源使用。
更准确地说,在我的代码中 (checkout the [now outdated] commit on GitHub) I try to load a random sound with the MediaPlayer
's create()
convenience method using an Uri
to file:///android_asset/pathToFile
.
然而,这会在运行时抛出 IOException。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_PLAY_FART.equals(action)) {
Log.v(LOG_TAG, "Intent received");
String pathToFile = String.format(
Locale.US,
"fart%02d.ogg",
Utility.getIntBetween(1, 15)
);
MediaPlayer tempMediaPlayer = MediaPlayer.create(
this,
Uri.parse("file:///android_asset/"+pathToFile)
);
tempMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mp.release();
stopSelf();
}
});
Log.v(LOG_TAG, "Start playing");
tempMediaPlayer.start();
}
}
return START_NOT_STICKY;
}
是否有一种简单的方法(即除了内容提供商之外)从 加载 那些资产文件=]Service
?
[编辑,提供更多信息]
显然可以访问资产文件,但 MediaPlayer
无法正确加载它们。
我将代码修改为:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_PLAY_FART.equals(action)) {
Log.v(LOG_TAG, "Intent received");
String pathToFile = String.format(
Locale.US,
"fart%02d.ogg",
Utility.getIntBetween(1, 15)
);
try {
AssetFileDescriptor assetFD = this.getAssets().openFd(pathToFile);
MediaPlayer tempMediaPlayer = new MediaPlayer();
tempMediaPlayer.setDataSource(assetFD.getFileDescriptor());
tempMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mp.release();
stopSelf();
}
});
Log.v(LOG_TAG, "Start playing");
tempMediaPlayer.start();
} catch (IOException e) {
Log.e(LOG_TAG, "File "+pathToFile+" could not be loaded.", e);
}
}
}
我得到的输出是:
08-29 15:29:54.868 10191-10191/com.y0hy0h.furzknopf V/VolatileFartService﹕ Intent received
08-29 15:29:54.868 10191-10191/com.y0hy0h.furzknopf V/MediaPlayer﹕ constructor
08-29 15:29:54.868 10191-10191/com.y0hy0h.furzknopf V/MediaPlayer﹕ setListener
08-29 15:29:54.868 10191-10191/com.y0hy0h.furzknopf V/MediaPlayer﹕ setDataSource(51, 0, 576460752303423487)
08-29 15:29:54.898 10191-10191/com.y0hy0h.furzknopf E/MediaPlayer﹕ Unable to to create media player
08-29 15:29:54.908 10191-10191/com.y0hy0h.furzknopf E/VolatileFartService﹕ File fart07.ogg could not be loaded.
java.io.IOException: setDataSourceFD failed.: status=0x80000000
at android.media.MediaPlayer.setDataSource(Native Method)
at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1032)
at com.y0hy0h.furzknopf.widget.VolatileFartService.onStartCommand(VolatileFartService.java:39)
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2524)
at android.app.ActivityThread.access00(ActivityThread.java:138)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1302)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4929)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:798)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:565)
at dalvik.system.NativeStart.main(Native Method)
注意第39行是
tempMediaPlayer.setDataSource(assetFD.getFileDescriptor());
或者,有没有办法传递 FileDescriptor 或其他对象供服务加载?
或者可能有一种甚至 更好的方法 来处理可能同时播放的多个声音,而不是将该功能放在 Service
中?
试试这个;
- 在您的资源目录中创建一个原始文件夹,例如res/raw
- 将要播放的文件放在raw文件夹中
使用媒体播放器加载文件,如下所示
MediaPlayer mediaPlayer = MediaPlayer.create(serviceClass.this, R.raw.sound_file_1); mediaPlayer.start();
阅读更多; http://developer.android.com/guide/topics/media/mediaplayer.html#mpandservices
作为
显然资产是 not stored as would be expected。相反,资产的起始位置和长度及其 (non Asset
-)FileDescriptor
需要传递给 MediaPlayer。
工作代码:
String pathToFile = String.format(
Locale.US,
"fart%02d.ogg",
Utility.getIntBetween(1, 15)
);
try {
AssetFileDescriptor assetFD = getAssets().openFd(pathToFile);
MediaPlayer tempMediaPlayer = new MediaPlayer();
tempMediaPlayer.setDataSource(
assetFD.getFileDescriptor(),
assetFD.getStartOffset(),
assetFD.getLength()
);
assetFD.close();
tempMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mp.release();
stopSelf();
}
});
tempMediaPlayer.prepare();
Log.v(LOG_TAG, "Start playing");
tempMediaPlayer.start();
} catch (IOException e) {
Log.e(LOG_TAG, "File "+pathToFile+" could not be loaded.", e);
}