由于一些原因,在Ubuntu手机上,只支持一个前台运行的应用.每当应用被推到后台后,就会被自动挂起(suspended),该应用将不再被运行.这也是Ubuntu手机和安卓系统不一样的地方.更确切地说,Ubuntu手机应用的设计更类似于iPhone的应用.只允许一个前台运行的应用.当然如果把手机接到一个无线鼠标和键盘,那么我们的手机会自动进入到Desktop模式,也就自动具有多任务的功能.

对于很多开发者来说,没有了多任务,就好像没有了一些创新的应用,比如,我们不可能把导航的应用推到后台就不运行了.在实际的一些应用中,能够在后台运行,也是一个非常有用的功能.在今天的文章中,我们将介绍如何实现能够在后台运行的应用.在该应用中,我们使用了"unconfined"安全策略.必须注意的一点是,这个样的应用将不能进入到Ubuntu Store.如果有可能的话,可以上传到https://open.uappexplorer.com/apps商店供一些特别需要的用户使用.


1)使用unconfined安全策略



就像我们上面提到的,为了能够访问我们一般应用访问不到的文件地址,我们必须在我们的apparmor文件中定义一些特别的安全策略:



{
    "policy_groups": [
        "networking"
    ],
    "policy_version": 1.3,
    "template": "unconfined"
}



有了上面

unconfined定义,我们的应用就可以访问到一些我们不能访问到的资源.从某种意义上讲,已经打破了我们在Ubuntu手机上所设置的安全机制.

必须注意的是

这样的不明来源的应用可能对我们的手机造成伤害.




2)修改应用的lifecycleException标示




一旦一个应用的这个lifecycleException标志被修改,我们的应用在被推到后台时,将不被系统所挂起(suspended).那么我们怎么修该我们的应用的这个标示呢?我们可以仿照在launchpad.net上的 https://launchpad.net/tweakgeek项目.我们可以通过上面的方法来获取我们当前应用的信息,并做适当的修改.



Main.cpp


#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickView>

#include "applicationmodel.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    qmlRegisterType<ApplicationModel>("TweakGeek", 1, 0, "ApplicationModel");
    qmlRegisterUncreatableType<ApplicationItem>("TweakGeek", 1, 0, "ApplicationItem", "bla");

    QQuickView view;
    view.setSource(QUrl(QStringLiteral("qrc:///Main.qml")));
    view.setResizeMode(QQuickView::SizeRootObjectToView);
    view.show();
    return app.exec();
}



在上面我们把ApplicationModel及ApplicationItem数据类型进行登记,这样我们可以在我们的QML中运用它们.



Main.qml



import QtQuick 2.4
import Ubuntu.Components 1.3
import TweakGeek 1.0
import QtQuick.Layouts 1.1

MainView {
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "keeprunningapp.liu-xiao-guo"

    width: units.gu(60)
    height: units.gu(85)

    property int index: 0
    property var myapp

    ApplicationModel {
        id: applicationModel
    }

    Timer {
        interval: 500; running: true; repeat: true
        onTriggered: {
            index ++
            console.log("index: " + index)
        }
    }

    function findMyApp() {
        var count = applicationModel.count();
        for ( var i = 0 ; i < count; i++ ) {
            var app = applicationModel.get(i)
            // console.log("app name: " + app.name)
            if ( app.name === "keeprunningapp" ) {
                // if our own app is found, make it not die
                console.log("My app setLifecycleException: " + app.lifecycleException)
                myapp = app
            }
        }
    }

    Page {
        header: PageHeader {
            title: "keeprunningapp"
        }

        Connections {
            target: Qt.application
            onActiveChanged: {
                console.log("Qt.application.active: " + Qt.application.active);
            }
        }

        Column {
            anchors.centerIn: parent
            spacing: units.gu(2)

            Label {
                text: "current app is: " + Qt.application.active
                fontSize: "large"
            }

            Label {
                text: "index: " + index
                fontSize: "large"
            }

            RowLayout {
                spacing: units.gu(5)

                Label {
                    text: "Prevent suspending"
                    Layout.fillWidth: true
                    fontSize: "large"
                }

                Switch {
                    checked: myapp.lifecycleException
                    onClicked: myapp.lifecycleException = checked
                }
            }
        }
    }

    Component.onCompleted: {
        findMyApp();
    }
}



我们的实现也非常地简单.我们首先通过findMyApp来寻找到我们自己应用的实例,并在Switch中:


Switch {
                    checked: myapp.lifecycleException
                    onClicked: myapp.lifecycleException = checked
                }

进行绑定.



在我们的应用,为了显示我们的应用是否正在运行,我们特别设计了一个定时器,每隔500毫秒,输出一行字.



Timer {
        interval: 500; running: true; repeat: true
        onTriggered: {
            index ++
            console.log("index: " + index)
        }
    }



我们可以运行我们的应用:





android 应用保持后台运行 安卓保持后应用台运行_#include




当我们的应用的"Prevent suspending"开关设为"关"时,每当应用被推到后台,上面的定时器的输出就会停止,表明应用被suspended.而当我们的开关设为"开"时,每当应用被推到后台时,在我们的应用窗口还是可以看到连绵不断地输出:



qml: index: 418
qml: index: 419
qml: index: 420
qml: index: 421
qml: index: 422
qml: index: 423
qml: index: 424
qml: index: 425
qml: index: 426
qml: index: 427


整个项目的源码在: https://github.com/liu-xiao-guo/keeprunningapp