【问题标题】:Load asset files inside background service在后台服务中加载资产文件
【发布时间】:2015-08-29 12:57:39
【问题描述】:

我正在开发一个播放随机声音的小部件。我的问题在于获取我用来处理MediaPlayer 并播放声音以加载我存储在应用程序资产文件夹中的声音文件的服务。
我选择了MediaPlayer,而不是我的应用程序中的SoundPool,因为它有一个onCompletionListener,它允许我在完全播放声音后停止服务。这样我希望尽量减少小部件的资源使用。

更准确地说,在我的代码 (checkout the [now outdated] commit on GitHub) 中,我尝试使用 MediaPlayercreate() 便捷方法 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.access$1900(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 中?

【问题讨论】:

  • 您可以从服务中的 Assets 文件夹加载文件,但在提出答案之前,请提供您的代码,以便人们可以轻松提供帮助,而不是假设您可能需要或不需要什么。
  • 我添加了一个代码sn-p。对于更多代码,我提供了指向我的 GitHub 存储库的链接。这是提供代码的坏方法吗,我应该直接在 Stackoverflow 上包含更多代码吗?
  • 你试过 AssetManager 资产 = Context.get assets();资产.open("文件名");?
  • 这正是问题所在,服务没有context 变量。因此我不能在那里使用AssetManager

标签: android android-service android-assets


【解决方案1】:

作为Want2bExpert's suggestion 将文件移动到 res/raw 文件夹的替代方法,资产文件可以“手动”加载到 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);
}

【讨论】:

    【解决方案2】:

    试试这个;

    • 在资源目录中创建一个原始文件夹,例如资源/原始
    • 将要播放的文件放在原始文件夹中
    • 使用媒体播放器加载文件,如下所示

      MediaPlayer mediaPlayer = MediaPlayer.create(serviceClass.this, R.raw.sound_file_1); mediaPlayer.start();

    阅读更多; http://developer.android.com/guide/topics/media/mediaplayer.html#mpandservices

    【讨论】:

    • 从哪里可以得到context 传递给create()
    • 您是否尝试过在服务中声明一个 Context 全局变量?我不记得以前做过,但试一试。例如。在您的服务类上下文上下文中 = this;
    • Service 获得Context 的唯一位置是在其静态createIntentPlayFart() 方法中。我不能在那里设置成员变量。另外,我更新了我的问题,因为您的提议帮助我进一步确定了问题。
    • 这很有趣,你有一个意图如何,服务将文件从 Assest 文件夹加载到内部 App 目录,然后将文件目录广播到 Receiver,然后使用 FileDir 作为额外的启动服务意图启动服务?
    • 除了服务不是上下文吗?我认为我给出的解决方案应该为你工作。请参阅我编辑的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-04
    • 1970-01-01
    相关资源
    最近更新 更多