我使用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)
我可以通过以下方式重新创建相同的异常:
这让我相信mediaController在某种程度上泄漏并试图在原始的Activity实例中展示自己。我找不到任何理由,因为mediaController在Activity的onCreate()中实例化,并且仅与活动本身相关联。 (然后,活动处理将命令传递给服务)。
答案 0 :(得分:7)
我认为您在上一个活动完成生命周期之前过早地调用show()
。通过延迟调用BadTokenException
直到调用所有生命周期方法,可以避免show()
。您可以为此发布延迟的runnable。或者你可以试试,
if (!((Activity)your_context).isFinishing()) {
mediaController.show(5000);
}
答案 1 :(得分:6)
我也遇到了同样的问题并通过执行以下操作来修复它,
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
try{
mediaController.show(0);
}catch(Exception e){
e.printStackTrace();
}
}
现在它就像一个魅力。
答案 2 :(得分:1)
我相信问题就在这一行。
AudioPlayActivity.this.audioService = audioServiceBinder.getAudioService();
您可以查看here了解详细信息。阅读其中的所有评论,而不仅仅是答案。
答案 3 :(得分: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
。
如果我完全误解了这个问题,请道歉。
答案 4 :(得分:0)
根据您提到的步骤,似乎在步骤1中创建的先前活动的泄漏实例上调用onConnected()
。如果服务是按需(绑定服务),那么您应该绑定/取消绑定{分别为{1}} / onResume()
。
要确认实例泄漏,请执行以下操作:
onPause()
在log.i("LEAKTEST", "Connected to instance " + this.toString());
内。
现在,重新创建场景,并注意logcat中的对象id,它就像“@ 1246464”。每次启动活动时,检查其在新对象ID上仅调用一次。