it-roy-ru.com

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

Шаги:

  1. Запросить разрешение у Fragment или Activity
  2. Показать DialogFragment изнутри onRequestPermissionsResult()
  3. Выдается Java.lang.IllegalStateException: не удается выполнить это действие после onSaveInstanceState

Это не происходит, когда я показываю диалог после некоторой задержки (с использованием postDelayed) . Согласно http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html ) на устройствах после сотовой связи мы МОЖЕМ commit() между onPause() и onStop() без УТВЕРЖДЕНИЯ И ИСКЛЮЧЕНИЯ. Вот ссылка на пример источника проекта, файл журнала и записанную проблему . https://drive.google.com/folderview?id=0BwvvuYbQTUl6STVSZF9TX2VUeHM&usp=sharing

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

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

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

Ошибка принята и будет исправлена, однако я категорически не согласен с решениями 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;
    }
}
24
Kenneth

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

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

 @Override public void onRequestPermissionsResult (разрешения типа int requestCode, String [], int [] grantResults) {

 if (requestCode == PERMISSION_CODE) {
 если (/ * РАЗРЕШЕНИЕ РАЗРЕШЕНО/ОТКАЗАНО * /) {
 new Timer (). schedule (new TimerTask () {
 @Override public void run () {
 // ВЫПОЛНИТЬ ДЕЙСТВИЯ (ПОДОБНАЯ ФРАКМЕНТНАЯ СДЕЛКА ETC.) 
} 
}, 0) ;
 } 
 } 

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

6
ldulcic

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

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

Итак, если вы покажете свой диалог на PostResume, у вас все будет хорошо.

4
CanC

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

// ...
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
Scott Key

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

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

4
Юрій Мазуревич

Я справился с этим, как мне кажется, гораздо более детерминированным способом. Вместо того, чтобы использовать таймеры, я в основном ставлю результат в очередь до 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 был звонил до этого).

2
pdub

Я также решил это с помощью обработчика, но я смог избежать использования таймера, устанавливающего Looper в MainLooper. Так что он будет запущен после того, как представление будет отображено:

new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        // do your fragment transaction here
    }
});
0
Rubén Viguera

Как объяснено в одном из комментариев в issue , это происходит только при использовании DialogFragment библиотеки поддержки (т.е., Android.support.v4.app.DialogFragment). Вы можете изменить свой код, чтобы использовать «нативный» DialogFragment (Android.app.DialogFragment), и он будет работать нормально.

Я знаю, что в некоторых случаях использование нативной DialogFragment невозможно, но в этом случае, учитывая, что вы имеете в виду разрешения времени выполнения (функция, которая доступна только в последних версиях Android), вы, скорее всего, сможете это сделать. У меня была именно эта проблема, и я мог бы исправить это таким образом.

0
Franco