问题 无法修复MediaController.show()异常


我使用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. 单击通知以打开活动
  2. 按向后关闭活动。
  3. 单击通知以打开活动的新版本。

这让我相信mediaController在某种程度上泄漏并试图在原始的Activity实例中展示自己。我找不到任何理由,因为mediaController在Activity的onCreate()中实例化,并且仅与活动本身相关联。 (然后,活动处理将命令传递到服务)。


8749
2017-08-25 05:45


起源

为什么这会被贬低? - you786
你试过mymediaplayer.setDisplay(mysurface)吗? - sschrass
@SatelliteSD不,但这只是一个音频文件。此外,没有表面,因为MediaPlayer实例正在服务中运行。 - you786
哎呀似乎我误读了你的问题:( - sschrass
“这特别难,因为我无法再创造它!”只是因为您的设备首先准备媒体播放器然后显示媒体控制器请尝试在较低的配置设备中复制,这可能会帮助您解决这个问题。 - Varun


答案:


我想你在打电话 show() 太早,在之前的活动完成生命周期之前。 BadTokenException 可以通过延迟呼叫来避免 show() 直到调用所有生命周期方法。您可以为此发布延迟的runnable。或者你可以尝试以下,

if (!((Activity)your_context).isFinishing()) {
    mediaController.show(5000);
}

7
2017-08-30 11:43



实际上,这似乎是问题,但对可运行的任意延迟似乎是一件坏事。这似乎可能是操作系统中的一个错误,因为在任何地方都没有关于此限制的文档。我不认为你的isFinishing()方法会起作用,因为它是一个不同的Activity实例。 - you786
Ritesh Gune是正确的,但为了避免延迟,您可以将show放置在调用所有生命周期方法之后调用的onAttachedToWindow方法中。 - jonathanrz


修复了这个问题

我也有同样的问题并通过以下方式修复它,

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

现在它就像一个魅力。


6
2018-06-20 18:40



是的这真的有效! - Kevin Lee


我相信问题就在这一行。

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

你可以看 这里 细节。阅读其中的所有评论,而不仅仅是答案。


1
2017-09-03 06:22





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

如果我完全误解了这个问题,请道歉。


0
2017-09-03 06:25





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

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

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

onConnected()

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


0
2017-09-03 06:54