首页 > 解决方案 > 使用android架构组件时不应该销毁和创建的片段

问题描述

我有点傻。我有一个包含 3 个片段的游戏应用程序。片段 A、片段 B、片段 C 使用实时数据和来自 Google 的导航组件。当用户在片段 C 上完成时,我使用导航组件将返回堆栈弹回片段 A。

第一个问题:当我弹出回栈时,该片段的 onDestroy() 被调用。我的印象是,这不应该通过这篇文章发生:链接

第二个问题:一旦用户被弹回片段A,有一个选项可以导航到片段B。一旦调用该行进行导航,片段C就会以某种方式添加到返回堆栈中,并创建视图,导致我的应用程序崩溃,因为我有一个侦听器在我的 viewModel 中侦听一个变量,该变量在片段 B 中设置的片段之间共享。

我不知道为什么当我弹出返回堆栈时片段会被破坏,我也不知道为什么当我尝试导航到片段 B 时它会被创建。

我已经记录了生命周期回调,以确保片段 C 被销毁并重新创建。而且我在单步执行应用程序时监控了导航组件的后台堆栈。看到看似随机添加的片段 C。

考虑到它是观察者内部的代码导致我的应用程序崩溃,我也尝试只在 onResume() 中添加观察者并在 onDestroy() 中分离它,但是,当重新创建片段时,它通过 onResume() 并刚刚附加立即观察者

从片段 A 导航到片段 B

 viewModel.addPlayer(asPlayer).addOnCompleteListener {
            Log.d("Game", "current destination before joining: "+resources.getResourceEntryName(navController.currentDestination!!.id))
            navController.navigate(R.id.action_joinGameFragment_to_waitingFragment)
        }

观察者被调用导致崩溃。

 //so this observer is not getting detached, maybe manually detach it and reattach it in on resume
        viewModel.gameExists.observe(this, androidx.lifecycle.Observer { exists ->
            //someone ended the game
            var debug = navController
            if(!exists && navController.currentDestination?.id == R.id.gameFragment){ endGame() }
        })

崩溃的函数

 fun endGame(){
        viewModel.endGame()
        Log.d("Game", "current destination before crash: "+resources.getResourceEntryName(navController.currentDestination!!.id))
        timer.cancel()
        Log.d("Game", "current destination before poping: "+resources.getResourceEntryName(navController.currentDestination!!.id))
        navController.popBackStack(R.id.startFragment, false)
        Log.d("Game", "current destination after poping: "+resources.getResourceEntryName(navController.currentDestination!!.id))

    }

日志猫的结果

2019-06-16 20:13:40.278 13021-13021/com.dangerfield.spyfall D/Game: current destination before poping:  (FRAGMENT C)
2019-06-16 20:13:40.279 13021-13021/com.dangerfield.spyfall D/Game: current destination after poping: (FRAGMENT A)
2019-06-16 20:13:40.279 13021-13021/com.dangerfield.spyfall D/View Model: game =  null
2019-06-16 20:13:40.299 13021-13021/com.dangerfield.spyfall D/GAME: ON DESTROY
2019-06-16 20:13:40.315 13021-13021/com.dangerfield.spyfall D/View Model: game =  null
2019-06-16 20:14:05.289 13021-13021/com.dangerfield.spyfall D/Game: current destination before joining: (FRAGMENT A)
2019-06-16 20:14:05.316 13021-13021/com.dangerfield.spyfall D/Game: on Create
2019-06-16 20:14:05.317 13021-13021/com.dangerfield.spyfall D/Game: onCreateView
2019-06-16 20:14:05.353 13021-13021/com.dangerfield.spyfall D/GAME: ON RESUME
2019-06-16 20:14:05.353 13021-13021/com.dangerfield.spyfall D/Game: current destination before crash: (FRAGMENT C)
019-06-16 20:14:05.359 13021-13021/com.dangerfield.spyfall E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.dangerfield.spyfall, PID: 13021
    kotlin.UninitializedPropertyAccessException: lateinit property timer has not been initialized
        at com.dangerfield.spyfall.game.GameFragment.endGame(GameFragment.kt:158)

(游戏片段为片段C)

任何帮助/方向将不胜感激,非常感谢。

标签: androidandroid-fragmentskotlinandroid-architecture-componentsandroid-livedata

解决方案


推荐阅读