Transitions in the app life cycle

An app can transition between states as a result of a user's action or because of the state of the device. These transitions make up your app's life cycle. When an app makes a transition from one state of the life cycle to another, the BlackBerry 10 OS notifies the app using events. The events that an app receives can vary depending on the way the user configures the settings on the device.

The following diagram shows how an app can move from state to state:

A diagram that illustrates the app life cycle for BlackBerry 10 apps.

If you are developing a Cascades app, you can manage the life cycle transitions in your app using the Application class. This class contains signals that are emitted when the app state changes. The awake() signal is emitted when the app becomes active and transitions to the foreground. The asleep() signal is emitted when the app becomes inactive. You can connect to these signals to be notified when your app transitions from one state to another. For more information about using signals and slots, see Signals and slots.

If you are developing your app in C, your app receives a navigator event when it undergoes a transition. The window state and the app state might also change. The two simplest events are NAVIGATOR_WINDOW_ACTIVE and NAVIGATOR_WINDOW_INACTIVE, which determine the activity of the app. The NAVIGATOR_WINDOW_ACTIVE event is received when launching the app or bringing the app back to the foreground. In this state, the app should be active, interacting with the user. Your app shouldn’t be doing any work when the NAVIGATOR_WINDOW_INACTIVE event is received unless it has permission to run when it is backgrounded.

To create a great app, you should understand the states that an app goes through, from when the user starts your app to when the user closes it.

Run partitions

There are three run partitions for an app: foreground, background, and stopped. Each of these partitions represents a queue that an app can be placed into. When the Adaptive Partition Scheduler moves the app to a different partition, the NAVIGATOR_APP_STATE navigator event is sent to notify the app about the change.

NAVIGATOR_APP_FOREGROUND
The app is in the full screen state and is considered to be the main app on the device.
NAVIGATOR_APP_BACKGROUND
The app transitions from the full screen state to the thumbnail state. If the app has the run_when_backgrounded permission, it may still be running.
NAVIGATOR_APP_STOPPING
The app is not running and is not visible. The stopped partition is entered after the NAVIGATOR_APP_STOPPING event is received.

You should manage your app as it moves among these partitions.

Launching your app

Launching is the transition of your app to a fullscreen state. The following navigator events are sent when an app is started until it reaches the fullscreen state:

NAVIGATOR_WINDOW_ACTIVE
The app receives the NAVIGATOR_WINDOW_ACTIVE navigator event when it starts.
NAVIGATOR_WINDOW_FULLSCREEN
The app enters the NAVIGATOR_WINDOW_FULLSCREEN window state when it starts.
NAVIGATOR_WINDOW_COVER
The NAVIGATOR_WINDOW_COVER navigator event provides information about the window cover when the app starts.
NAVIGATOR_DEVICE_LOCK_STATE
The app receives the NAVIGATOR_DEVICE_LOCK_STATE navigator event when the device has been unlocked and the app returns to the fullscreen state.

You can detect app launch events by using the BlackBerry Platform Services (BPS). The following code samples illustrate how an app can initialize and handle the NAVIGATOR_WINDOW_ACTIVE event, which indicates that the app window has changed from hidden to fullscreen.

Not applicable

Not applicable

static void handleNavigatorEvent(bps_event_t *event) {

     bps_event_t *activation_event = NULL;

     switch (bps_event_get_code(event)) {
     case NAVIGATOR_WINDOW_INACTIVE:

          // Perform app deactivation steps here
          break;

     case NAVIGATOR_WINDOW_ACTIVE:

          // Perform app activation steps here
          break;
     }
}
int rc;

// Initialize the BPS library
bps_initialize();

//Request and process available navigator events
navigator_request_events(0)

for(;;) {
     bps_event_t *event = NULL;
     rc = bps_get_event(&event, 0);

     assert(rc == BPS_SUCCESS);

     if (event) {
          int domain = bps_event_get_domain(event);

          if (domain == navigator_get_domain()) {
               handleNavigatorEvent(event);
          }
     } else {
          break;
     }
}

Running in the background

Most apps do all their work while they're running in the foreground, in response to some user input. However, sometimes it's necessary for your app to also be able perform actions in the background, when it's running as an Active Frame. This type of app might listen for a particular event to occur on the device and then respond by notifying the user. All apps can notify the user if the Active Frame is visible to the user.

If the Active Frame is not visible, the app enters the invisible state. By default, processor time isn't allocated to apps that are in the invisible state. This approach helps to minimize battery power consumption and maximize the performance of the device. To allow an app to continue running while it's not visible, you must specify the  run_when_backgrounded permission in your project's bar-descriptor.xml file.

<permission>run_when_backgrounded</permission>

Headless apps

Headless apps are another type of app that is able to run in the background. A headless app is made up of two parts: a UI app and a headless process that runs in the background. Typically, the headless process works in cooperation with the UI, performing some task in the background and notifying the user when an event occurs.

Headless apps have a different life cycle than standard apps. For more information about headless apps and their app states, see Headless apps.

Saving your app state

The BlackBerry 10 OS can deactivate your app and move it to the background at any time. For example, a user may leave your app to open another app. When the OS deactivates your app, your code should first save the app state. In addition, your app should stop any unnecessary threads and processes (such as updating the UI in real time) to preserve system resources. When the OS activates your app again, you can reload the saved state, and restart any suspended processes.

An app can also experience interruptions during its life cycle such as losing focus, low memory events, and low battery events. Your app should listen for these events and respond by saving information about the state of the app. For example, the screen element currently in focus, or any user-entered data, should be saved. The saved state can be reloaded so that when the user returns to the app, the user can then continue on as before.

The type of information that you should save about the app state depends on your app. For example, in an eReader app, the user expects to return to the same page of the same book that they were reading. In a text editor app or on a settings page, the user expects that any text entered is still visible, even if those changes were not saved manually. In an interactive game, such as a driving game, you might save much more data about the state of the app. You should also save the app state after other significant events. For example, in a game app, you might save the state each time the user completes a level. In each case, you must determine what data is necessary to save so that the user can continue when the saved state is reloaded.

You should not try to save the app state after receiving an exit event, because apps don't have enough time to perform any operations before being terminated.

The following code sample shows how an eReader app saves and reads the current page number from a file:

Not applicable

Not applicable

#include <stdio.h>

/* Save page number to a file */
void save_to_file(int page) {
     /* Open file as binary */	
     FILE *fp = fopen("data/save.dat", "wb");
     if (!fp) {
          return;
     }
     fprintf(fp, "%i", page);
     fclose(fp);
}

/* Retrieve page number from file */
int read_from_file() {
     int page = 0;
     /* Open file as binary */
     FILE *fp = fopen("data/save.dat", "rb");
     if (!fp) {
        return 0;
     }

     int rc = fscanf(fp, "%i", &page);
     if (rc != 1) {
        return 0;
     }
     fclose(fp);
     return page;
}

Minimizing, maximizing, and peeking

Minimization is the transition from a fullscreen state to a paused background state. It is the window transition from a fullscreen state to a thumbnail state.The following navigator events are sent when an app is minimized:

Maximization is the transition from a paused background state to a foreground state. It is the window transition from a thumbnail state to a fullscreen state. The following navigator events are sent when an app is maximized:

Peeking is when an app is moved aside, causing the app to transition from a fullscreen state to the invisible state directly. The peeking feature is the ability to see behind a card using a gesture to drag the card off the screen to expose the card's parent or root.

The following navigator events are sent when an app moves aside because of peeking:

Handling standby mode

You should also consider how you should handle the screen display of your app during state transitions. By default, an app is affected by the user setting that causes a device to go into standby mode, which turns off the backlight and deactivates the app. However, you can use the SCREEN_IDLE_MODE_KEEP_AWAKE flag with the screen_set_window_property_iv() function to prevent your app from going into standby mode automatically. The following code sample shows you how to prevent an app from going into standby mode.

This code should be used only when it is necessary for your app, such as when playing a video. Even when this code is implemented, the app enters standby mode whenever the user puts the device to sleep.

Not applicable

WId winId = this->mainWindow->winId();
if( winId != NULL )
{
  int idleMode = SCREEN_IDLE_MODE_KEEP_AWAKE;

  screen_set_window_property_iv(screen_window_t(winId), 
                                SCREEN_PROPERTY_IDLE_MODE, 
                                &idleMode);
}
   int idle_mode = SCREEN_IDLE_MODE_KEEP_AWAKE;
   
   screen_set_window_property_iv(screen_win, 
                                 SCREEN_PROPERTY_IDLE_MODE, 
                                 &idle_mode);
   
  

Handling device lock and unlock

The BlackBerry 10 device can be locked at any point during the app's life cycle. You should prepare for the event that the device becomes locked while your app is running.

Lock or unlock from the invisible state
When the app is in the invisible state, the BlackBerry 10 OS sends the event NAVIGATOR_DEVICE_LOCK_STATE to notify the app that the device has become locked or unlocked. The app is already in a stopped state, so there are not a lot of actions that your app needs to perform to manage this transition. Also, no window states are changed because the app is already invisible on the screen and there is no change from the app's perspective.
Lock or unlock from the thumbnail state

If the device is locked while the app is in a thumbnail state, the app transitions from the thumbnail state to the invisible state and the following events are sent:

When the device is unlocked, having been locked from a thumbnail state, the app transitions from the invisible state back to the thumbnail state and receives the following events:

Unlock/Lock from the fullscreen state

When the device is locked with the app in the fullscreen state, the app transitions from the fullscreen state to the invisible state and the following navigator events are sent:

When the device is unlocked, having been locked from a fullscreen state, the app transitions from the invisible state back to the fullscreen state and receives the following events:

Handling critical conditions

Sometimes your app can enter an abnormal state and behave in an unexpected way. There are circumstances when the BlackBerry 10 OS acts on the app without any user interaction. For example, when the device runs out of battery and shuts down, it terminates all apps on the device immediately, regardless of the run partition. You should manage these system actions so that your app is ready for any transition. You should also make sure that your app saves its state and preserves its data during system events. For more information, see Saving your app state.

When you receive a low-memory event, your app should immediately save its state, release resources, and destroy its objects. If these steps are done correctly, the user's app data is preserved when your app is terminated. When your app receives focus again, the user expects to return to the app in the state it was in when it was pushed to the background. Your app should detect whether it was terminated due to low memory and then load the saved state.

The following code sample shows how to detect a low-memory event using the BlackBerry Platform Services:

Not applicable

Not applicable

/* Initialize the BPS library */
bps_initialize();

/* Request and process available navigator events */
navigator_request_events(0)

for(;;) {
     bps_event_t *event = NULL;
     rc = bps_get_event(&event, 0);

     if (event) {
          int domain = bps_event_get_domain(event);
          if (domain == navigator_get_domain()) {
               switch (bps_event_get_code(event)) {
                    case NAVIGATOR_LOW_MEMORY:
                    /* Perform low memory handling steps here */
                    break;
               }
          }
     } else {
          break;
     }
}
        

If the battery of the device drains completely, the device closes all apps and shuts itself down until the battery recharges. Your app can receive notification about changes in the battery level and state. You can evaluate the remaining battery and respond appropriately by saving the app state and warning the user, before the system shuts down.

For more information on responding to system events, see BlackBerry Platform Services.

Closing your app

When a user closes your app, you might want to perform some final cleanup operations before the app closes. For example, you can save the user's data, shut down your threads in a particular way, or close database or network connections manually.

If you are developing a Cascades app, you can use the Application class to handle app termination. The Application class includes several functions and signals that you can use to detect the termination of your app and delay it long enough to perform any final tasks.

setAutoExit(bool autoExit)
The setAutoExit(bool autoExit) function sets the value of the autoExit property, which indicates whether your app should close automatically or should receive a signal when a user closes your app. If you set this property to false using this function, your app receives the manualExit() signal (described below) when a user closes your app. You should remember that if you set the autoExit property to false, you must call quit() to terminate the app when you finish any final clean-up operations.
manualExit()
The manualExit() signal is emitted if the autoExit property is set to false by calling setAutoExit(). Your app can respond to this signal and perform any clean-up operations that are required before closing. After this signal is emitted, your app has approximately three seconds to perform any final tasks and terminate itself before the operating system terminates it forcibly. You can request more time to complete your tasks by calling extendTerminationTimeout().
extendTerminationTimeout()
The extendTerminationTimeout() function requests additional time to perform clean-up operations before your app is terminated forcibly. When you call this function, your app has about two seconds to finish its tasks and terminate itself. Calling this function doesn't add two seconds to the existing timeout period. Instead, it resets the timeout period to two seconds from the time this function is called. You can call this function as many times as you need to extend the timeout period.

If you are developing a C app, the following navigator events are sent when an app transitions from a thumbnail state to a terminated state:

NAVIGATOR_APP_BACKGROUND
When the app closes, the app is placed into the NAVIGATOR_APP_BACKGROUND run partition.
NAVIGATOR_WINDOW_INVISIBLE
The navigator window state is set to NAVIGATOR_WINDOW_INVISIBLE.
NAVIGATOR_EXIT
The app receives the NAVIGATOR_EXIT event and has approximately three seconds to perform any final tasks and terminate itself before the operating system terminates it forcibly.

Last modified: 2015-05-07



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus