Search code examples
androidandroid-support-libraryandroid-lifecycle

Lifecycle observer inconsistent with lifecycle functions on Nexus 5


I have noticed some inconsistent behavior on Nexus 5 devices between the standard Android lifecycle methods (onCreate, onStart, etc.) and a LifecycleObserver attached to the AppCompatActivity lifecycle.

The issue appears when opening a share dialog and then closing it again.

Given this activity:

package gustavkarlsson.se.lifecycleerrordemo

import android.arch.lifecycle.Lifecycle.Event.*
import android.arch.lifecycle.LifecycleObserver
import android.arch.lifecycle.OnLifecycleEvent
import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.util.Log
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    init {
        lifecycle.addObserver(object : LifecycleObserver {
            @OnLifecycleEvent(ON_CREATE)
            fun logOnCreate() {
                Log.i(TAG, "Observed: $ON_CREATE")
            }

            @OnLifecycleEvent(ON_START)
            fun logOnStart() {
                Log.i(TAG, "Observed: $ON_START")
            }

            @OnLifecycleEvent(ON_RESUME)
            fun logOnResume() {
                Log.i(TAG, "Observed: $ON_RESUME")
            }

            @OnLifecycleEvent(ON_PAUSE)
            fun logOnPause() {
                Log.i(TAG, "Observed: $ON_PAUSE")
            }

            @OnLifecycleEvent(ON_STOP)
            fun logOnStop() {
                Log.i(TAG, "Observed: $ON_STOP")
            }

            @OnLifecycleEvent(ON_DESTROY)
            fun logOnDestroy() {
                Log.i(TAG, "Observed: $ON_DESTROY")
            }
        })
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.i(TAG, "Ran onCreate")
        email_button.setOnClickListener { openShareDialog() }
    }

    private fun openShareDialog() {
        Intent(Intent.ACTION_SEND).run {
            type = "message/rfc822"
            putExtra(Intent.EXTRA_EMAIL, arrayOf("test@mail.com"))
            putExtra(Intent.EXTRA_SUBJECT, "Subject")
            putExtra(Intent.EXTRA_TEXT, "Text")
            startActivity(Intent.createChooser(this, "Send Email"))
        }
    }

    override fun onStart() {
        super.onStart()
        Log.i(TAG, "Ran onStart")
    }

    override fun onResume() {
        super.onResume()
        Log.i(TAG, "Ran onResume")
    }

    override fun onPause() {
        super.onPause()
        Log.i(TAG, "Ran onPause")
    }

    override fun onStop() {
        super.onStop()
        Log.i(TAG, "Ran onStop")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.i(TAG, "Ran onDestroy")
    }

    companion object {
        private const val TAG = "MainActivity"
    }
}

I expect every lifecycle transition to be logged both from the observer and the corresponding lifecycle method.

However, when I click the button to open the share dialog and then dismiss it again, the behavior is different for different devices.

The Samsung Galaxy A3 behaves as I would expect:

04-03 12:53:55.394 30109-30109/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Ran onCreate
04-03 12:53:55.396 30109-30109/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Observed: ON_CREATE
04-03 12:53:55.398 30109-30109/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Ran onStart
04-03 12:53:55.398 30109-30109/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Observed: ON_START
04-03 12:53:55.404 30109-30109/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Ran onResume
04-03 12:53:55.404 30109-30109/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Observed: ON_RESUME

* Opened share menu *

04-03 12:54:02.431 30109-30109/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Observed: ON_PAUSE
04-03 12:54:02.432 30109-30109/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Ran onPause

* Closed share menu *

04-03 12:54:07.635 30109-30109/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Ran onResume
04-03 12:54:07.636 30109-30109/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Observed: ON_RESUME

The Google Nexus 5 observes ON_STOP and ON_START even though onStop and onStart are never called:

04-03 12:51:32.964 22062-22062/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Ran onCreate
04-03 12:51:32.965 22062-22062/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Observed: ON_CREATE
04-03 12:51:32.977 22062-22062/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Ran onStart
04-03 12:51:32.977 22062-22062/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Observed: ON_START
04-03 12:51:32.977 22062-22062/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Ran onResume
04-03 12:51:32.977 22062-22062/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Observed: ON_RESUME

* Opened share menu *

04-03 12:51:42.676 22062-22062/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Observed: ON_PAUSE
04-03 12:51:42.676 22062-22062/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Ran onPause
04-03 12:51:43.395 22062-22062/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Observed: ON_STOP

* Closed share menu *

04-03 12:51:47.791 22062-22062/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Ran onResume
04-03 12:51:47.791 22062-22062/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Observed: ON_START
04-03 12:51:47.791 22062-22062/gustavkarlsson.se.lifecycleerrordemo I/MainActivity: Observed: ON_RESUME

My question is: Why does the Nexus observe events even though the corresponding lifecycle method is never run? Is this a bug or am I misunderstanding the lifecycle?

Versions:

  • com.android.tools.build:gradle:3.1.0
  • org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.31
  • com.android.support:support-v4:27.1.0
  • com.android.support:appcompat-v7:27.1.0
  • compileSdkVersion 27
  • targetSdkVersion 27

I've uploaded the demo project here.


Solution

  • The Lifecycle library marks AppCompatActivity and Fragment Lifecycle objects as CREATED and dispatches ON_STOP when onSaveInstanceState() is called without waiting for a call to the onStop() method. This is described in the documentation for the library.

    The documentation states this was done to make it easier for observers not to do things that would alter the UI state after onSaveInstanceState() has been called, because such changes would not be saved if the Activity or Fragment was stopped and destroyed straight after.

    Your code didn't have any logging in onSaveInstanceState(), which is why you didn't see this method call logged.

    NB. I'm assuming that you're using version 1.0.0-rc2 or higher of the Lifecycle library, which was released October 18, 2017 (several months before your question). According to the release notes, this behavior changed in that release.