Testing Android's Activity Lifecycle
Apps should be resilient to the Activity
lifecycle. Developers need to know how to reproduce different lifecycle scenarios. Logging can be used to get a good understanding of that lifecycle. Lifecycle scenarios are then easier to reproduce for App testing. This article discusses Android Activity lifecycle and how to view the lifecycle in action. Being familiar with the Android lifecycle will help in building better behaving Apps.
(This Android tutorial assumes that Android Studio is installed; a basic App can be created and run; and code can be correctly copied into Studio. Here, for the tutorial App, the Application name is set to MyAndroid and an Empty Activity is used. Adapt the tutorial code as required.)
An Introduction to Activity Lifecycle
Android is designed for life on the go, where a user is engaged in multiple tasks, taking calls, checking email, sending SMS, social networking, taking pictures, accessing the Internet, running Apps and more, maybe including some work! As such a device can have multiple Apps and hence many Activities loaded in memory. The foreground App and its current Activity can be interrupted and paused at any moment. Apps, and hence Activities, that are paused can be removed from memory to free up space for newly started Apps. An App has a lifecycle which it cannot control as it is the Android operating system that starts, monitors, pauses, resumes and destroys the Apps Activities. Yet an Activity does know what is going on, as Activities are instantiated, hidden and destroyed various functions are called. This allows the Activity to keep track on what the operating system is doing to the App. App developers become familar with the functions invoked when an Activity starts:
- onCreate(Bundle savedInstanceState){...};
- onStart(){...};
- onResume(){...};
And the functions called when an Activity finishes and is removed from memory (destroyed).
- onPause(){...};
- onStop(){...};
- onDestroy(){...};
Viewing Activity Lifecycle
It is easy to view Activity lifecycle functions in action. In the MainActivity.java class override the functions listed above, calling through to the super class versions. The super class will be AppCompatActivity
, if using a new Studio project, or the Activity class (from which AppCompatActivity is derived). Add a call to Log.d() and pass in the name of the App and called function. The code will look similar to this (the Import statement for Log will be required, press Alt-Enter when prompted by Studio):
package com.example.myandroid;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("MyAndroid", "onCreate");
}
@Override
protected void onStart(){
super.onStart();
Log.d("MyAndroid", "onStart");
}
@Override
protected void onResume(){
super.onResume();
Log.d("MyAndroid", "onResume");
}
@Override
protected void onPause(){
super.onPause();
Log.d("MyAndroid", "onPause");
}
@Override
protected void onStop(){
super.onStop();
Log.d("MyAndroid", "onStop");
}
@Override
protected void onDestroy(){
super.onDestroy();
Log.d("MyAndroid", "onDestroy");
}
}
There are other ways to print the program name and function name in Java but hard coded strings are used here for convenience and simplicity. Run the program on a device (virtual or physical) to see the debug messages in logcat. If Logcat is not visible open the Android Monitor (at the bottom of Studio or use the View then Tool Windows menu, or press Alt-6). Use the back key to close the App to get the three teardown messages.
To see only the messages from the App add a Logcat filter. Use the end drop down above the Logcat display area and select Edit Filter Configuration. In Create New LogCat Filter give the filter a name, here MyAndroid is used. The Log Tag is used for filtering (the first parameter of the Log.d() call, again set to "MyAndroid". LogCat will now show only the messages explicitly sent from the App.
There are other ways to filter Logcat messages. For example filtering can be done on different Log functions, e.g. use Log.i() and filter for Info messages using the log level drop down. The Logcat output can be further simplfied by changing the header configuration. Use the gear icon to the left of the Logcat area:
The LogCat output can be cleared by clicking the trash can icon to the left of the Logcat area. It is useful to have a clean sheet before performing an action to watch for more messages. Now test the functions called when an App is paused. First add the function for onRestart() and the debug message.
@Override
public void onRestart() {
super.onRestart();
Log.d("MyAndroid","onRestart");
}
Run the program, click the Home button, then select the App again from tiled App button.
LogCat shows the usual start up function sequence, then when the Home button is pressed onPause() and onStop() run, but no onDestroy(). The program is not ending but effectively sleeping. When the program is activated again it is not reloaded therefore no onCreate() executes, instead onRestart() is called.
Run the program again, on the device or emulator, then kill the App (swipe it from the screen in tiled view or go into Apps via Settings and select the program and press the Force Close button). Then start the program again from the device (or emulator).
The usual start up functions are invoked then the Activity "sleeps". No onDestroy() is seen as the second instance is run. The above has shown different lifecycle scenarios.
- Normal start up and then finish.
- Start up, pause and then restart.
- Start up, pause, forced removal from memory and then start up again.
These scenarios result in different sequences of lifecycle functions being executed. Using these common scenarios when testing ensures an App performs correctly for a user. The techniques shown here can be extended when implementing additional overridden functions that need to be tested. The techniques also apply to using Fragments in an Activity and testing their lifecycle.
Download the code for this article ready for importing into Android Studio. The code can also be accessed via the Android Example Projects page. A version of this article was produced for the O'Reilly Android Cookbook.
Basic Activity Lifecycle Summary
- Activity Starts: onCreate() – onStart() – onResume(), Activity is running.
- Back button is Pressed: onPause() – onStop() – onDestroy(), Activity finishes (Activity calling finish() has same effect).
- Home button is Pressed: onPause() – onStop(), Activity paused (also occurs when Activity is interrupted, for example incoming call).
- Activity Restored from Pause State: onRestart() – onStart() – onResume(), Activity is running.
- Activity Starts, Pauses, Forced from Memory and then Restarted: onCreate() – onStart() – onResume() – onPause() – onStop() – onCreate() – onStart() – onResume(), Note no onDestroy() called!
Archived Comments
Jesus Vega in January 2018 said: Amazing tutorial.
Author:Daniel S. Fowler Published: Updated: