Вызов 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);
Ответы
Ответ 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 вызывается до этого).