To find bugs faster contributing less effort you need to know what developers usually forget to handle. If you start checking things where the chance of error is higher - you will find a bug faster.
This article teaches you to find bugs related to an application Lifecycle. Lifecycle handling isn’t such a complicated thing itself, it’s just super easy to forget about it, and developers of course often do.
Android Framework provides components with entry points, where developers implement application UI and logic. Those components have a non-trivial Lifecycle (example), that is controlled by the framework. During the lifetime components are created and destroyed. Developers are in charge of state saving and restoring. It’s easy to spot Lifecycle related bug in 4 simple steps.
Lifecycle bug detection algorithm:
- Open a screen;
- Change a screen state, i.e. do anything that changes UI: input text, start loading, etc;
- Trigger Lifecycle event (will see them later);
- Verify that application is in the correct state;
We will consider different Lifecycle events, just trigger them in the third step of the bug detection algorithm.
OS has many different configurations that affect UI appearance: orientation (vertical/horizontal), Day/Night mode, system language, and others… When one of the configuration parameters changes, Android recreates components to adopt the app for a new setting.
Expected behavior is to get an app in the same state, but adopted to a new configuration. For instance, user filled up a few fields, switched to phone settings, then changed system language, and returned to an app. In that case, expected state is fields are filled, but all text in the app is changed to a selected language.
The easiest way to trigger configuration change is to rotate a device or turn on/off night mode (turn on battery saver to get night mode).
Android manages RAM without user interaction. When there is a low amount of available memory, OS tries to free up some space. It selects a process with the lowest priority among running processes and kills it. Visible applications have the highest priority. So when a user switches between apps, and there is memory starvation, the previous app is likely to be killed by OS. When a user switches back to the previous app its state is restored, so a user even doesn’t notice that violence took place here.
For testing purposes, you can set Background process limit to No background process in developer options.
To test: switch from testing app to any other, wait for a few seconds and switch back. If your app has a splash screen, you will see it during switching back.
Let’s practice by finding common bugs related to Lifecycle using the algorithm, that we’ve just considered. All these bugs were found in real Android projects and replicated by me in the test application. All considered bugs will have one common thing: feature works well until a Lifecycle event.
Not saved state
On the STATE tab, you can find the following feature: every time user clicks +1 button counter increases by 1.
- Open the STATE tab;
- Click three times to change counter to three;
- Rotate device to trigger configuration change;
- Check that counter is still there after rotation.
Actual result: After configuration change counter was reset to 0.
Of course, this is a simplified example, you will probably never find bug exactly like this. But sometimes developers forget to restore one of many text pieces on the screen.
In the next example, a user can choose a pill: red or blue. After clicking the “Choose a pill” button, a dialog appears with possible options. When a user selects a pill, a chosen option is displayed on the screen.
- Open the DIALOG tab;
- Click “Choose a pill” button;
- Rotate device to trigger configuration change;
- Check that dialog is still present;
- Select a pill;
- Verify that the selected option is on the screen;
Actual result: Dialog is still presented after the configuration change, but it does nothing. A user has chosen a red pill, but there is still blue on the screen.
User input loss
Consider the following feature: when a user enters the screen, the app loads some data from the server and lets a user edit it. Once a user presses the “Update” button, new information is updated on the server.
- Set background process limit to No background processes;
- Open the INPUT tab;
- Fill-up input fields;
- Switch to a different app to cause a process death;
- Switch back to the original app;
- Verify that your input is still present;
Actual result: App overrides inputted data by data from the server.
This scenario may seem complex and unlikely to happen, but on second thought it’s a common thing. Imaging: you’re filling-up a huge form, and after a minute of hard work somebody calls you. When you answer a call, phone switches apps. Now your app with a form in the background, so there is a chance that the system can kill it. The chance is much higher if it’s a video call.
Lifecycle related bugs are tricky, you won’t see them if you just work with the app. But users are different: they use app laying down on a sofa, turning from side to side, causing configuration change because of rotation; their phones run out of battery, causing configuration change because of night mode. The knowledge that you get in this article is a powerful weapon in your hand against Lifecycle related bugs. Don’t let them reach your users, good luck!