1. Managing the application life cycle
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.
2. Application
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.
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
|
3. Activity lifecycle
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.
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:
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. 4. Activity instance state
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
or Serialisable
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 exampleEditText
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 theonRestoreInstanceState()
and onCreate()
methods.5. Nonconfiguration instance scope
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()
andonRetainNonConfigurationInstance()
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.6. Controlling configuration
6.1. Avoiding configuration
change restarts
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 an activity 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>
6.2. Fixing the orientation for
an 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>
7. Exercise: Lifecycle
7.1. Prepare
project
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
.
7.2. Testing
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.
7.3. Instance
state
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