Android Lifecycle Basics in Practice


Top-Level Items

Android applications must conform to the Android OS’s concepts of application lifecycle. Applications failing to code for application lifecycle can be less responsive, less stable and less compatible across the range of Android devices than an app that even handles simple lifecycle events. This whitepaper is designed to provide guidelines and tips for application developers with respect to handling common lifecycle events.

Note: This document is not a replacement for a basic understanding of Android application development. It is merely additional, practical documentation on Android application lifecycle behavior in real applications on commercial devices. For Android application basics, visit http://developer.android.com/index.html.

Android applications are based on several components, the most visible of which is the Activity class (“visible” is literal, because it implements all UI and views in an Android application). Android specifies the lifecycle of each Activity in an application in detail, known as the Android Activity Lifecycle.

Note: More and more developers are developing their applications as pure native apps, using NativeActivity and native_app_glue from the NDK. Using these constructs does NOT automatically handle any additional Android lifecycle events for you. This document is just as appropriate for developers of pure-native games as it is for explicitly Java/JNI-developed applications. You will still need to code and test your application for Android lifecycle events. For an introduction to NativeActivity, see: http://developer.android.com/reference/android/app/NativeActivity.html.

For documentation on NativeActivity, see the NDK’s $NDKROOT/docs/NATIVE-ACTIVITY.HTML.

In addition, see the samples in the Tegra Android Native Samples Pack.

Simple Android applications, especially 3D games that handle all user interfaces in the 3D engine tend to be single-Activity Android applications. As a result, the concepts of "application lifecycle" and "Activity lifecycle" can be considered as one and the same. We will do so for the purposes of this document.

The Android lifecycle is exposed to applications via a set of callback member functions in the application’s Java code. At a top level, the basic sequence of Android lifecycle callbacks follow a sort of "stack," along with a set of additional callbacks that appear to happen outside of this strictly hierarchical sequence. We note the items that should likely be handled in each. This list is not exhaustive and covers mainly the common ones.

NativeActivity and native_app_glue

Applications using NativeActivity or native_app_glue must handle all of the Android Activity lifecycle cases as do Java apps. These native constructs are not a free pass from lifecycle handling.

While the core of this document describes the Android lifecycle in terms of the Android Java-level lifecycle callbacks, the NDK “pure native” frameworks NativeActivity and native_app_glue have messages/callbacks that match each of the important lifecycle callbacks. To better understand how a native application can and should code for Android lifecycle, we recommend reading the document normally, then re-reading the section Mapping Lifecycle to Native Code to understand the specifics of lifecycle constructs and native coding.

In most cases, the following mappings are very direct:

In fact, these are generally obvious 1-to-1 mappings. Everything in this document relating to the Java-level Activity methods should apply. The only changes tend to be basic syntax and tactics. So the entire document applies to these cases as well. Where possible, some specifics that differ between Java, NativeActivity and native_app_glue are discussed in-line in each section.

The Lifecycle Hierarchy Events

The following events follow a basic hierarchy as indicated by indentation. They are all override-able members of Activity.

onCreate: called to set up the Java class for the instance of the app

onStart: technically, called to initiate the "visible" lifespan of the app; at any time between onStart and onStop, the app may be visible. We can either be onResumed or onStopped from this state. Note that there is also an event for onRestart, which is called before onStart if the application is transitioning from onStop to onStart instead of being started from scratch.

onResume: technically, the start of the "foreground" lifespan of the app, but this does not mean that the app is fully visible and should be rendering (this will be discussed in more detail later in this document).

onPause: the app is losing its foreground state; this is normally an indication that something is fully covering the app. On versions of Android before Honeycomb, once we returned from this callback, we could be killed at any time with no further app code called. We can either be onResumed or onStopped from this state.

onStop: the end of the current visible lifespan of the app – we may transition to on(Re)Start to become visible again, or to onDestroy if we are shutting down entirely. Once we return from this callback, we can be killed at any time with no further app code called on any version of Android.

onDestroy: called when the Java class is about to be destroyed. Once this function is called, there is only one option for transition (other than being killed): onCreate.

The non-Hierarchy Events

Another set of important events are not hierarchical; we have seen them come in various levels of the previously described hierarchy events. These events cover two categories: window focus and surface status.

Window Focus Callbacks

Window focus generally indicates whether an application’s window is the top-most, visible and interact-able window in the system. There are two functions that relate window focus, and they seem to be redundant for applications with a single major view: Activity::onWindowFocusChanged and View::onWindowFocusChanged. Each of these functions is passed a Boolean that indicates whether the callback is denoting focus gained or lost.

At onCreate, the application does not have focus; initially, any app-cached “focus” flag should be false. Applications should be looking for an initial focus gained callback. In practice, focus gain messages tend to come in a stricter range of lifecycle; they tend to come only between onResume and onPause callbacks. In other words, most “focus gained” callbacks come after a launched application has already received onCreate, onStart, and onResume.

We might expect that this process is fully hierarchical; we might assume that since we generally receive “focus gained” callbacks when we are in the resumed state that perhaps we always receive “focus lost” callbacks while we are still resumed. In other words, that the sequence would always be of the form onResume, focus gained, focus lost, onPause. However, focus lost callbacks do not tend to fall tightly into the expected hierarchy. As can be seen from callback sequences later in this document, an application may not receive a focus lost message before its onPause callback is called. In fact, in cases of quick shutdown for configuration changes, the system may go from resumed and focused all the way to onDestroy without ever indicating focus lost. So onPause and onWindowFocusChanged must be used in tandem to determine the visible and interact-able state of the app. It is this lack of focus lost callbacks at expected times which places the window focus events in the “non-hierarchical” category.

Surface Callbacks

The other forms of non-hierarchical callbacks of great importance to 3D applications are the surface callbacks. There are three of them, and they are associated with an application’s SurfaceView(s). Since most 3D games will use a single, full-screen SurfaceView, we will assume a singleton. This singleton SurfaceView is the host of the application’s EGL Surface for rendering, and is thus pivotal to the app.

Note that some applications may choose to use GLSurfaceView to wrap their OpenGL ES rendering structure. While we do not assume this use in the course of this document (we wish to handle the rendering more directly), the surface callbacks remain in place.

The three callbacks are: surfaceCreated, surfaceChanged, and surfaceDestroyed. These three callbacks form a hierarchy within themselves, as you will always receive a surfaceCreated callback to indicate a surface is available for the SurfaceView, one or more surfaceChanged callbacks to indicate that format or (most commonly) width and/or height have changed, and then a surfaceDestroyed callback to indicate that the SurfaceView no longer makes a surface available.

These are pivotal to 3D applications because the application’s EGL Surface may only exist between the surfaceCreated and surfaceDestroyed callbacks. Furthermore, since most Android EGL implementations require that you bind a non-NULL surface whenever you bind a non-NULL context, until you have a valid surface, you cannot bind a context and thus cannot load any 3D resources.

While these functions form their own hierarchy, they can interleave into the overall hierarchy of the lifecycle in rather complex ways. Like window focus, we generally see windows initially created (surfaceCreate) by an app in the resumed state. However, in practice, we see surfaceDestroyed callbacks after onPause or even after onDestroy. As a result, applications may need to handle shutting down EGL before their surfaces are destroyed.

Mapping Lifecycle to Native Code

Developers writing “pure native” applications via NativeActivity and native_app_glue should not delude themselves; their applications are still based on a Java Android Activity that is built into the OS image. As a result, the developer must still handle all of the aforementioned Android lifecycle cases. Fortunately, all of the previously discussed Java application callbacks are directly represented in both NativeActivity and native_app_glue. The following table lists the mappings:

Java Activity member NativeActivity callback native_app_glue message
onCreate ANativeActivity_onCreate android_main
onStart ::onStart APP_CMD_START
onResume ::onResume APP_CMD_RESUME
onPause ::onPause APP_CMD_PAUSE
onStop ::onStop APP_CMD_STOP
onDestroy ::onDestroy APP_CMD_DESTROY
onWindowFocusChanged ::onWindowFocusChanged APP_CMD_GAINED/LOST_FOCUS
surfaceCreated ::onNativeWindowCreated APP_CMD_INIT_WINDOW
surfaceChanged ::onNativeWindowResized APP_CMD_WINDOW_RESIZED
surfaceDestroyed ::onNativeWindowDestroyed APP_CMD_TERM_WINDOW
onConfigurationChanged ::onConfigurationChanged APP_CMD_CONFIG_CHANGED

Native application developers can handle all lifecycle events in ways completely analogous to the Java-level discussions. In fact, the availability of EGL in native code when using NativeActivity makes handling contexts and configs even easier than in the pre-GB JNI situation (where the EGL code had to be written in Java and called from native code via JNI).

 

 

 


NVIDIA® GameWorks™ Documentation Rev. 1.0.220830 ©2014-2022. NVIDIA Corporation and affiliates. All Rights Reserved.