DEVELOPERS BLOG

Listen Up to Those Important Lifecycle Event Signals

Your BlackBerry 10 application goes through different states during its lifecycle.  And, of course, the application instance won’t stay silent with all those events happening. The Application class actually makes a buzz (or in more technical terms: emits a signal) every time your application undergoes an event and moves from one state to the other.

lifecycle hero

App state diagram in case of no run_when_backgrounded permission

The fullScreen() and awake() signals are sent when your application enters the “Foreground” state. The thumbnail() and asleep() signals are emitted when the application is moved into the “Background” state. The invisible() signal is sent when the application moves into the “Stop” state, i.e. the backlight is turned off or another app goes to the foreground. The aboutToQuit() signal is emitted when the app is exited. The manualExit() signal is emitted when the app is exited && the autoExit flag is set to false. Etc.

Ever wondered why those signals are made available to you?

Well, listening to and appropriately responding to those application lifecycle events will make your application more responsive and user-friendly.

In this blog post, I’m going to highlight the importance of two of those signals as an example: the thumbnail() and the manualExit() signals. I will also bring to light the lowMemory() signal available to you through the MemoryInfo class. Toward the end, I will share with you some C++ and QML code on how to make your app listen and respond to those signals.

thumbnail()

This signal is emitted by the Application class on a user swipe up from the bottom bezel, i.e. when the user minimizes the app. The application is sent to the background and it appears to the user as a thumbnail displaying a screenshot of the app right before it was minimized. At this point, your app will be moved into the background CPU partition, Cascades will stop all rendering and unless your app has the run_when_backgrounded permission, it will take away all resources…

Is that right? Will your application *REALLY* be denied *ALL* resources?

Actually not, the good news is that your app, in fact, is still given enough resources to customize the thumbnail (aka cover) of your app turning it into an active frame.

And here comes the importance of the thumbnail() signal.

Listen to this signal and just let your imagination run on what you want to implement in the connected slot to impress the user with a stunning cover using the SceneCover API and the cover property of the bb::cascades::Application class.

P.S. The update to your active frame will only take effect 30 seconds from the time the app was thumbnailed.

manualExit()

This signal is also emitted by the Application class and is emitted when the user exits the application and the application has explicitly set the autoExit flag to false. By Default, the autoExit flag is set to true and the OS handles the app exiting automatically, in which case the manualExit() signal is not fired.

But, here again, you are given the option to gain control and handle the exiting process of your application so you can, for example, save the application state for the user’s next login or save some data to the database.

All you need to do is set the autoExit flag to false, listen to the manualExit() signal and use the connected slot to perform any custom exiting processing like saving data, saving the app state, shutting down threads, etc.

P.S. You are given 3 seconds from the time you receive this signal and the app gets slayed by the OS. If you need additional time, call the extendTerminationTime() function, which will reset the timeout countdown timer to two seconds from the time it gets called.

lowMemory()

This signal is emitted by the MemoryInfo class when the memory resources on the device get below certain thresholds. If the device undergoes a severe low memory condition, the OS might slay some of the open apps under certain OS settings.

Here again, Cascades made this signal available to you to give your application a warning and the opportunity to set your app to appropriately handle a device low memory condition.

Now to make use of this signal, all you need to do is create a MemoryInfo class, listen to the lowMemory() signal and in the connected slot handle the low memory device condition in a way that assures a pleasing user experience with your app, even if it get unexpectedly slayed due to a low memory condition.

P.S. The lowMemory() signal contains a parameter called level. This parameter indicates the severity of the memory condition and can have one of the following values: LowPriority and HighPriority.

If you want to learn more about application lifecyle, checkout this link and this link or take this 25-minute Cascades training on the Navigator events and app covers. Below is some C++ and QML code that you can copy/paste and test in your application. Hope you make use of those important lifecycle event signals in your application and make them part of your future app design process.

Let me know in the comments section below, if you have any questions…
@SamarAbdelsayed

**************************** C++ **************************** Applicationui.hpp … #include <bb/cascades/SceneCover> #include <bb/cascades/Label> … class ApplicationUI : public QObject { … public slots:        void onLowMemory(bb::LowMemoryWarningLevel::Type);        void onThumbnail(); void onManualExit(); private:     bb::cascades::Label *m_pLabel;     bb::cascades::SceneCover *m_pSceneCover; … } Applicationui.cpp ApplicationUI::ApplicationUI(bb::cascades::Application *app) : QObject(app) {      QmlDocument *qml = QmlDocument::create("asset:///main.qml");        qml->setContextProperty("app", this); … connect(Application::instance(), SIGNAL(thumbnail()), this, SLOT(onThumbnail())); … bb::MemoryInfo *memoryInfo = new bb::MemoryInfo(this); connect(memoryInfo,SIGNAL(lowMemory(bb::LowMemoryWarningLevel::Type)),              this, SLOT( onLowMemory(bb::LowMemoryWarningLevel::Type) )); … //set autoExit to false in order to activate the manualExit signal        appInstance->setAutoExit(false);        connect(appInstance, SIGNAL(manualExit()), this, SLOT(onManualExit())); } void ApplicationUI::onLowMemory(bb::LowMemoryWarningLevel::Type) {        // free cached items that can be re-obtained later        // release memory for images that can be re-read from the file system        // delete objects no longer in use        // inactivate objects loaded dynamically using the active flag        // explicitly release objects by calling destroy()        // make objects that you must leave active invisible        // etc. qDebug() << "Low Memory!!!";        if (level == bb::LowMemoryWarningLevel::LowPriority) { console.log("low priority");        } else if (level == bb::LowMemoryWarningLevel::HighPriority) {               console.log("high priority");        } } void ApplicationUI::onThumbnail() {        // stop any threads such as networking // save data // etc. // update the cover, e.g. code below qDebug() << "on thumbnail";        m_pLabel = Label::create();        m_pSceneCover = SceneCover::create().content(m_pLabel);        Application::instance()->setCover(m_pSceneCover);        m_pLabel->setText(“This is the app cover”);  } void App::onManualExit() { // save data // save application state // etc.        qDebug() << "onManualExit"; bb::cascades::Application::instance()->extendTerminationTimeout();// for 2 sec        bb::cascades::Application::instance()->quit(); } **************************** QML**************************** import bb.cascades 1.0 import bb 1.0 // QML Plugin for libbb for the MemoryInfo class  Page { // Once the page is created, attach the application signals to JS functions. // The slots call the respective functions defined in C++. // Application object is made available to QML through setContextProperty.        onCreationCompleted: {        … Application.thumbnail.connect(onThumbnailed); … Application.setAutoExit(false); Application.manualExit.connect(onManualExit); …        } …        function onThumbnailed() {         app.onThumbnail();        } function onManualExit() {         app.onManualExit(); }        … Container {         id: top … … …             // MemoryInfo isn't a visible object so it must be wrapped as an             // attached object.             attachedObjects: [                 MemoryInfo {                     id: memoryInfo                     onLowMemory: {                         if (level == LowMemoryWarningLevel.LowPriority) {                             console.log("low priority signal");                         } else if (level == LowMemoryWarningLevel.HighPriority) {                             console.log("high priority signal");                         }                     }                 }             ] } }

About sabdelsayed