在onStop()之后finish()的onCreate()

11

我有一个 Android 的活动,在其 onStop() 方法内调用了 finish(),这样当我切换到其他活动(包括主菜单)时,该活动将会关闭。到此为止,一切都按预期工作。

但是,当我再次运行应用程序时,(有时,不总是)我注意到应用程序使用与之前相同的 PID 运行,并且它再次调用了 onCreate()。我没有看到任何对 onRestart() 的调用,因此我假设 onCreate() 调用是在 onStop() 之后直接执行的,这违反了活动生命周期的规定。当应用程序使用新的 PID 时,我可以理解为什么会调用 onCreate(),因为这是活动的开始。

有人知道这是为什么吗?

关于我正在开发的应用程序:这是一个 Unity + Vuforia + Android 应用程序。我创建了一个自定义活动,因为我需要在 Android 上创建本地 UI(而不是使用 Unity 创建)。

我在 Android 项目中发现了一个类似的问题:http://code.google.com/p/android/issues/detail?id=15331,但我不确定原因是否相同。

更新:从日志中看到,在 finish() 调用之后,并没有调用 onDestroy()。但是,如果发生了我提到的问题(该活动使用相同的进程启动),则会在活动开始时调用 onDestroy()

更新:很抱歉更新晚了。这里展示了日志的一部分。

## First run

I/ActivityManager(  265): Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=the.app/the.app.UnityAriusActivity bnds=[238,115][351,273] } from pid 423
I/ActivityManager(  265): Start proc the.app for activity the.app/the.app.UnityAriusActivity: pid=1686 uid=10013 gids={3003, 1006, 1015}
D/arius   ( 1686): UnityAriusActivity: onStart
D/arius   ( 1686): UnityAriusActivity: onResume

## Home button is pressed

I/ActivityManager(  265): Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10200000 cmp=com.sonyericsson.home/.HomeActivity } from pid 265
D/arius   ( 1686): UnityAriusActivity: onPause
D/arius   ( 1686): UnityAriusActivity: onStop

## Second run

I/ActivityManager(  265): Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=the.app/the.app.UnityAriusActivity bnds=[238,115][351,273] } from pid 423

## Same process, onStart is called again

D/arius   ( 1686): UnityAriusActivity: onStart
D/arius   ( 1686): UnityAriusActivity: onResume
I/ActivityManager(  265): Displayed the.app/the.app.UnityAriusActivity: +500ms
D/Unity   ( 1686): Creating OpenGL ES 2.0 context (RGB16 565 16/0)
W/IInputConnectionWrapper(  423): showStatusIcon on inactive InputConnection
I/QCAR    ( 1686): onSurfaceCreated

## Strangely, there's an onDestroy here

D/arius   ( 1686): UnityAriusActivity: onDestroy

## Unity apparently kills the process from its onDestroy

I/Process ( 1686): Sending signal. PID: 1686 SIG: 9
I/ActivityManager(  265): Process the.app (pid 1686) has died.
问题在于第二次运行时,在 onStart() 之后有一个 onDestroy()。我的活动基本上是 Vuforia/QCAR 活动的子类,这也是从 Unity 的活动中继承而来的。所以,在我的 onDestroy() 中,我调用了超类的方法(super.onDestroy()),并对其他我重写的方法也是同样的处理。
如果我查看 Unity 和 Vuforia/QCAR Android 库(我很好奇所以我对它们进行了反编译 - 是的,这可能不正确),在 Unity 的 onDestroy() 中,Unity 尝试杀死它自己的进程(即应用程序进程)。
Process.killProcess(Process.myPid());

所以,当这种情况发生时,我的应用程序再次崩溃。如果第二次运行使用了不同的进程,则奇怪的onDestroy()不会发生。

我还尝试了noHistory方法。但是同样的事情仍然发生:(当第二次运行使用相同的进程时,会出现迟到的onDestroy(),然后进程被Unity杀死。


我还需要在用户按下主页按钮时调用finish()。这就是为什么我从onStop()中调用它的原因。 - fajran
任何对finish()的调用都会强制重新运行onCreate,因为这将销毁Activity。 - Mohamed_AbdAllah
1
这里到底是什么问题?onDestroy()没有被调用吗?还是已经被调用了?Activity生命周期似乎要求您处理这两种可能性。 - Chris Stratton
只是一个解决方案的猜测:保持所有不变,在清单文件中将该活动属性设置为 android:launchMode="singleInstance" 或尝试使用 singleTask - MKJParekh
如果应用程序进程被终止,它应该接收一个新的PID,所以这显然是个bug。你是否尝试使用在onCreate中初始化的静态字段来检测它? - giZm0
看起来你正在尝试遵循状态机逻辑,但没有正确处理进程... - Serguei Fedorov
4个回答

12

你犯了一个可以理解但是关键的错误,认为一个新的活动一定必须在一个新的进程中运行。实际上在安卓系统中并不是这样的 - 你可以让一个新的活动实例的onCreate()方法在之前承载另一个活动实例的进程中运行。

这会导致任何相对于进程静态的东西(特别是在本地代码中,但不仅限于此)变得难以可靠地预测。

因为正在启动的活动是一个全新的活动,它不会接收到onRestart()方法 - 只有在重新启动现有的活动时才会发生。


那么你的意思是说一个完全新的活动(就其生命周期而言)可以重用以前的进程? - fajran
2
无法保证onDestroy()方法一定会被调用,特别是当进程正在被销毁时 - 可以查看Activity生命周期图表中的可杀性列。 - Chris Stratton
你可能正在看到一个配置更改周期。 - Chris Stratton
这是我关于配置更改的内容 android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|fontScale" Unity使用它们,所以我也使用它们。 - fajran
这是否意味着,即使onDestroy()尚未被调用(对于同一活动),仍有可能调用onCreate() - user16299318
显示剩余2条评论

5

@fajran - 我应该在上面提到,你在onStop()中调用finish()的方式似乎有些hack。你为什么不使用更平台友好的方法来防止你的活动进入历史堆栈呢? - Josh
我猜我这样做是因为我不知道noHistory选项,我在Android开发方面相对较新 :) 无论如何...我尝试了这个方法,不幸的是我仍然看到了这个问题 :( - fajran
好的,请稍微退后一点。你说你尝试过这个。你尝试了哪种方法?我希望在你进行这个测试时,你已经从onStop()中删除了finish()调用,对吗?那么当你将noHistory设置为true时,确切会发生什么事情呢?你能像这样描述它吗:“打开Activity A。从A打开Activity B。按Home键。从主屏幕打开应用程序。仍然看到Activity B!” - Josh
让我再强调一下,追踪生命周期并试图弯曲操作系统处理过程以符合您的意愿将导致很多挫败感。有更好的方法来确保您离开应用程序时不会返回到刚刚查看的相同活动。 - Josh

3
在您提供的链接文档中,onDestroy 的描述如下:

在销毁活动之前,您会收到的最后一个调用。这可能是因为活动正在完成(有人在上面调用了 finish()),或者因为系统暂时销毁了此活动实例以节省空间。您可以使用 isFinishing() 方法区分这两种情况。

而对于 onStop

当另一个活动已经恢复并覆盖此活动时,此活动不再对用户可见时调用。这可能是因为正在启动新活动、将现有活动置于此活动前面或销毁此活动。如果此活动要返回与用户交互,则接下来是 onRestart();如果此活动要消失,则接下来是 onDestroy()。

这意味着 finish() 调用的是 onDestroy 而不是 onStop,因此当活动重新启动时,必须调用 onCreate,因为您在 onStop 中调用 finish() 将强制运行 onDestroy

是的,我理解这部分。当活动处于非活动状态(即onStop())时,我想结束该活动。这就是为什么我在onStop()内调用finish()的原因。 - fajran

0

我遇到了类似的问题:当启动活动时,onDestroy 在 onCreate/onStart/onResume 之后奇怪地被调用。我没有明确的确认,但我的感觉是 onDestroy 调用对应于上一个活动,而不是新的活动。但由于某种原因,它的执行被延迟到进程再次被激活时(启动新活动时)。

我认为这是由于从 onStop 中调用 "finish()" 导致的。我在日志中注意到活动管理器抱怨活动报告停止,但不再停止(实际上正在完成)。所以我想知道这是否会影响活动管理器认为我的活动处于的状态。

在你的情况下,由于 onDestroy 调用,整个进程都被杀死了。由于你的新活动在与前一个活动相同的进程中启动,因此你的应用程序立即退出。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接