android: 返回导航后恢复 Activity 状态需要什么?

android: what is needed to restore Activity status after back-navigation?

免责声明:我是一名经验丰富的程序员,但几乎是 Android 的新手;我可能问的很明显。

我的暂定应用程序有一个相当简单的 GUI。

具体来说我有一个最顶层的Switch来转我的申请on/off.

启动时开关关闭。

当它打开时,我可以 select 从子菜单 MonitorActivity 显示正在发生的事情(某种 运行 日志)。

MonitorActivity 正确链接到父级,所以我可以返回到 MainActivity

到目前为止,还不错。一切正常。

问题是当我向后导航时 MainActivity 完全重置,就像重新开始一样;特别是我的 on/off Switch 回到了 off 位置。

我对relevant documentation的理解是MainActivity应该停止了;相关片段是:

The previous activity remains in the stack, but is stopped. When an activity stops, the system retains the current state of its user interface. When the user presses the Back button, the current activity is popped from the top of the stack (the activity is destroyed) and the previous activity resumes (the previous state of its UI is restored).

由此我明白我不应该做任何特定的事情来恢复 MainActivity 状态;这是正确的吗?

我应该 post 我的 AndroidMainfest.xml (或其他文件)吗?

更新(按要求):

我严格遵循 MyFirstApp:

中的模式

注意:这正是 MyFirstApp 的行为:从 DisplayMessageActivity 返回时 EditText 小部件的内容消失了(即使 none 明确清除它)。

也许我应该重命名问题:"How can I preserve 'message' after 'Send' in 'MyFirstApp'?"

UPDATE2:我覆盖了几个回调来追踪真正发生的事情; 这是日志(内联评论,查找 '<<<<<'):

02/27 16:05:24: Launching app
$ adb install-multiple -r -t -p it.condarelli.myfirstapp /home/mcon/AndroidStudioProjects/MyfirstApp/app/build/intermediates/split-apk/debug/slices/slice_2.apk 
Split APKs installed in 483 ms
$ adb shell am start -n "it.condarelli.myfirstapp/it.condarelli.myfirstapp.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -D
Waiting for application to come online: it.condarelli.myfirstapp | it.condarelli.myfirstapp.test
Waiting for application to come online: it.condarelli.myfirstapp | it.condarelli.myfirstapp.test
Connecting to it.condarelli.myfirstapp
Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page.
I/elli.myfirstap: Not late-enabling -Xcheck:jni (already on)
W/elli.myfirstap: Unexpected CPU variant for X86 using defaults: x86
W/ActivityThread: Application it.condarelli.myfirstapp is waiting for the debugger on port 8100...
I/System.out: Sending WAIT chunk
I/System.out: Debugger has connected
    waiting for debugger to settle...
Connected to the target VM, address: 'localhost:8603', transport: 'socket'
I/System.out: waiting for debugger to settle...
I/System.out: waiting for debugger to settle...
I/System.out: waiting for debugger to settle...
I/chatty: uid=10085(it.condarelli.myfirstapp) identical 2 lines
I/System.out: waiting for debugger to settle...
I/System.out: debugger has settled (1354)
W/elli.myfirstap: JIT profile information will not be recorded: profile file does not exits.
I/chatty: uid=10085(it.condarelli.myfirstapp) identical 10 lines
W/elli.myfirstap: JIT profile information will not be recorded: profile file does not exits.
I/InstantRun: starting instant run server: is main process
I/MainActivity: onCreate(null)             <<<<< This is App start
W/elli.myfirstap: Accessing hidden method Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z (light greylist, reflection)
    Accessing hidden method Landroid/view/ViewGroup;->makeOptionalFitsSystemWindows()V (light greylist, reflection)
I/MainActivity: onStart()
I/MainActivity: onResume()
I/MainActivity: onPostResume()
D/OpenGLRenderer: HWUI GL Pipeline
D/: HostConnection::get() New Host Connection established 0xcb81c0c0, tid 17161
I/ConfigStore: android::hardware::configstore::V1_0::ISurfaceFlingerConfigs::hasWideColorDisplay retrieved: 0
    android::hardware::configstore::V1_0::ISurfaceFlingerConfigs::hasHDRDisplay retrieved: 0
I/OpenGLRenderer: Initialized EGL, version 1.4
D/OpenGLRenderer: Swap behavior 1
W/OpenGLRenderer: Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...
D/OpenGLRenderer: Swap behavior 0
D/EGL_emulation: eglCreateContext: 0xde3584a0: maj 2 min 0 rcv 2
D/EGL_emulation: eglMakeCurrent: 0xde3584a0: ver 2 0 (tinfo 0xcb943060)
D/EGL_emulation: eglMakeCurrent: 0xde3584a0: ver 2 0 (tinfo 0xcb943060)
I/AssistStructure: Flattened final assist data: 2344 bytes, containing 1 windows, 8 views
I/MainActivity: onPause()                  <<<<< This is first message after I pressed 'Send' button.
W/ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy@18e99e1
I/DisplayMessageActivity: onStart()
I/DisplayMessageActivity: onResume()
I/DisplayMessageActivity: onPostResume()
D/EGL_emulation: eglMakeCurrent: 0xde3584a0: ver 2 0 (tinfo 0xcb943060)
D/EGL_emulation: eglMakeCurrent: 0xde3584a0: ver 2 0 (tinfo 0xcb943060)
D/EGL_emulation: eglMakeCurrent: 0xde3584a0: ver 2 0 (tinfo 0xcb943060)
D/EGL_emulation: eglMakeCurrent: 0xde3584a0: ver 2 0 (tinfo 0xcb943060)
D/EGL_emulation: eglMakeCurrent: 0xde3584a0: ver 2 0 (tinfo 0xcb943060)
I/MainActivity: onStop()
I/MainActivity: onSaveInstanceState()      <<<<< This is where log stops while DisplayMessageActivity is focused
I/DisplayMessageActivity: onPause()        <<<<< This is first message after Back-navigation
I/MainActivity: onDestroy()                <<<<< WHY THIS NOW??
W/ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy@bd33145
I/MainActivity: onCreate(null)             <<<<< MainActivity is recreated from scratch
I/MainActivity: onStart()
I/MainActivity: onResume()
I/MainActivity: onPostResume()
D/EGL_emulation: eglMakeCurrent: 0xde3584a0: ver 2 0 (tinfo 0xcb943060)
D/EGL_emulation: eglMakeCurrent: 0xde3584a0: ver 2 0 (tinfo 0xcb943060)
D/EGL_emulation: eglMakeCurrent: 0xde3584a0: ver 2 0 (tinfo 0xcb943060)
I/DisplayMessageActivity: onStop()
I/DisplayMessageActivity: onDestroy()

我做错了什么?

我认为您可以存储 MainActivity 的状态并使用 startActivityForResult() 启动子项。

这里有一个 kotlin 的例子(我省略了代码中不相关的部分):

class ProductDataActivity : BaseActivity(),ProductDataFragment.ProductDataListener {

    private var productData: ProductBinding? = null

    companion object {
        private const val INSTANCE_STATE_PRODUCT_DATA = "state_product_data"
        private const val RESULT_DATA = 1
    }

    override fun onSaveInstanceState(outState: Bundle?) {
        outState?.putSerializable(INSTANCE_STATE_PRODUCT_DATA, this.productData)
        super.onSaveInstanceState(outState)
    }

    override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
        this.productData = savedInstanceState?.getSerializable(INSTANCE_STATE_PRODUCT_DATA) as ProductBinding
        super.onRestoreInstanceState(savedInstanceState)
    }

    override fun onDataClicked() {
        val intent = TabProductDetailsActivity.getCallingIntent(this, productData!!, false)
        this.startActivityForResult(intent, RESULT_DATA)
    }

另一个activity:

class TabProductDetailsActivity : BaseActivity() {

    private var productData: ProductBinding? = null //The activity is started with an intent as you can see above in the ProducDataActivity.

   companion object {
        const val INTENT_EXTRA_PRODUCT_DATA = "intent_product_data"
    }

   override fun onBackPressed() {
        val resultIntent = Intent()
        resultIntent.putExtra(INTENT_EXTRA_PRODUCT_DATA, productData)
        setResult(Activity.RESULT_OK, resultIntent)
        finish()
    }

我希望你能理解我在这里做什么,基本上我是将一个对象从一个 activity 传递到另一个,当我按下后退按钮时,我没有遇到启动 activity 的问题=20=]。不确定这是否正是您想要的,但我认为它为您可以采用的众多方法之一提供了一些启示。

事实证明 "culprit" 是 android.support.v7.app.AppCompatActivity 的用法, 似乎 (经过大量调试)无条件调用 requestDestroy() dispatchMessage() 的结尾在 startActivity(intent) 发起的链的末尾调用。

onSaveInstanceState() 的默认实现尽职尽责地将小部件状态保存在 Bundle 中,但那是 而不是 传递给 MainActivity.onCreate(null)

制作 public class MainActivity extends Activity { ...(而不是 public class MainActivity extends AppCompatActivity)可以解决问题(它还会对 ActionBar 和其他组件造成严重破坏,但这是另一回事了)。

您的问题与您使用 "up navigation"(即:菜单栏中的箭头)而不是使用返回按钮有关。我假设,如果您按返回按钮 return 到您的 MainActivity,一切都会如您所愿。

您应该通过删除以下内容来禁用 "up navigation":

android:parentActivityName=".MainActivity"

来自清单中的 <activity> 声明。

当您详细了解如何自定义 "up navigation" 的行为时,您可以确保当您使用它返回时,不会使用前一个 Activity 的新实例已创建,但重复使用现有实例。