由于一些原因,在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)
}
}
我们可以运行我们的应用:
当我们的应用的"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