Невозможно исправить исключение 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". Убедитесь, что он вызывается только один раз, при новом идентификаторе объекта, каждый раз, когда начинается действие.