Android 10 - 奇怪的活动生命周期

Posted

技术标签:

【中文标题】Android 10 - 奇怪的活动生命周期【英文标题】:Android 10 - Weird activity lifecycle 【发布时间】:2020-12-09 11:55:13 【问题描述】:

我在 android 10 上遇到了一个奇怪的问题,当我开始新的横向活动时,下面的活动将重新创建。

假设有两个Activity 类:

ActivityA: orientation = unspecified
ActivityB: orientation = force landscape, full screen, opaque

如果我从ActivityA开始ActivityB,生命周期事件的日志:

D/ActivityA: onPause() called
D/ActivityB: onCreate() called
D/ActivityB: onStart() called
D/ActivityB: onResume() called
D/ActivityA: onStop() called
D/ActivityA: onDestroy() called
D/ActivityA: onCreate() called
D/ActivityA: onStart() called
D/ActivityA: onResume() called
D/ActivityA: onPause() called
D/ActivityA: onStop() called

可以清楚地看到ActivityA被重新创建了,而且ActivityA#onResumeActiviyB#onResume之后被调用了??

好的,现在我们在堆栈顶部有ActivityB,然后我按下返回按钮:

D/ActivityB: onPause() called
D/ActivityA: onStart() called
D/ActivityA: onResume() called
D/ActivityA: onPause() called
D/ActivityA: onStop() called
D/ActivityA: onDestroy() called
D/ActivityA: onCreate() called
D/ActivityA: onStart() called
D/ActivityA: onResume() called
D/ActivityB: onStop() called
D/ActivityB: onDestroy() called

ActivityA 又重新创建了?

正如我在我的设备中看到的,在ActivityB 变得可见之前,ActivityA 旋转到横向模式,当ActivityB 退出时,ActivityA 再次旋转回到纵向模式。这种行为可能会导致ActivityA 一次又一次地重新创建。

乱七八糟,你知道如何防止ActivityA在这种情况下重新创建,或者这是Android本身的错误吗?

更新 1

我可以轻松处理ActivityA上的配置更改,问题是ActivityA有一个非常复杂的视图结构,没有必要重新创建它会导致UI滞后,而且活动的生命周期回调混乱也会导致逻辑中断。

更新 2

我刚刚发现recreate() 方法被AppCompatDelegateImpl 类调用了两次,这就是为什么我得到奇怪的生命周期行为:

【问题讨论】:

从活动 A 开始带有标志单顶的活动 B 以强制活动 A 关闭 @alokHarman 这个评论毫无意义。 请提供简单的示例代码以及全屏活动的方式 alokHarman 的意思是为活动设置 android:launchMode=singleTop (q.v. developer.android.com/guide/topics/manifest/activity-element),它应该通过重新触发意图来重用活动。但我怀疑它是否会在这里起作用,因为配置更改会导致破坏(请参阅下面的答案) 【参考方案1】:

对于这种行为,您可能无能为力。如果不会给您带来太多问题,我会建议以下内容:

android:configChanges="orientation" 添加到ActivityA 的清单条目中 在ActivityA 中实现onConfigurationChanged() 并自行处理方向更改

这将防止 Android 在方向更改期间终止并重新创建 ActivityA

【讨论】:

还有一个问题,我严重依赖方法onResume 来确定堆栈顶部可见的活动,但是在这种行为中,ActivityA#onResume 在它位于 ActivityB 后面时被调用,它导致了很多逻辑问题。【参考方案2】:

这看起来像是一个特定于设备的错误。

我在 Pixel 3a (API 29 & 30) 模拟器上尝试了您的代码。这是我得到的:

ActivityA开始ActivityB

D/MainActivityA: onPause() called
D/MainActivityB: onCreate() called
D/MainActivityB: onStart() called
D/MainActivityB: onResume() called
D/MainActivityA: onStop() called

按下后退按钮:

D/MainActivityB: onPause() called
D/MainActivityA: onStart() called
D/MainActivityA: onResume() called
D/MainActivityB: onStop() called
D/MainActivityB: onDestroy() called

您应该 file an issue 使用适当的 Android 版本和使用的设备

问题中关于更新 2 的想法

日志显示正在调用onConfigurationChanged(Configuration)。 Documentation 指出 Activity 将在运行时配置更改时重新创建。

您可以做的是在您的活动中覆盖onConfigurationChanged(Configuration) 并记录配置对象以查看更改了哪些设备配置。可能是OrientationScreen sizeScreenLayoutKeyboard availability

【讨论】:

感谢您在您的设备上进行测试,我只是运行了一个示例项目并发现它不是因为方向问题,请参阅我的更新。【参考方案3】:

不仅你面临这个问题(很多人都面临这个问题),这是一个需要修复的错误,实际上它不应该被弄乱我可以知道你使用的是哪个设备吗?您可以在评论中告诉。

【讨论】:

我的设备是三星M10,android 10,One UI 2.0 您知道其他地方讨论过的任何相关问题吗?【参考方案4】:

在横向/纵向上重新创建活动是 Android 的一项设计功能(不是错误),因为以编程方式重绘 UI 比计算元素的重新定位/调整大小更容易。即使没有 B,您也应该在 A 上翻转方向时获得相同的效果。Android 文档 (https://developer.android.com/guide/components/activities/state-changes#cco) 中也很好地描述了这一点

这应该不是太大的问题,只要您不保留对活动本身的任何引用;此时的资源已被缓存,因此几乎没有闪存 I/O,大部分工作将是重新创建视图。

【讨论】:

在我看来更像是一个错误,如果 ActivityA 仅设计用于纵向模式怎么办?又怎么能解释为什么在ActivityB#onResume之后调用ActivityA#onResume(ActivityA在ActivityB后面)。 根据您的说法,ActivityA 可以是纵向或横向(您注明“未指定”)。仅将其设为纵向应该会停止过渡。【参考方案5】:

按照官方 Activity Lifecycle onDestroy() 文档中的说明,默认情况下,每次轮换后都会重新创建 Activity。

如果您希望 Activity 的 UI 状态在整个配置更改(例如旋转)期间保持不变,则应使用 ViewModel、onSaveInstanceState() 和/或本地存储的组合来保留用户的瞬态 UI 状态,如在Saving and restoring transient UI state

您可以使用 AndroidManifest 中活动标签的配置更改属性覆盖此行为。有关更多详细信息和不同选项,请参阅Handle configuration changes

最后,您可以在此处了解有关Activity Lifecycle in Context of Screen Rotation 的更多信息。

【讨论】:

以上是关于Android 10 - 奇怪的活动生命周期的主要内容,如果未能解决你的问题,请参考以下文章

(Android第一行代码活动的生命周期)生命周期

android 活动的生命周期

[Android]Activity的生命周期

Android 活动生命周期

Android 活动生命周期 - 所有这些方法的用途是啥?

Android之Activity的生命周期