Невозможно исправить исключение MediaController.show()
У меня есть аудиофайл, играющий в переднем плане с помощью MediaPlayer. Когда пользователь удаляет уведомление, связанное с службой переднего плана, я запускаю действие с использованием Intent следующим образом:
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);
Затем этот объект привязывается к службе, чтобы показать пользователю 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.
}
}
.. и в Activity 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() и привязан к самой активности. (Затем действие обрабатывает передачу команд в службу).
Ответы
Ответ 1
Я думаю, что вы вызываете show()
слишком рано, прежде чем предыдущая операция завершит жизненный цикл. BadTokenException
можно избежать, задерживая вызов show()
, пока не будут вызваны все методы жизненного цикла. Вы можете опубликовать задержанную версию для этого. Или вы можете попробовать,
if (!((Activity)your_context).isFinishing()) {
mediaController.show(5000);
}
Ответ 2
Исправлена проблема
У меня тоже была такая же проблема, и я исправил ее, выполнив следующее:
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
try{
mediaController.show(0);
}catch(Exception e){
e.printStackTrace();
}
}
Теперь он работает как шарм.
Ответ 3
Я считаю, что проблема в этой строке.
AudioPlayActivity.this.audioService = audioServiceBinder.getAudioService();
Вы можете посмотреть здесь для деталей. Прочитайте все комментарии в нем, а не только ответ.
Ответ 4
Внутри 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
.
Извините, если я полностью не понял этот вопрос.
Ответ 5
Из шагов, о которых вы упоминали, кажется, что onConnected()
вызывается в пропущенном экземпляре предыдущей активности, созданной на шаге 1. Если служба по требованию (связанная служба), вы должны привязать/развязать в onResume()
/onPause()
соответственно.
Чтобы подтвердить утечку экземпляра, поместите:
log.i("LEAKTEST", "Connected to instance " + this.toString());
внутри onConnected()
.
Теперь заново создайте сценарий и обратите внимание на идентификатор объекта в logcat, это будет похоже на "@1246464". Убедитесь, что он вызывается только один раз, при новом идентификаторе объекта, каждый раз, когда начинается действие.