Search code examples
androidandroid-intentandroid-activityactivity-finish

Difference between switching from activity A->B->C- back to A via Intent vs several calls to finish()


Say I have three activities A, B and C which are used in the following way. You start on activity A and press a button which takes you to B where you press another button that takes you to C. Now while on C you are done and want to go back to A. There are probably many ways of doing this, but I am interested in the difference between going back to A via an Intent vs using finish().

Using Intent: Intent(this, A::class.java).also { startActivity(it) }

Using finish(): In order to properly end up back in A, I believe I would have to do something like this.

// Activity_B.kt
...
override fun onResume() {
    super.onResume()
    if(resumed > 0) { // resumed is "private var resumed: Int = 0"
        // Next time we resume, we'll enter here.
        // Specifically, we end up here after C calls finish()
        finish() // This takes us back to activity A.
    } else {
        // End up here after first time A is created
        resumed++
    }
}
// Activity_C.kt
...
    finish() // This takes us back to Activity_B and onResume() is called
...

Using Intents we immediately end up in A while if we do it in the second way we have to pass back through B first. My question is whether one is more appropriate than the other? For some reason the second way using finish() feels more natural because we backtrack back by finishing up the use of activites while when using intents it feels like we're "leaving activites waiting and unfinished" in some sense. Is there actually a difference or are they equivalent in behaviour "behind the scenes"?


Solution

  • The primary difference is what you end up with in your back stack. If you go back to A using an intent you will end up with a back stack of [A, B, C, A], which means if the user presses the back button on their phone after doing this, they will go back to C. Pressing back again will then take them back to B. Depending on what these activities are, this may or may not be desired behavior.

    If you use finish() the activities are removed from the back stack, so after going back to A your back stack would not include B and C, it would just be [A] However, you should use an onActivityResult callback or just finish B before moving to C instead of relying on onResume for that. Using onResume would have unintended side effects, for example if the user minimized your app while on Activity B (e.g. to check their email or answer a call) - when they came back to your app it would call onResume and trigger finish() erroneously.

    Method 1 - Activity Result Callback

    The flow for this would be:

    1. Activity A starts activity B
    2. Activity B starts activity C using an appropriate onActivityResult callback. In this callback, activity B will call finish() when it is notified that C is finished.
    3. When activity C is done, it sets its result status to complete (RESULT_OK) and calls finish() - which will notify activity B that it must also call finish()

    For example, these would be in Activity B:

    // Activity B
    var goToC = registerForActivityResult(StartActivityForResult()) { result ->
        if (result.resultCode == Activity.RESULT_OK) {
             // This will be called when C is completed
             finish();
        }
    }
     
    private fun goToActivityC() {
        val intent = Intent(this, ActivityC::class.java)
        goToC.launch(intent)
    }
    

    And when activity C is done you would call this method to set its result and finish itself (taking the app back to B and immediately triggering the callback above). Note that if the user pressed back, the result wouldn't be set and they would just go back to B without triggering the callback.

    // Activity C
    private fun allDone() {
        Intent data = Intent()
        setResult(Activity.RESULT_OK, data)
        finish()
    }
    

    Method 2 - Finish B before launching C

    Another alternative to this would be to just finish B before you start C, so then Activity B would use

    private fun goToActivityC() {
        val intent = Intent(this, ActivityC::class.java)
        startActivity(intent)
        finish() // finish and remove B from the back stack
        // back stack will now be [A, C]
    }
    

    and in Activity C

    private fun allDone() {
        finish() // finish and remove C from the back stack
        // Will take you back to A and back stack will be just [A]
    }
    

    The main downside of this approach is that if the user presses back while on C they will go back to A, not B. This may not be intuitive behavior (really depends what these activities are).