Understanding the Android lifecycle
This tutorial describes how the Android application and activity lifecycle works.
Table of Contents
Android devices have limited resources, therefore the Android system is allowed to manage the available resources by terminating running processes or recycling Android components.
In addition to resource management, Android also recreates activities in case a configuration change occurs. The
Configuration
object contains the current device configuration, if this configuration changes activities are restarted, as they may use different resources for this configuration.For the user of the device this should happen transparently, i.e. he should not note if an Android component have been terminated nor not.
To support this the Android platform supports lifecycle event which are called in the case of process or component termination as well as in case of a configuration change. The developer is responsible for maintaining the state of the application. He is also responsible for restore the activity instance state. The instance state of an activity is the nonpersistent data that needs to be passed between activities restarts during a configuration change to restore user selections.
The application object is created whenever one of your Android components are started. It is started in a new process with a unique ID under a unique user. Even if you do not specify one in your
AndroidManifest.xml
file, the Android system creates a default object for you. This object provides the following main lifecycle methods:- onCreate() - called before the first components of the application starts
- onLowMemory() - called when the Android system requests that the application cleans up memory
- onTerminate() - only for testing, not called in production
- onConfigurationChanged() - called whenever the configuration changes
The application object starts before any component and runs at least as long as another component of the application runs.
If the Android system needs to terminate processes it follows the following priority system.
Table 1. Priorities
Process status | Description | Priority |
---|---|---|
Foreground | An application in which the user is interacting with an activity, or which has an service which is bound to such an activity. Also if a service is executing one of its lifecycle methods or a broadcast receiver which runs its onReceive() method. | 1 |
Visible | User is not interacting with the activity, but the activity is still (partially) visible or the application has a service which is used by a inactive but visible activity. | 2 |
Service | Application with a running service which does not qualify for 1 or 2. | 3 |
Background | Application with only stopped activities and without a service or executing receiver. Android keeps them in a least recent used (LRU) list and if requires terminates the one which was least used. | 4 |
Empty | Application without any active components. | 5 |
The Android system is also allowed to recycle Android components to free up resources. This part explains which for activities, the lifecycle of other components is described in the respective part of these components.
An activity can be in different states which are described by the following table.
Table 2. Activity state
State | Description |
---|---|
Running | Activity is visible and interacts with the user. |
Paused | Activity is still visible but partially obscured, instance is running but might be killed by the system. |
Stopped | Activity is not visible, instance is running but might be killed by the system. |
Killed | Activity has been terminated by the system of by a call to its finish() method. |
The user should not notice if an activity which is still part of an activity stack has been terminate or not. For this the developer needs to store the state of the activity at the right point in time and restore it. He also should stop any unnecessary actions if the activity is not visible anymore to save system resources.
The Android system defines a lifecycle for activities for activities via predefined (lifecycle) methods. The most important methods are:
Table 3. Important Activity lifecycle methods
Method | Purpose |
---|---|
onCreate() | Called then the activity is created. Used to initialize the activity, for example create the user interface. |
onResume() | Called if the activity get visible again and the user starts interacting with the activity again. Used to initialize fields, register listeners, bind to services, etc. |
onPause() | Called once another activity gets into the foreground. Always called before the activity is not visible anymore. Used to release resources or save application data. For example you unregister listeners, intent receivers, unbind from services or remove system service listeners. |
onStop() | Called once the activity is no longer visible. Time or CPU intensive shut-down operations, such as writing information to a database should be down in the onStop() method. This method is guaranteed to be called as of API 11. |
The life cycle of an activity with its most important methods is displayed in the following diagram.
Android has more life cycle methods but not all of these methods are guaranteed to be called. The
onDestroy()
method is not guaranteed to be called, hence you typically do not use it. For more information on the other methods see Activity lifecycle - Official documentation .Instance state of an activity which is required to restore the activity to the state in which the user left it.
Assume for example the user scrolled through a
ListView
with thousands of items and the activity is recreated. Loosing the position in the list is annoying for the user, hence the position should be restored.The
onSaveInstanceState()
can be used to store this instance state as a Bundle
. A Bundle
can contain primitive data types, arrays, String and objects which are of the Parcelable
orSerialisable
type.The persisted
Bundle
data is passed at restart of the activity to the onCreate()
method andonRestoreInstanceState()
as parameter.If you override
onSaveInstanceState()
and onRestoreInstanceState()
you should call the super implementation of it, because the default views of Android store their data via a call toView.onSaveInstanceState
from the onSaveInstanceState()
method of the activity. For example EditText
stores its content via the default call of this method.The
onRestoreInstanceState()
or the onCreate()
methods can be used to recreate the instance scope of an activity if it is restarted.If the user interacts with an activity and presses the Back button or if the
finish()
method of an activity is called, the activity is removed from the current activity stack and recycled. In this case there is no instance state to save and the onSaveInstanceState()
method is not called.If the user interacts with an activity and presses the Home button, the activity instance state must be saved. The
onSaveInstanceState()
method is called. If the user restarts the application it will resume or restart the last running activity. If it restarts the activity it provides the bundle with the save data to the onRestoreInstanceState()
and onCreate()
methods.Nonconfiguration instance scope are Java objects which need to passed from one instance to the next instance of an activity in case of an configuration change.
Saving and restoring one object was possible with the
getLastNonConfigurationInstance()
and onRetainNonConfigurationInstance()
methods. These methods have been deprecated, you should prefer using headless retained fragments for holding to objects which should be passed between activity instances due to configuration changes.An activity is restarted if a configuration change occurs. A configuration change happens if an event is triggered from the actual the Android device which may be relevant for the application.
An instance of the
Configuration
class defines the current configuration of the device. Typical configuration is the device orientation, the locale the smallest width, etc.For example if the user changes the orientation of the device (vertically or horizontally). Android assumes that an activity might want to use different resources for these orientations and restarts the activity.
In case an activity is restarted the programmer must ensure that the activity is recreated in the same state as before the restart. The Android provides several potential means for doing this.
In the emulator you can simulate the change of the orientation via the Ctrl+F11 shortcut.
You can avoid a restart of your application for certain configuration changes via the
configChanges
attribute on your activity definition in your AndroidManifest.xml
. The following setting avoids anactivity restart incase of orientation changes or position of the physical keyboard (hidden / visible).<activity android:name=".ProgressTestActivity"
android:label="@string/app_name"
android:configChanges="orientation|keyboardHidden|keyboard">
</activity>
It is also possible to define that an activity should only be used in a specific screen orientation via the
AndroidManifest.xml
file. Such an example configuration is listed below.<activity
android:name="com.vogella.android.multitouch.MainActivity"
android:label="@string/app_name"
android:screenOrientation="landscape" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Create a new project called com.vogella.android.lifecycle.activity.
Create the following class which is used to report lifecycle events via notifications.
package com.vogella.android.lifecycle.activity;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.os.Bundle;
public class TracerActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
notify("onCreate");
}
@Override
protected void onPause() {
super.onPause();
notify("onPause");
}
@Override
protected void onResume() {
super.onResume();
notify("onResume");
}
@Override
protected void onStop() {
super.onStop();
notify("onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
notify("onDestroy");
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
notify("onRestoreInstanceState");
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
notify("onSaveInstanceState");
}
private void notify(String methodName) {
String name = this.getClass().getName();
String[] strings = name.split("\\.");
Notification noti = new Notification.Builder(this)
.setContentTitle(methodName + " " + strings[strings.length - 1]).setAutoCancel(true)
.setSmallIcon(R.drawable.ic_launcher)
.setContentText(name).build();
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify((int) System.currentTimeMillis(), noti);
}
}
Create two activity which extend this one. The first activity should allow to start the second one via an
Intent
.Start your application and trigger the second activity. Review the notifications and ensure you know why this order of things are happening.
Press the Back button on the second activity. Validate that
onSaveInstanceState()
is not called. Explain why it is not called.Press the home button on the second activity. Validate that
onSaveInstanceState()
is called. Explain why it is called.Start the second activity. Switch the orientation of your emulator via the CTRL+F11 shortcut and see which lifecycle methods of the activity are called. Is the first activity also re-created or only the second one?
Activate the Don't keep activities setting in the Developer Options. Test again which methods are called.
Create a string array and add a spinner to your first activity using this array. The following lists the
strings.xml
and the layout file used by the first activity.<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Lifecycle</string>
<string name="action_settings">Settings</string>
<string name="hello_world">Hello world!</string>
<string-array name="operating_systems">
<item >Ubuntu</item>
<item >Android</item>
<item >iOS</item>
</string-array>
</resources>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<Spinner
android:id="@+id/spinner1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="58dp"
android:entries="@array/operating_systems" />
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClick"
android:layout_gravity="bottom"
android:text="Start new Activity" />
</LinearLayout>
Ensure that the selection of the spinner is saved and restored between configuration changes or restarts of the activity by the Android system.
No comments:
Post a Comment