IllegalStateException:您使用了一个片段

Fragments have been around since forever, and so has the hatred against them. They were introduced in the Honeycomb era, when tablets were a real thing, and have stayed on since then despite the constant banter from the dev community.

碎片从此永远存在,对它们的仇恨也是如此。 它们是在平板电脑才是真正的东西的蜂窝时代引入的,并且从那时起一直沿用至今,尽管开发者社区一直在开玩笑。

https://www.reddit.com/r/androiddev/comments/2iodnx/advocating_against_android_fragments/

https://www.reddit.com/r/androiddev/comments/2iodnx/advocating_against_android_fragments/

Well, you cannot really blame the devs for feeling so strongly about fragments because they do create some not-so-easy-to-fix problems. One of the biggest challenges with fragments is the dreaded “IllegalStateException: Can not perform this action after onSaveInstanceState”. StackOverflow has probably 100s of questions on this with 1000s of answers, but still not all of us feel comfortable saying this won’t happen ever again.

好吧,您不能真正责怪开发人员对片段如此强烈的感觉,因为它们确实会造成一些不太容易解决的问题。 片段的最大挑战之一是可怕的“ IllegalStateException:在onSaveInstanceState之后无法执行此操作”。 StackOverflow可能对此有100多个问题,而答案却有1000多个,但仍然不是所有人都觉得这不会再发生了。

In this blog post, we will try to find the real culprit behind this issue and try to extend a friendly hand towards fragments one more time.

在此博客文章中,我们将尝试找到此问题的真正原因,并再次尝试向片段提供友善的帮助。

背景故事 (Background Story)

In this section, I will give some background story which is important for you to understand the real issue. You may skip whatever sub-sections you are already aware of.

在本节中,我将提供一些背景故事,这对于您理解实际问题很重要。 您可以跳过已经知道的任何小节。

活动生命周期 (Activity Lifecycle)

To summarise quickly, Android Activities have a lifecycle and everything else revolves around that for the most part. A brief, incomplete overview:

总而言之,Android活动具有生命周期,而其他所有事物在很大程度上都围绕着生命周期。 简短,不完整的概述:

  • onCreate — First method to be called, when the activity is being created.

    onCreate —创建活动时要调用的第一个方法。

  • onStart — Called after onCreate when activity is visible to the user

    onStart —在活动对用户可见时在onCreate之后调用

  • onResume — Called after onStart, when the activity is both visible and interacting with the user

    onResume —在活动同时可见并与用户交互时,在onStart之后调用

  • onPause — Called when the activity is no longer interacting with the user

    onPause —在活动不再与用户互动时调用

  • onStop — Called after onPause, when the activity is no longer visible to the user

    onStop-在活动不再对用户可见时,在onPause之后调用

  • onDestroy — called after onStop, when the activity is being torn down.

    onDestroy —在活动被拆除时在onStop之后调用。

An important point to note here is while the activity is not visible to the user, or is in stopped state, it can be killed or destroyed by the system to reclaim memory for other tasks. When it is displayed again to the user, it must be completely restarted and restored to its previous state.

这里要注意的重要一点是,当用户看不到该活动或该活动处于停止状态时,该活动可能会被系统杀死或销毁以回收用于其他任务的内存。 当再次将其显示给用户时,必须完全重新启动它并将其还原到以前的状态。

保存和恢复状态: (Saving and Restoring State:)

The activity can be destroyed by the system if it not in foreground and when this happens, all the UI state, which is in-memory, is lost too.

如果活动不在前台,则该活动可以被系统销毁,并且在发生这种情况时,所有处于内存中的UI状态也会丢失。

As a simple example, lets say the user is filling his address in an EditText of an e-commerce app. While the user is filling the field, they receive a phone call and the e-commerce app goes into background. The system decides to destroy the background applications to restore memory. When the user returns to the app, whatever data they had filled will now be lost if the state is not saved when the activity was being destroyed by the system.

举一个简单的例子,假设用户将其地址填写在电子商务应用程序的EditText中。 当用户填写该字段时,他们会接到一个电话,并且电子商务应用程序进入后台。 系统决定销毁后台应用程序以恢复内存。 当用户返回到应用程序时,如果在系统销毁活动时未保存状态,则他们填写的所有数据现在都将丢失。

To prevent this, before destroying the activity, the system gives us an opportunity to persist any data we would like to, which we can later be used to restore the UI state. The data is stored in a Bundle, which is basically a map of primitives. This can be done with two system provided callbacks:

为了防止这种情况,在销毁活动之前,系统为我们提供了一个持久化我们想要保留的数据的机会,以后可以将其用于还原UI状态。 数据存储在Bundle中,该Bundle实际上是图元的映射。 这可以通过两个系统提供的回调来完成:

  • onSaveInstanceState(Bundle) — This method is our window of opportunity to save any UI state which we would like to before the activity is destroyed. The data can be stored in the Bundle passed as a parameter to this function.

    onSaveInstanceState(Bundle) —此方法是我们有机会在活动被破坏之前保存我们想要保存的任何UI状态的机会之窗。 数据可以存储在Bundle中作为参数传递给此函数。

  • onRestoreInstanceState(Bundle) — This method is called when the activity is being re-created from a previously saved state. The Bundle passed here is the same in which we saved the information in onSaveInstanceState method. This data can be used to bring the activity ui state back to how it was when the user left

    onRestoreInstanceStateState(Bundle) -从先前保存的状态重新创建活动时,将调用此方法。 此处传递的Bundle与我们在onSaveInstanceState方法中保存信息的方式相同。 此数据可用于将活动ui状态恢复为用户离开时的状态

UI components like EditText have their own implementation of these methods and can save state automatically provided they have an id associated with them.

诸如EditText之类的UI组件对这些方法有自己的实现,并且只要它们具有关联的ID,就可以自动保存状态。

片段管理器 (Fragment Manager)

Every activity has a fragment manager, which does exactly what its name suggests, manages all the fragments and their lifecycle associated to that particular activity. Among other things, FragmentManager is also responsible for saving the state of the fragment when the parent activity is about to be destroyed by the system to free up memory.

每个活动都有一个片段管理器,它按照其名称的含义工作,管理与特定活动相关的所有片段及其生命周期。 除其他事项外,当父活动将被系统破坏以释放内存时,FragmentManager还负责保存片段的状态。

That is all the background you need to completely understand the issue here, so let’s dive right into it.

这是您完全了解这里问题所需要的全部背景,因此让我们深入研究它。

IllegalStateException:在onSaveInstanceState之后无法执行此操作 (IllegalStateException: can not perform this action after onSaveInstanceState)

Once you know the background story, the exception message becomes a lot more self explanatory.

一旦您了解了背景故事,异常消息就会变得更加自我解释。

FragmentManager throws this exception whenever we try to commit a FragmentTransaction after the onSaveInstanceState method has been called on the parent activity.

每当我们在父活动上调用onSaveInstanceState方法后尝试提交FragmentTransaction时,FragmentManager都会引发此异常。

The reason behind this is that the fragment’s host activity has already saved its UI state and might get destroyed anytime now. The fragment transaction that you are about to do now, will not be saved in the bundle and when the user returns to this activity, this transaction will not be restored.

其背后的原因是该片段的宿主活动已经保存了其UI状态,并且现在可能随时被销毁。 您现在要执行的片段事务将不会保存在捆绑软件中,并且当用户返回到此活动时,该事务将不会恢复。

To understand the issue better, let’s take a look at an example. You want to show a login fragment to the user when an API call returns with a 403 (forbidden) response. By the time the client received the response, user pressed the home button and exited the app.

为了更好地理解该问题,让我们看一个例子。 您希望在API调用返回403(禁止)响应时向用户显示登录片段。 当客户端收到响应时,用户按下了主页按钮并退出了该应用程序。

When onSaveInstanceState was called, login fragment was not added and hence FragmentManager could not save that state.

调用onSaveInstanceState时,未添加登录片段,因此FragmentManager无法保存该状态。

Now, let’s assume the system had allowed for login fragment to be added after onSaveInstanceState. Then the system decided to kill the activity as it is in background. In this case, when the user would return, they would not see the login fragment even though it was added as that transaction was not remembered. This leads to inconsistency in UI (or an illegal state) and hence the crash.

现在,假设系统允许在onSaveInstanceState之后添加登录片段。 然后,系统决定终止活动,使其处于后台。 在这种情况下,当用户返回时,即使添加了登录片段,因为它不会记住该事务,他们也不会看到登录片段。 这导致UI不一致(或非法状态),从而导致崩溃。

如何解决这个问题? (How to fix this?)

There are two ways around this issue:

有两种方法可以解决此问题:

1.我们认为可以保留一个事实,那就是该交易可能不会保存并可能导致状态丢失 (1. We decide it is okay to live with the fact that the transaction might not be saved and can result in state loss)

We can opt for this by using commitAllowingStateLoss() or commitNowAllowingStateLoss() methods to commit fragment transactions. Although this will fix the exception, the occasional UI state loss might not be the best experience for your users.

我们可以通过使用commitAllowingStateLoss ()或commitNowAllowingStateLoss ()方法来提交片段事务。 尽管这将解决异常,但偶尔的UI状态丢失对于您的用户而言可能不是最佳体验。

2.我们确保在状态恢复之前和状态保存之后都不会发生碎片事务。 (2. We ensure that no fragment transaction takes place before the state has been restored and after the state has been saved.)

As a simple rule of thumb, if your application logic permits, commit your fragment transaction in onCreate or in response to a user interaction.

作为简单的经验法则,如果您的应用程序逻辑允许,请在onCreate或响应用户交互的情况下提交片段事务。

If your fragment is added based on an async event such as a network call, things get trickier. You can never be sure of what state the activity is in when the client receives the response. Hence, use of fragments in such scenario is discouraged.

如果您的片段是基于异步事件(例如网络调用)添加的,则事情将变得更加棘手。 您永远无法确定客户端收到响应时活动所处的状态。 因此,不建议在这种情况下使用片段。

To check if it is safe to commit fragment transaction, you may rely on FragmentManager.isStateSaved. If this method returns true, you should not commit any transaction and rely on commitAllowingStateLoss or commitNowAllowingStateLoss as a last resort.

要检查提交片段事务是否安全,您可以依赖FragmentManager.isStateSaved 。 如果此方法返回true,则您不应提交任何事务,并不得已使用commitAllowingStateLoss或commitNowAllowingStateLoss。

没有银弹 (No Silver Bullet)

Fragments can get tricky sometimes when used in complex use-cases, specially if you are not aware of what is happening under the hood. This was one such scenario. Hopefully, understanding why this error is thrown puts you in a lot better position to tackle the no-longer-dreaded IllegalStateException.

在复杂的用例中使用时,片段有时会变得棘手,特别是如果您不了解幕后情况。 这就是这样一种情况。 希望了解为什么会引发此错误,使您处于更好的位置来处理不再令人恐惧的IllegalStateException

翻译自: https://proandroiddev.com/illegalstateexception-you-used-a-fragment-99816e7cf71b

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值