11

我有一个使用 MediaPlayer 在前台服务中播放的音频文件。当用户点击与前台服务关联的通知时,我使用 Intent 启动一个 Activity,如下所示:

Intent audioPlayIntent = new Intent(context, AudioPlayActivity.class);
audioPlayIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
audioPlayIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, audioPlayIntent, 0);

然后,此 Activity 绑定到服务以向用户显示 MediaController。

这是服务中的绑定代码:

public class AudioPlayerServiceBinder extends Binder{

    public AudioPlayerService getAudioService(){
        return AudioPlayerService.this; //this class is declared in AudioPlayerService.java, so it has access to the Service instance.
    }

}

..在活动中onStart我调用了这个方法:

private void bindAudioService()
    {
        Intent i = new Intent(this, AudioPlayerService.class);
        serviceConnection = new AudioServiceConnection();
        bindService(i, serviceConnection, 0);
    }

我在下面的 mediaController.show(5000) 行中遇到异常:

private class AudioServiceConnection implements ServiceConnection{

    AudioPlayerServiceBinder audioServiceBinder;
@Override
        public void onServiceConnected(ComponentName name, IBinder serviceBinder)
        {
            serviceConnected = true;
            Log.i(TAG, "Connected to audio player service.");
            audioServiceBinder = ((AudioPlayerServiceBinder) serviceBinder);
            AudioPlayActivity.this.audioService = audioServiceBinder.getAudioService();
            mediaController.show(5000);
        }

例外是:

android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
at android.view.ViewRoot.setView(ViewRoot.java:527)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:177)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)
at android.view.Window$LocalWindowManager.addView(Window.java:424)
at android.widget.MediaController.show(MediaController.java:304)
at android.widget.MediaController.show(MediaController.java:249)
at com.myapp.AudioPlayActivity$AudioServiceConnection.onServiceConnected(AudioPlayActivity.java:295)

我可以通过以下方式重新创建相同的异常:

  1. 点击通知打开Activity
  2. 按返回关闭活动。
  3. 单击通知以打开新版本的活动。

这使我相信 mediaController 以某种方式泄漏并试图在原始 Activity 实例中显示自己。不过,我找不到任何发生这种情况的原因,因为 mediaController 在 Activity 的 onCreate() 中实例化,并且仅与 Activity 本身相关联。(然后活动处理将命令传递给服务)。

4

5 回答 5

7

我认为您show()在前一个活动完成生命周期之前调用得太早了。BadTokenException可以通过延迟调用show()直到调用所有生命周期方法来避免。您可以为此发布延迟的可运行文件。或者你可以尝试以下,

if (!((Activity)your_context).isFinishing()) {
    mediaController.show(5000);
}
于 2013-08-30T11:43:04.897 回答
6

修复了问题

我也遇到了同样的问题并通过执行以下操作来解决它,

@Override
public void onAttachedToWindow() {
    super.onAttachedToWindow();
    try{
        mediaController.show(0);
    }catch(Exception e){
        e.printStackTrace();
    }
}

现在它就像一个魅力。

于 2015-06-20T18:40:44.387 回答
1

我相信问题出在这条线上。

AudioPlayActivity.this.audioService = audioServiceBinder.getAudioService();

您可以在这里查看详细信息。阅读其中的所有评论,而不仅仅是答案。

于 2013-09-03T06:22:15.373 回答
0

从您提到的步骤来看,似乎onConnected()是在步骤 1 中创建的先前活动的泄漏实例上调用的。如果服务是按需的(绑定服务),那么您应该分别在onResume()/中绑定/取消绑定onPause()

要确认实例泄漏,请放置:

log.i("LEAKTEST", "Connected to instance " + this.toString());

里面onConnected()

现在,重新创建场景,并注意 logcat 中的对象 ID,它就像“@1246464”。每次启动活动时,检查它是否只在新对象 ID 上调用一次。

于 2013-09-03T06:54:25.873 回答
0

内部AudioPlayActivity's onCreate(Bundle)

而不是使用setContentView(int),膨胀布局(如果你已经这样做了,请跳过):

声明一个全局View变量:

View mContentView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mContentView = getLayoutInflater().inflate(R.layout.your_activitys_layout, null);       

    // initialize widgets
    Button b = (Button) mContentView.findViewById(...);

    ....
    ....

    // Finally
    setContentView(mContentView);
}

更改AudioServiceConnection为以下内容:

private class AudioServiceConnection implements ServiceConnection{

    AudioPlayerServiceBinder audioServiceBinder;
    @Override
    public void onServiceConnected(ComponentName name, IBinder serviceBinder)
    {
        serviceConnected = true;
        Log.i(TAG, "Connected to audio player service.");
        audioServiceBinder = ((AudioPlayerServiceBinder) serviceBinder);
        AudioPlayActivity.this.audioService = audioServiceBinder.getAudioService();

        mContentView.post(new Runnable() {

            @Override
            public void run() {
                mediaController.show(5000);
            }
        });
    }

这应该摆脱WindowManager$BadTokenException.

如果我完全误解了这个问题,我深表歉意。

于 2013-09-03T06:25:44.800 回答