Подтвердить что ты не робот

Вызов DialogFragment show() изнутри onRequestPermissionsResult() вызывает исключение IllegalStateException в Marshmallow

Шаги:

  • Запросить разрешение от Fragment или Activity
  • Показать диалоговое окно диалога <<22 >
  • java.lang.IllegalStateException: не удается выполнить это действие после onSaveInstanceState

Это не происходит, когда я показываю диалог после некоторой задержки (используя postDelayed). Согласно http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html) на устройствах сотовой связи мы можем commit() между onPause() и onStop() без каких-либо STATE LOSS или EXCEPTION. Вот ссылка на образец источника проекта, файл журнала и запись. https://drive.google.com/folderview?id=0BwvvuYbQTUl6STVSZF9TX2VUeHM&usp=sharing

Также я открыл проблему https://code.google.com/p/android/issues/detail?id=190966, но был отмечен как WorkAsIntended, и они предлагают просто перехватить исключение. Но это не решает проблему. Я знаю другие способы его решения, но разве это не ошибка Android?

UPDATE Статус ошибки снова присваивается. Надеюсь, скоро это будет исправлено. Мое временное решение

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        // do your fragment transaction here
    }
}, 200);
4b9b3361

Ответ 1

Ошибка принята и будет исправлена, однако я категорически не согласен с решениями postDelayed и timer. Лучший способ сделать это - ввести флаг состояния в Activity, в котором вы задали обратный вызов, и использовать его в onResume или аналогичном. Например:

private boolean someFlag;

public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
// some code checking status
    someFlag = true;
}

И затем в onResume:

protected void onResume() {
    if(someFlag == true) {
        doSomething();
        someFlag = false;
    }
}

Ответ 2

Я также думаю, что это ошибка Android. Я не могу поверить, что они отметили вашу проблему WorkAsIntended. Единственное решение на данный момент - отложить выполнение кода в onRequestPermissionsResult(), пока люди андроида не исправят это правильно.

Это мой способ решить эту проблему, если кто-то задается вопросом, как отложить выполнение:

  @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {

    if (requestCode == PERMISSION_CODE) {
      if (/* PERMISSION ALLOWED/DENIED */) {
          new Timer().schedule(new TimerTask() {
              @Override public void run() {
                  // EXECUTE ACTIONS (LIKE FRAGMENT TRANSACTION ETC.)
              }
          }, 0);
      }
  }

Это существенно задерживает выполнение до завершения onRequestPermissionsResult(), поэтому мы не получаем java.lang.IllegalStateException. Это работает в моем приложении.

Ответ 3

попробуйте что-то вроде этого:

// ...
private Runnable mRunnable;

@Override
public void onResume() {
   super.onResume();

   if (mRunnable != null) {
       mRunnable.run();
       mRunnable = null;
   }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
   super.onRequestPermissionsResult(requestCode, permissions, grantResults);

   if (/* PERMISSION_DENIED */) {
      mRunnable = /* new Runnable which show dialogFragment*/;
   }
}

Ответ 4

Это ошибка в Android https://code.google.com/p/android/issues/detail?id=190966&q=label%3AReportedBy-Developer&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars

Итак, на данный момент необходимы обходные пути. Вы можете использовать решение @ldulcic. Или вы можете использовать метод Handler postDelay. Предпочтительным является второй вариант.

Ответ 5

Поскольку вы используете DialogFragment, вы должны сохранить флаг или состояние, а затем показать свое диалоговое окно в onPostResume. Вы должны сделать это в onPostResume, а не в onResume или других методах жизненного цикла.

Как показывает документация , если вы совершаете транзакции в методах жизненного цикла активности, отличных от onPostResume или onResumeFragments (для FragmentActivity), в некоторых случаях метод можно вызвать до того, как состояние активности будет полностью восстановлено.

Итак, если вы покажете свое диалоговое окно onPostResume, все будет в порядке.

Ответ 6

Я обошел это в том, что я считаю более детерминированным. Вместо того, чтобы использовать таймеры, я в основном приостанавливаю результат до Activity.onResumeFragments() (или вы можете сделать это в Activity.onResume(), если вы не используете фрагменты). Я сделал onResumeFragments(), потому что я также перенаправляю результат на конкретный фрагмент, который запрашивал разрешение, поэтому мне нужно быть уверенным, что фрагменты готовы.

Здесь onRequestPermissionsResult():

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                       @NonNull int[] grantResults) {
    // This is super ugly, but google has a bug that they are calling this method BEFORE
    // onResume... which screws up fragment lifecycle big time.  So work around it.  But also
    // be robust enough to still work if/when they fix the bug.
    if (mFragmentsResumed) {
        routePermissionsResult(requestCode, permissions, grantResults);
    } else {
        mQueuedPermissionGrantResults = grantResults;
        mQueuedPermissionRequestCode = requestCode;
        mQueuedPermissions = permissions;
    }
}

Затем здесь onResumeFragments():

@Override
protected void onResumeFragments() {
    super.onResumeFragments();

    mFragmentsResumed = true;

    if (mQueuedPermissionGrantResults != null) {
        routePermissionsResult(mQueuedPermissionRequestCode, mQueuedPermissions,
                mQueuedPermissionGrantResults);
    }

И для полноты, здесь onPause(), которая очищает флаг mFragmentsResumed:

@Override
protected void onPause() {
    super.onPause();

    mFragmentsResumed = false;
}

Я использовал флаг mFragmentsResumed, потому что я не хочу, чтобы этот код переставал работать, если и когда Google исправляет ошибку (изменение порядка вызовов жизненного цикла приведет к тому, что код не будет работать, если бы я просто устанавливал переменные в очереди в onRequestPermissionsResult но onResumeFragments вызывается до этого).