Нет. В настоящее время на консоли видны только сообщения, отправленные через консоль уведомлений Firebase. Сообщения, отправленные через API, можно отслеживать в Diagnostics Tool , имея в виду, что это не включает сообщения, отправленные по темам .
Пожалуйста, проверьте мой ответ здесь . В основном я просто должен был:
@Override
protected void onSaveInstanceState(Bundle outState) {
//No call for super(). Bug on API Level > 11.
}
Не делайте вызов super()
по методу saveInstanceState
. Это было бесполезно ...
Это известная ошибка в пакете поддержки.
Если вам нужно сохранить экземпляр и добавить что-то в свой outState
Bundle
, вы можете использовать следующее:
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
super.onSaveInstanceState(outState);
}
В конце концов, правильное решение было (как в комментариях) для использования:
transaction.commitAllowingStateLoss();
при добавлении или выполнении FragmentTransaction
, вызывающего Exception
.
Я закончил создание базового фрагмента и сделал все фрагменты в моем приложении расширением
public class BaseFragment extends Fragment {
private boolean mStateSaved;
@CallSuper
@Override
public void onSaveInstanceState(Bundle outState) {
mStateSaved = true;
super.onSaveInstanceState(outState);
}
/**
* Version of {@link #show(FragmentManager, String)} that no-ops when an IllegalStateException
* would otherwise occur.
*/
public void showAllowingStateLoss(FragmentManager manager, String tag) {
// API 26 added this convenient method
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (manager.isStateSaved()) {
return;
}
}
if (mStateSaved) {
return;
}
show(manager, tag);
}
}
Тогда, когда я пытаюсь показать фрагмент, я использую showAllowingStateLoss
вместо show
следующим образом:
MyFragment.newInstance()
.showAllowingStateLoss(getFragmentManager(), MY_FRAGMENT.TAG);
Я подошел к этому решению из этого PR: https://github.com/googlesamples/easypermissions/pull/170/files
Я думаю, что использование transaction.commitAllowingStateLoss();
не лучшее решение. Это исключение будет выбрано, если конфигурация активности изменится, и вызывается фрагмент onSavedInstanceState()
, после чего ваш метод обратного вызова async пытается зафиксировать фрагмент.
Простым решением может быть проверка того, меняется ли активность в конфигурации или нет
, например, isChangingConfigurations()
i.e.
if(!isChangingConfigurations()) {
//commit transaction.
}
Оформить эту ссылку, а также
Еще одно возможное обходное решение, которое я не уверен, что помогает во всех случаях (источник здесь ):
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
final View rootView = findViewById(android.R.id.content);
if (rootView != null) {
rootView.cancelPendingInputEvents();
}
}
}
Всякий раз, когда вы пытаетесь загрузить фрагмент в своей деятельности, убедитесь, что активность возобновлена и не будет приостановлена. В состоянии паузы вы можете потерять операцию фиксации, которая будет выполнена.
Вы может использовать transaction.commitAllowingStateLoss () вместо transaction.commit () для загрузки фрагмента
или
Создать логическое значение и проверить, не активируется ли действие onpause
@Override
public void onResume() {
super.onResume();
mIsResumed = true;
}
@Override
public void onPause() {
mIsResumed = false;
super.onPause();
}
, а затем при загрузке проверки фрагмента
if(mIsResumed){
//load the your fragment
}
Это октябрь 2017 года, и Google делает Android Support Library новой компонентой Lifecycle. Это дает некоторую новую идею для этого «Невозможно выполнить это действие после проблемы onSaveInstanceState».
Короче:
Более длинная версия с объяснением:
FragmentManager
из своей активности (которая, как предполагается, держит ваш фрагмент, предположим?), Чтобы зафиксировать транзакцию для фрагмента. Обычно это будет выглядеть так, как будто вы пытаетесь выполнить некоторую транзакцию для следующего фрагмента, в то время как активность хоста уже вызывает метод savedInstanceState
(пользователь может попасть в домашнюю кнопку, чтобы активность вызывала onStop()
, в моем случае это Причина) Обычно эта проблема не должна происходить - мы всегда пытаемся загрузить фрагмент в действие в самом начале, как метод onCreate()
- это идеальное место для этого. Но иногда это случается, особенно когда вы не можете решить, какой фрагмент вы загрузите для этой активности, или вы пытаетесь загрузить фрагмент из блока AsyncTask
(или что-то займет немного времени). Время, прежде чем транзакция фрагмента действительно произойдет, но после метода onCreate()
активности пользователь может сделать что угодно. Если пользователь нажимает кнопку «домой», которая запускает метод onSavedInstanceState()
активности, произошел сбой can not perform this action
. Если кто-то захочет увидеть глубже в этом вопросе, я предлагаю им взглянуть на этот блог post . Он выглядит глубоко внутри исходного кода и объясняет многое об этом. Кроме того, это указывает на то, что вы не должны использовать метод commitAllowingStateLoss()
для обхода этого сбоя (верьте мне, что он не предлагает ничего хорошего для вашего кода) commitAllowingStateLoss()
для загрузки фрагмента? Нет, не стоит; Должен ли я переопределить метод onSaveInstanceState
, игнорировать метод super
внутри него? Нет, не стоит; Должен ли я использовать магическую активность isFinishing
для проверки активности хоста в нужный момент для транзакции фрагмента? Да, это похоже на правильный путь. AppCompatActivity
(и нескольких других базовых классов, которые вы должны использовать в своем проекте), что упрощает определение текущего состояния жизненного цикла. Оглянитесь на нашу проблему: почему эта проблема возникнет? Это потому, что мы делаем что-то не так. Поэтому мы стараемся не делать этого, и эта проблема исчезнет. Я немного код для своего проекта, вот что я использую LifeCycle
. Я код в Котлин. val hostActivity: AppCompatActivity? = null // the activity to host fragments. It's value should be properly initialized.
fun dispatchFragment(frag: Fragment) {
hostActivity?.let {
if(it.lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)){
showFragment(frag)
}
}
}
private fun showFragment(frag: Fragment) {
hostActivity?.let {
Transaction.begin(it, R.id.frag_container)
.show(frag)
.commit()
}
Как я показал выше. Я проверю состояние жизненного цикла активности хоста. С компонентом Lifecycle в библиотеке поддержки это может быть более конкретным. Код lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)
означает, что если текущее состояние не менее onResume
, не позднее этого? Это гарантирует, что мой метод не будет выполняться во время какого-либо другого состояния жизни (например, onStop
).
onStop
, эта строка кода не будет делать ничего, и, таким образом, не покажет ничего на вашем экране. Когда пользователи возвращаются в приложение, они будут видеть пустой экран, это пустая активность хоста, которая не показывает никаких фрагментов. Это плохой опыт (да немного лучше, чем крушение). Поэтому я хочу, чтобы было что-то приятнее: приложение не будет разбиваться, если дело дойдет до состояния жизни позже onResume
, метод транзакции - это состояние жизни; кроме того, действие будет пытаться продолжить завершение этого действия транзакции фрагмента после того, как пользователь вернется в наше приложение. Я добавляю что-то еще к этому методу: class FragmentDispatcher(_host: FragmentActivity) : LifecycleObserver {
private val hostActivity: FragmentActivity? = _host
private val lifeCycle: Lifecycle? = _host.lifecycle
private val profilePendingList = mutableListOf<BaseFragment>()
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun resume() {
if (profilePendingList.isNotEmpty()) {
showFragment(profilePendingList.last())
}
}
fun dispatcherFragment(frag: BaseFragment) {
if (lifeCycle?.currentState?.isAtLeast(Lifecycle.State.RESUMED) == true) {
showFragment(frag)
} else {
profilePendingList.clear()
profilePendingList.add(frag)
}
}
private fun showFragment(frag: BaseFragment) {
hostActivity?.let {
Transaction.begin(it, R.id.frag_container)
.show(frag)
.commit()
}
}
}
Я поддерживаю список внутри этого класса dispatcher
, чтобы сохранить этот фрагмент, не имеет шансов завершить действие транзакции. И когда пользователь возвращается с главного экрана и обнаружил, что все еще есть фрагмент, ожидающий запуска, он перейдет к методу resume()
в аннотации @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
. Теперь я думаю, что он должен работать так, как я ожидал.
Добавьте это в свою активность
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (outState.isEmpty()) {
// Work-around for a pre-Android 4.2 bug
outState.putBoolean("bug:fix", true);
}
}
Не используйте commitAllowingStateLoss (), его следует использовать только в тех случаях, когда пользовательское пользовательское состояние нормально меняется на пользователя.
Вместо этого используйте if (fragment.isResume ()) для проверки вне операции, с которой вы столкнулись с этим IllegalStateException "Can не выполнять это действие после onSaveInstanceState "
Если вы делаете некоторое FragmentTransaction в onActivityResult, что вы можете сделать, вы можете установить некоторое логическое значение внутри onActivityResult, а затем в onResume вы можете сделать свой FragmentTransaction на основе логического значения. Пожалуйста, ознакомьтесь со следующим кодом.
@Override
protected void onResume() {
super.onResume;
if(isSwitchFragment){
isSwitchFragment=false;
bottomNavigationView.getTabAt(POS_FEED).select();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == FilterActivity.FILTER_REQUEST_EVENT && data != null) {
isSwitchFragment=true;
}
}
Я получал это исключение, когда я нажимал кнопку «Назад», чтобы отменить намерение выбора на моей активности фрагмента карты. Я решил это, заменив код onResume (где я инициализировал фрагмент) на onstart (), и приложение работает нормально. Надеюсь, что это поможет.
BEWARE, использование transaction.commitAllowingStateLoss()
может привести к плохому опыту для пользователя. Для получения дополнительной информации о том, почему это исключение выбрано, см. этот пост .
Исключение выбрано здесь (In FragmentActivity):
@Override
public void onBackPressed() {
if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
super.onBackPressed();
}
}
В FragmentManager.popBackStatckImmediate()
сначала вызывается FragmentManager.checkStateLoss()
. В этом причина IllegalStateException
. См. Реализацию ниже:
private void checkStateLoss() {
if (mStateSaved) { // Boom!
throw new IllegalStateException(
"Can not perform this action after onSaveInstanceState");
}
if (mNoTransactionsBecause != null) {
throw new IllegalStateException(
"Can not perform this action inside of " + mNoTransactionsBecause);
}
}
Я решаю эту проблему просто с помощью флага, чтобы отметить текущее состояние Activity. Вот мое решение:
public class MainActivity extends AppCompatActivity {
/**
* A flag that marks whether current Activity has saved its instance state
*/
private boolean mHasSaveInstanceState;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
mHasSaveInstanceState = true;
super.onSaveInstanceState(outState);
}
@Override
protected void onResume() {
super.onResume();
mHasSaveInstanceState = false;
}
@Override
public void onBackPressed() {
if (!mHasSaveInstanceState) {
// avoid FragmentManager.checkStateLoss()'s throwing IllegalStateException
super.onBackPressed();
}
}
}
Предоставлено: Решение для IllegalStateException
Эта проблема раздражала меня в течение длительного времени, но, к счастью, я пришел с конкретным решением для нее. Подробное объяснение этого - здесь .
Использование commitAllowStateloss () может предотвратить это исключение, но приведет к нарушениям пользовательского интерфейса. До сих пор мы поняли, что IllegalStateException встречается, когда мы пытаемся зафиксировать фрагмент после того, как состояние Activity будет потеряно, поэтому мы должны просто задержать транзакцию до восстановления состояния. Это можно сделать просто так
Объявить две частные логические переменные
public class MainActivity extends AppCompatActivity {
//Boolean variable to mark if the transaction is safe
private boolean isTransactionSafe;
//Boolean variable to mark if there is any transaction pending
private boolean isTransactionPending;
Теперь в onPostResume () и onPause мы устанавливаем и удаляем нашу логическую переменную isTransactionSafe. Идея заключается в том, чтобы пометить безопасность транзакций только тогда, когда активность находится на переднем плане, поэтому нет никаких шансов stateloss.
/*
onPostResume is called only when the activity's state is completely restored. In this we will
set our boolean variable to true. Indicating that transaction is safe now
*/
public void onPostResume(){
super.onPostResume();
isTransactionSafe=true;
}
/*
onPause is called just before the activity moves to background and also before onSaveInstanceState. In this
we will mark the transaction as unsafe
*/
public void onPause(){
super.onPause();
isTransactionSafe=false;
}
private void commitFragment(){
if(isTransactionSafe) {
MyFragment myFragment = new MyFragment();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.frame, myFragment);
fragmentTransaction.commit();
}
}
-Что мы сделали до сих пор, сэкономить на IllegalStateException, но наши транзакции будут потеряны, если они выполняются после того, как действие перемещается на задний план, вроде commitAllowStateloss (). Для этого у нас есть логическая переменная isTransactionPending
public void onPostResume(){
super.onPostResume();
isTransactionSafe=true;
/* Here after the activity is restored we check if there is any transaction pending from
the last restoration
*/
if (isTransactionPending) {
commitFragment();
}
}
private void commitFragment(){
if(isTransactionSafe) {
MyFragment myFragment = new MyFragment();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.frame, myFragment);
fragmentTransaction.commit();
isTransactionPending=false;
}else {
/*
If any transaction is not done because the activity is in background. We set the
isTransactionPending variable to true so that we can pick this up when we come back to
foreground
*/
isTransactionPending=true;
}
}
Вот другое решение этой проблемы.
Используя частную переменную-член, вы можете установить возвращаемые данные как намерение, которое затем может быть обработано после super.onResume ();
Так же:
private Intent mOnActivityResultIntent = null;
@Override
protected void onResume() {
super.onResume();
if(mOnActivityResultIntent != null){
... do things ...
mOnActivityResultIntent = null;
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
if(data != null){
mOnActivityResultIntent = data;
}
}
super.onActivityResult()
.
– Sufian
10 February 2015 в 12:40
Чтобы обойти эту проблему, мы можем использовать Компонент архитектуры навигации , который был введен в Google I / O 2018. Компонент архитектуры навигации упрощает реализацию навигации в приложении для Android.
У меня была аналогичная проблема, сценарий был примерно таким:
Метод onCreate для работы был таким:
mMainFragment = (SelectionFragment) getSupportFragmentManager()
.findFragmentByTag(MAIN_FRAGMENT_TAG);
if (mMainFragment == null) {
mMainFragment = new SelectionFragment();
mMainFragment.setListAdapter(new ArrayAdapter<String>(this,
R.layout.item_main_menu, getResources().getStringArray(
R.array.main_menu)));
mMainFragment.setOnSelectionChangedListener(this);
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG);
transaction.commit();
}
Исключение было выбрано потому, что при изменении конфигурации (поворот устройства) активность создается, основной фрагмент извлекается из истории диспетчера фрагментов, и в то же время фрагмент уже имеет OLD-ссылку на уничтоженную активность
, изменяя реализацию, чтобы решить эту проблему:
mMainFragment = (SelectionFragment) getSupportFragmentManager()
.findFragmentByTag(MAIN_FRAGMENT_TAG);
if (mMainFragment == null) {
mMainFragment = new SelectionFragment();
mMainFragment.setListAdapter(new ArrayAdapter<String>(this,
R.layout.item_main_menu, getResources().getStringArray(
R.array.main_menu)));
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG);
transaction.commit();
}
mMainFragment.setOnSelectionChangedListener(this);
вам нужно настроить своих слушателей каждый раз, когда создается действие, чтобы избежать ситуации, когда фрагменты ссылаются на старые уничтоженные экземпляры активности.
Возможно, самое гладкое и самое простое решение, которое я нашел в моем случае, заключалось в том, чтобы избежать появления фрагмента оскорбления из стека в ответ на результат активности. Поэтому меняем этот вызов в моем onActivityResult()
:
popMyFragmentAndMoveOn();
на это:
new Handler(Looper.getMainLooper()).post(new Runnable() {
public void run() {
popMyFragmentAndMoveOn();
}
}
помогли в моем случае.
Что касается @Anthonyeef отличного ответа, вот пример кода в Java:
private boolean shouldShowFragmentInOnResume;
private void someMethodThatShowsTheFragment() {
if (this.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
showFragment();
} else {
shouldShowFragmentInOnResume = true;
}
}
private void showFragment() {
//Your code here
}
@Override
protected void onResume() {
super.onResume();
if (shouldShowFragmentInOnResume) {
shouldShowFragmentInOnResume = false;
showFragment();
}
}
Проверьте, активна ли isFinishing()
перед отображением фрагмента и обратите внимание на commitAllowingStateLoss()
.
Пример:
if(!isFinishing()) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
DummyFragment dummyFragment = DummyFragment.newInstance();
ft.add(R.id.dummy_fragment_layout, dummyFragment);
ft.commitAllowingStateLoss();
}
Я также столкнулся с этой проблемой, и проблема возникает каждый раз, когда изменяется контекст вашего FragmentActivity
(например, изменение ориентации экрана и т. д.). Поэтому лучшим решением для него является обновление контекста из вашего FragmentActivity
.
Я знаю, что есть признанный ответ от @Ovidiu Latcu, но через некоторое время ошибка все еще сохраняется.
@Override
protected void onSaveInstanceState(Bundle outState) {
//No call for super(). Bug on API Level > 11.
}
Crashlytics по-прежнему отправляет мне это странное сообщение об ошибке.
Однако ошибка теперь возникает только в версии 7+ (нуга). Моим решением было использовать commitAllowingStateLoss () вместо commit () ) при фрагментации.
Этот пост полезен для commitAllowingStateLoss () и никогда больше не имел проблемы с фрагментом.
Чтобы подвести итог, принятый ответ здесь может работать на версиях андроида до Нугата.
Это может спасти кого-то несколько часов поиска. счастливые кодировки. & lt; 3 ура
Я нашел грязное решение для такого рода проблем. Если вы по-прежнему хотите сохранить свой ActivityGroups
по какой-либо причине (у меня были причины ограничения по времени), вы просто реализуете
public void onBackPressed() {}
в своем Activity
и делаете там код back
. даже если такого метода для старых устройств нет, этот метод вызывается более новыми.
У меня была такая же проблема. Это произошло из-за разрушения предыдущей деятельности. когда ı поддержал предыдущую деятельность, она была уничтожена. Я положил его на базовую активность (WRONG)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SpinnerCustom2.setFragmentManager(getSupportFragmentManager());
onCreateDrawerActivity(savedInstanceState);
}
Я положил его в onStart, это было RIGHT
@Override
protected void onStart() {
super.onStart();
SpinnerCustom2.setFragmentManager(getSupportFragmentManager());
}
Есть много связанных проблем с похожим сообщением об ошибке. Проверьте вторую строку этой конкретной трассировки стека. Это исключение конкретно связано с вызовом FragmentManagerImpl.popBackStackImmediate
.
Этот вызов метода, например popBackStack
, будет всегда сбой IllegalArgumentException
, если состояние сеанса уже было сохранены. Проверьте источник. Вы ничего не можете сделать, чтобы остановить это исключение.
super.onSaveInstanceState
не поможет. commitAllowingStateLoss
не поможет. Вот как я заметил проблему:
onSaveInstanceState
. popBackStackImmediate
. IllegalStateException
выбрано. Вот что я чтобы решить эту проблему:
Поскольку невозможно избежать IllegalStateException
в обратном вызове, catch & amp; игнорируйте его.
try {
activity.getSupportFragmentManager().popBackStackImmediate(name);
} catch (IllegalStateException ignored) {
// There's no way to avoid getting this if saveInstanceState has already been called.
}
Этого достаточно, чтобы остановить приложение от сбоев. Но теперь пользователь восстановит приложение и увидит, что кнопка, по которой они думали, что они нажали, не была нажата вообще (они думают). Фрагмент формы все еще отображается!
Чтобы исправить это, когда создается диалог, сделайте некоторое состояние, чтобы указать, что процесс запущен.
progressDialog.show(fragmentManager, TAG);
submitPressed = true;
И сохраните это состояние в bundle.
@Override
public void onSaveInstanceState(Bundle outState) {
...
outState.putBoolean(SUBMIT_PRESSED, submitPressed);
}
Не забудьте загрузить его снова в onViewCreated
Затем, при возобновлении, откат фрагментов, если попытка отправки была ранее предпринята. Это не позволяет пользователю вернуться к тому, что кажется неприемлемой формой.
@Override
public void onResume() {
super.onResume();
if (submitPressed) {
// no need to try-catch this, because we are not in a callback
activity.getSupportFragmentManager().popBackStackImmediate(name);
submitPressed = false;
}
}
Начиная с версии поддержки версии 24.0.0, вы можете вызвать метод FragmentTransaction.commitNow()
, который совершает транзакцию синхронно, вместо вызова commit()
, а затем executePendingTransactions()
. Поскольку документация говорит, что этот подход еще лучше:
Вызов commitNow предпочтительнее вызова commit (), за которым следует executePendingTransactions (), поскольку последний будет иметь побочный эффект от попыток зафиксировать все текущие ожидающие транзакции независимо от того, является ли это желаемым поведением или нет.
popBackStackImmediate
, он немедленно провалится, если состояние сохранено. Ранее добавление фрагмента сcommitAllowingStateLoss
не играет никакой роли. Мое тестирование показывает, что это правда. Это не влияет на это конкретное исключение. Нам нужен методpopBackStackImmediateAllowingStateLoss
. – Synesso 9 January 2015 в 02:32