[12] Android Activity Lifecycles - mariamaged/Java-Android-Kotlin GitHub Wiki

Android - Activity Lifecycles

  1. The user taps a launcher icon to start our activity.
  2. Then, the user rotates the screen, causing a configuration change.
  3. Later, the user presses BACK to return to the launcher.

While those things were going on, Android was calling lifecycle methods on our activity, to let us know what is going on.

  • An activity, generally speaking, is in one of four states at any point in time:
    • Active: the activity was started by the user, is running, and is in the foreground. This is what you are used to thinking of in terms of your activity's operation.
    • Paused: the activity was started by the user, is running, and is visible, but another activity is overlaying part of the screen. During this time, the user can see parts of your activity but may not be able to interact with it.
    • Stopped: the activity was started by the user, is running, but is completely hidden by other activities that have been launched or switched to.
    • Destroyed: the activity was destroyed, perhaps due to the user pressing the BACK button.

1. onCreate() and onDestroy()

  • onCreate() will be called in two primary situations:
    • When the activity is launched by the user, such as from a launcher icon, onCreate() will be created with a null parameter.
    • If the activity undergoes a configuration change, by default your activity will be re-created and onCreate() will be called.

In general, onCreate() is where you initialize your user interface and set up anything that needs to be done once, regardless of how the activity gets used.

  • onDestroy() may be called on the end of the lifecycle when the activity is shutting down.
    • The activity called finish().
    • The user presses the BACK button.
  • Hence, onDestroy() is mostly for:
    • Cleaning resources you obtained in onCreate() (if any).
    • Plus making sure that anything that you started up outside of lifecycle methods gets stopped, such as background threads.

Bear in mind, though, that onDestroy() may not be called. This would occur in a few circumstances:

  • You crash with an unhandled exception.
  • The user force-stops your application, such as through the Settings app.
  • Android has an urgent need to free up the RAM (e.g., to handle an incoming phone call), wants to terminate your process, and cannot take the time to call all the lifecycle methods.
     
  • If the user presses HOME to being up the home screen, your activity is not automatically destroyed.
  • onDestroy() will not be called until Android does decide to gracefully terminate your app, and that could be seconds, minutes, or hours later.

2. onStart(), onRestart(), and onStop()

  • An activity can come to the foreground either because:
    • It is first being launched, or because it is being brought back to the foreground after having been hidden (e.g., by another app's activity).
  • The onStart() method is called in either of those cases.
  • The onRestart() method is called in the case where the activity had been stopped and is now restarting.
     
  • Conversely, onStop() is called when the activity is about to be stopped.
  • Primarily, in onStop(), you clean up everything you set up in onStart().
     
  • Once started, your activity is visible, at least partially.
  • Anything that should be happening while your activity is visible should be set up in onCreate() or onStart() and cleaned up in onStop().

3. onPause() and onResume()

  • The onResume() method is called just before your activity comes to foreground, either:
    • After being initially launched.
    • Before restarted from a stopped state.
    • After a pop-up dialog (e.g., incoming call) is cleared.
  • When your activity is resumed and is now fully in the foreground, the user can interact with it:
    • They can interact with widgets.
    • Navigation button clicks, such as BACK, affect your activity.
    • Hardware inputs, such as from a keyboard, is sent to your activity.
  • Conversely, anything that takes over user input-the activation of another activity- will result in your onPause() being called.
    • Here, you should undo anything you did on onResume().

Once onPause() is called, Android reserves the right to kill of your activity's process at any point. Hence, you should not be relying upon receiving any further events.

4. Making the Superclass Happy

If you override a lifeycle method, you need to chin to the superclass' implementation of the method.

  • Otherwise, you will crash at runtime with a SuperNotCalledException.

Android Studio will warn you if you implement a lifecycle method and fail to chin to the superclass.

  • It is safest:
    • Chain to the superclass before doing your own work for the creation methods (onCreate(), onStart(), onResume()).
    • Chain to the superclass after doing your own work for the destruction set of methods (onPause(), onStop(), onDestroy()).

5. Context Anti-Pattern: Outliving It

private lateinit var doNotDoThis: View

class MainActivity: AppCompatActivity() {
	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)

		setContentView(R.layout.activity_main)

		doNotDoThis = showElapsed // from the SimpleBloom, showElapsed is a widget.
	}
}
  • We take a widget from our activity's layout and we assign it to a global property.
  • On this surface, this may not seem all that bad.
    • However, when the activity is destroyed - due to a configuration change, BACK button press, etc. - we now have a memory leak.

Each widget holds a reference to the activity that created it.

  • In this case, doNotDoThis has a reference back to our MainActivity.
  • Since doNotDoThis is global in scope, that widget cannot be garbage collected.
  • And since the doNotDoThis reference prevents the widget from being garbage collected, it prevents the destroyed activity from being garbage collected.

In general, be very careful about having objects that are tied to some Context outlive a Context itself.

This can include forking a background thread that has a reference to a Context, as that Context cannot be garbage-collected until the thread terminates.