我们知道Ubuntu平台提供了良好的融合(convergence)设计.通过融合设计,使得我们的同样一个应用在不需要修改任何代码的情况下,重新打包就可以运行到不同的屏幕尺寸的设备上.当然,Canonical公司最终的目的是实现snap应用运行到所有设备上,而不需要进行任何的重新打包的动作.目前Ubuntu手机上支持的应用打包格式是click包.在为了的Ubuntu SDK中,最终我们会把snap的支持加入到我们的SDK之中去.那么目前我们怎么把我们已经开发好的应用打包成为一个snap应用包,并可以成功部署到我们的电脑桌面(16.04)上呢?

如果大家对如何安装一个snap应用到16.04的桌面系统上的话,请参阅文章"安装snap应用到Ubuntu 16.4桌面系统".

1)通过Ubuntu SDK开发一个我们需要的手机应用

我们可以通过Ubuntu SDK来创建一个我们想要的项目.关于如何创建一个Ubuntu手机应用,这个不在我们的这个教程范围.如果你对如何利用Ubuntu SDK开发一个手机应用感兴趣的话,请参考我们的文章"Ubuntu 手机开发培训准备".这里将不再累述!
值得指出的是:在今天的教程中,我们将教大家如何把一个qmake的Ubuntu手机应用打包为一个snap的应用.这里,我们将利用之前我已经开发的一个项目作为例程来开始.我们在terminal下打入如下的命令:
$ git clone https://github.com/liu-xiao-guo/rssreader_snap

下载后的源码结构如下:

liuxg@liuxg:~/snappy/desktop/rssreader$ tree -L 2
.
├── snapcraft.yaml
└── src├── manifest.json.in├── po├── rssreader└── rssreader.pro

从上面的结构上,我们可以看到:在src目录下的整个项目是有我们的Ubuntu SDK所创建的一个qmake项目,它有一个项目文件.pro文件.在手机上的运行情况如下:

  
同时,在我们项目的根目录下,我们发现了另外一个文件snapcraft.yaml.这个文件就是为了能够让我们把我们的qmake项目最终打包为snap应用的snap项目文件.

2)为我们的qmake项目打包

在上节中,我们已经提到了我们项目的snapcraft.yaml文件.现在我们把这个文件展示如下:

snapcraft.yaml

name: rssreader-app
version: 1.0
summary: A snap app from Ubuntu phone app
description: This is an exmaple showing how to convert a Ubuntu phone app to a desktop snap app
confinement: strictapps:rssreader:command: desktop-launch $SNAP/lib/x86_64-linux-gnu/bin/rssreaderplugs: [network,network-bind,network-manager,home,unity7,opengl]parts:rssreader:source: src/plugin: qmakeqt-version: qt5build-packages:- cmake- gettext- intltool- ubuntu-touch-sounds- suru-icon-theme- qml-module-qttest- qml-module-qtsysteminfo- qml-module-qt-labs-settings- qtdeclarative5-u1db1.0- qtdeclarative5-qtmultimedia-plugin- qtdeclarative5-qtpositioning-plugin- qtdeclarative5-ubuntu-content1- qt5-default- qtbase5-dev- qtdeclarative5-dev- qtdeclarative5-dev-tools- qtdeclarative5-folderlistmodel-plugin- qtdeclarative5-ubuntu-ui-toolkit-plugin- xvfbstage-packages:- ubuntu-sdk-libs- qtubuntu-desktop- qml-module-qtsysteminfo- ubuntu-defaults-zh-cnstage:- -usr/share/pkgconfig/xkeyboard-config.pc snap:- -usr/share/doc- -usr/includeafter: [desktop/qt5]
初以乍看,这个文件和我们以往所看到的文件都不同.似乎很复杂!关于snapcraft.yaml的具体解释,我们可以参考我们的官方文档"snapcraft.yaml syntax".
在这里,我们做一个简单的解释:
  • name: 这是最终的包的名称.针对我们的情况,我们最终的snap包的名字为rssreader-app_1.0_amd64.snap
  • version: 这是我们包的版本信息.就像我们包的名称rssreader-app_1.0_amd64.snap所展示的那样.1.0是我们的版本信息
  • summary: 这是一个描述我们包信息的字符串.根据我们的设计,他只能最多长达79个字符
  • description:这是一个描述我们包的字符串.它可以比summary来得更长一些
  • confinement: 受限的种类:strict 或 devmode.当我们设置为devmode时,在安装时加上--devmode选项时,可以使得我们的应用不接受任何的安全的限制.就像我们以前在Ubuntu电脑上开发一样.我们可以随意地访问任何一个我们想要访问的目录等等
  • apps: 在这里定义我们的应用及其运行时所需要的命令.针对我们的情况,我们定义rssreader为我们的应用.当我们执行我们的应用时,我们需要使用<<包名>>.<<应用名>>来运行我们的应用.针对我们的情况,我们使用rssreader-app.rssreader来通过命令行来运行我们的应用
    • command:这是用来启动我们应用所需要的命令行.针对我们的情况:desktop-launch $SNAP/lib/x86_64-linux-gnu/bin/rssreader.这里的desktop-launch来自我们下面的已经预先编译好的包 desktop/qt5
    • plugs:这一项定义了我们的snap应用所访问的权限.通过我们的设定,我们可以访问系统的$HOME目录及使用opengl等.更多细节请擦参阅Interfaces
  • parts: 每个part定义了我们软件所需要的部分.每个part就像一个mini的小项目.在我们编译时可以在parts的目录中分别找到对应的部分
    • rssreader: 我们定义的part的名称.它的名字可以是任何你所喜欢的名称

      • source: 定义part的源码.它可以在网站上的任何一个软件(bzr, git, tar)
      • plugin: 定义编译part所需要用到的plugin.开发者也可以拓展snapcraft的plugin.请参阅文章"Write your own plugins"
      • qt-version:这个是针对Qt plugin来说的.定义Qt的版本
      • build-packages:定义在这里的每个包都是为了用来编译我们的项目的.需要安装的.它们将不会出现在最终的snap文件中
      • stage-packages:这些定义的包都要最终被打入到snap包中,并形成运行该应用所需要的文件.特别值得指出的是:我们加入了中文包ubuntu-defaults-zh-cn,从而使得我们的应用可以看见中文的显示.当然,我们包的大小也从100多兆增加到300多兆.注意这里的包都对应在我们通常ubuntu下的debian包
      • snap:定义了我们需要的或不需要的文件.在这里我们通过"-"来把一些不想要的文件剔除从而不打入到我们的包中
      • after:表明我们的这个part的编译必须是在desktop/qt5下载之后.对于有些项目,我们必须先得到一个part,并使用这个part来编译我们其它的part.在这种情况下,我们可以使用after来表明我们的先后顺序.在这里,我们也利用了其它人所开发的part desktop/qt5.我们可以在网址https://wiki.ubuntu.com/snapcraft/parts找到别人已经发布的parts.这些parts的描述也可以在地址https://wiki.ubuntu.com/Snappy/Parts找到.我们可以重复利用它们.在命令行中,我们也可以公共如下的命令来查找已经有的parts:
        • snapcraft update
        • snapcraft search

当然我们只做了一个简单的描述.更详细的关于snapcraft.yaml的描述,请参阅我们的文档"snapcraft.yaml syntax"或文档.

3)编译我们的snap应用

为了编译我们的snap应用,其实非常简单.我们直接进入到我们的项目的根目录下,并打入如下的命令:
$ snapcraft

这样,我们就可以编译我们的应用,并最终生产我们所需要的.snap文件:

312M 7月  13 12:25 rssreader-app_1.0_amd64.snap
就像我们上节中介绍的那样,由于我们加入了中文字体,所以我们的应用变得非常庞大.没有字体的snap包大约为141M.
如果我们想要清楚我们的打包过程中的中间文件,我们可以打入如下的命令:
$ snapcraft clean

它将清除在parts, stage及prime目录下的所有的文件.更多关于snapcraft的介绍,可以参阅我的文章"安装snap应用到Ubuntu 16.4桌面系统".我们也可以通过如下的方式来得到它的帮助:

$ snapcraft --help

4)安装及运行我们的应用

我们可以通过如下的命令来安装我们的.snap文件:
$ sudo snap install rssreader-app_1.0_amd64.snap --force-dangerous

我们可以通过如下的命令来运行我们的应用:

$ rssreader-app.rssreader

运行时的画面如下:

从上面可以看出来.我们没有经过任何的修改,但是我们的手机应用也可以在16.04的桌面上运行得非常好.在本应用中,它也使用了融合(Convergence)技术,从而使得我们的应用在不同的屏幕尺寸上自动适配.大家可以参阅我的文章"运用AdaptivePageLayout来做融合(convergence)设计以实现动态布局"

5)安全调试

我们可以在我们的桌面系统中安装如下的软件:
$ snap install snappy-debug

如果在安装的过程中提示还需要安装其它的应用软件,我们按照提示安装即可.

接下来我们在一个terminal中打入如下的命令:
$ snappy-debug.security scanlog

根据Interfaces文档介绍,log-observe是不能自动连接的,我们需要使用如下的命令来手动建立这种连接:

$ sudo snap connect snappy-debug:log-observe ubuntu-core:log-observe

这种方法也适用于我们建立其它需要手动链接的的情况.然后在另外一个terminal中打入如下的命令:
$ rssreader-app.rssreader

那么我们可以在这个窗口看到如下的信息:

显然在我们的应用运行时产生了一些安全的问题,并提示一些上面的输出信息.我们切换到另外一个运行命令"snappy-debug.security scanlog"的窗口:
显然在这个输出窗口也显示了一些"DENIED"安全错误信息.那么这些问题是怎么来的呢?显然,这可能是我们的应用没有设置相应的plug所致.我们可以参阅我们的官方文档interfaces,并结合我们的应用.我们可以初步判断,我们可能需要network及network-bind plug.这是因为我们的应用是一个网路的应用,需要从网上抓数据.另外,在我们代码的main.cpp中,我们使用了如下的代码:
QNetworkAccessManager *MyNetworkAccessManagerFactory::create(QObject *parent)
{QNetworkAccessManager *nam = new QNetworkAccessManager(parent);QString path = getCachePath();QNetworkDiskCache* cache = new QNetworkDiskCache(parent);cache->setCacheDirectory(path);nam->setCache(cache);return nam;
}

这段代码是为了设置一个cache,从而使得我们抓过来的照片能够得到cache,进而我们不需要浪费网路资源重复获取同样的一个照片.根据我们目前所有的plugs:

liuxg@liuxg:~$ snap interfaces
Slot                 Plug
:camera              -
:cups-control        -
:firewall-control    -
:gsettings           -
:home                rssreader-app
:locale-control      -
:log-observe         snappy-debug
:modem-manager       -
:mount-observe       -
:network             -
:network-bind        -
:network-control     -
:network-manager     -
:network-observe     -
:opengl              rssreader-app
:optical-drive       -
:ppp                 -
:pulseaudio          -
:snapd-control       -
:system-observe      -
:timeserver-control  -
:timezone-control    -
:unity7              rssreader-app
:x11                 -
显然,netwrok-manager是我们所需要的plug.我们可以在我们的snapcraft.yaml加入上面所述的两个plug.修改过后的snapcraft.yaml文件如下:

snapcraft.yaml

name: rssreader-app
version: 1.0
summary: A snap app from Ubuntu phone app
description: This is an exmaple showing how to convert a Ubuntu phone app to a desktop snap app
confinement: strictapps:rssreader:command: desktop-launch $SNAP/lib/x86_64-linux-gnu/bin/rssreaderplugs: [network,network-bind,network-manager,home,unity7,opengl]parts:rssreader:source: src/plugin: qmakeqt-version: qt5build-packages:- cmake- gettext- intltool- ubuntu-touch-sounds- suru-icon-theme- qml-module-qttest- qml-module-qtsysteminfo- qml-module-qt-labs-settings- qtdeclarative5-u1db1.0- qtdeclarative5-qtmultimedia-plugin- qtdeclarative5-qtpositioning-plugin- qtdeclarative5-ubuntu-content1- qt5-default- qtbase5-dev- qtdeclarative5-dev- qtdeclarative5-dev-tools- qtdeclarative5-folderlistmodel-plugin- qtdeclarative5-ubuntu-ui-toolkit-plugin- xvfbstage-packages:- ubuntu-sdk-libs- qtubuntu-desktop- qml-module-qtsysteminfo- ubuntu-defaults-zh-cnsnap:- -usr/share/doc- -usr/includeafter: [desktop/qt5]

重新打包我们的应用并安装.我们可以利用:
$ snap interfaces

来显示我们应用所有的plug:

liuxg@liuxg:~/snappy/desktop/rssreader$ snap interfaces
Slot                 Plug
:camera              -
:cups-control        -
:firewall-control    -
:gsettings           -
:home                rssreader-app
:locale-control      -
:log-observe         snappy-debug
:modem-manager       -
:mount-observe       -
:network             rssreader-app
:network-bind        rssreader-app
:network-control     -
:network-manager     -
:network-observe     -
:opengl              rssreader-app
:optical-drive       -
:ppp                 -
:pulseaudio          -
:snapd-control       -
:system-observe      -
:timeserver-control  -
:timezone-control    -
:unity7              rssreader-app
:x11                 -
-                    rssreader-app:network-manager

在上面的最后一行,我们可以看到:
-                    rssreader-app:network-manager

这是什么意思呢?我们来重新查看连接interfaces,在那个页面里虽然目前还没有network-manager的介绍,但是我们可以看到诸如:

network-controlCan configure networking. This is restricted because it gives wide, privileged access to networking and should only be used with trusted apps.Usage: reserved Auto-Connect: no

我们需要注意的是最下面的一句话:Auto-Connect: no.也就是说自动连接不存在.对于我们的networrk-manager的情况也是一样的.我们需要手动来连接.那么我们该如何来手动连接呢?

liuxg@liuxg:~$ snap --help
Usage:snap [OPTIONS] <command>The snap tool interacts with the snapd daemon to control the snappy software platform.Application Options:--version  print the version and exitHelp Options:-h, --help     Show this help messageAvailable commands:abort        Abort a pending changeack          Adds an assertion to the systemchange       List a change's taskschanges      List system changesconnect      Connects a plug to a slotcreate-user  Creates a local system userdisconnect   Disconnects a plug from a slotfind         Finds packages to installhelp         Helpinstall      Install a snap to the systeminterfaces   Lists interfaces in the systemknown        Shows known assertions of the provided typelist         List installed snapslogin        Authenticates on snapd and the storelogout       Log out of the storerefresh      Refresh a snap in the systemremove       Remove a snap from the systemrun          Run the given snap commandtry          Try an unpacked snap in the system

我们通过上面的命令,我们可以知道snap有一个叫做connect的命令.进一步:

liuxg@liuxg:~$ snap connect -h
Usage:snap [OPTIONS] connect <snap>:<plug> <snap>:<slot>The connect command connects a plug to a slot.
It may be called in the following ways:$ snap connect <snap>:<plug> <snap>:<slot>Connects the specific plug to the specific slot.$ snap connect <snap>:<plug> <snap>Connects the specific plug to the only slot in the provided snap that matches
the connected interface. If more than one potential slot exists, the command
fails.$ snap connect <plug> <snap>[:<slot>]Without a name for the snap offering the plug, the plug name is looked at in
the gadget snap, the kernel snap, and then the os snap, in that order. The
first of these snaps that has a matching plug name is used and the command
proceeds as above.Application Options:--version            print the version and exitHelp Options:-h, --help               Show this help message

显然我们需要使用如下的命令来完成我们的connect工作:

$ snap connect <plug> <snap>[:<slot>]

针对我们的情况,我们使用如下的命令:

$ sudo snap connect rssreader-app:network-manager ubuntu-core:network-manager

当我们打入上面的命令后,我们重新来看我们的snap interfaces:

liuxg@liuxg:~$ snap interfaces
Slot                 Plug
:camera              -
:cups-control        -
:firewall-control    -
:gsettings           -
:home                rssreader-app
:locale-control      -
:log-observe         snappy-debug
:modem-manager       -
:mount-observe       -
:network             rssreader-app
:network-bind        rssreader-app
:network-control     -
:network-manager     rssreader-app
:network-observe     -
:opengl              rssreader-app
:optical-drive       -
:ppp                 -
:pulseaudio          -
:snapd-control       -
:system-observe      -
:timeserver-control  -
:timezone-control    -
:unity7              rssreader-app
:x11                 -

显然这一次,我们已经成功地把network-manager加入到我们的应用中.我们可以重新运行我们的应用:
liuxg@liuxg:~/snappy/desktop/rssreader$ rssreader-app.rssreader (process:9770): Gtk-WARNING **: Locale not supported by C library.Using the fallback 'C' locale.
Gtk-Message: Failed to load module "overlay-scrollbar"
Gtk-Message: Failed to load module "gail"
Gtk-Message: Failed to load module "atk-bridge"
Gtk-Message: Failed to load module "unity-gtk-module"
Gtk-Message: Failed to load module "canberra-gtk-module"
qml: columns: 1
qml: columns: 2
qml: currentIndex: 0
qml: index: 0
qml: adding page...
qml: sourcePage must be added to the view to add new page.
qml: going to add the page
qml: it is added: 958
XmbTextListToTextProperty result code -2
XmbTextListToTextProperty result code -2

显然我们的应用再也没有相应的错误提示了.

最终的项目的所有代码在:https://github.com/liu-xiao-guo/rssreader_final/blob/master/snapcraft.yaml

6)为我们的应用加上应用图标

到目前我们的应用虽然接近完美,但是我们还是不能够在我们的dash中启动我们的应用.为此,我们在我们的应用的根目录下创建了一个setup/gui目录.在该目录下,我们创建如下的两个文件:
liuxg@liuxg:~/snappy/desktop/rssreader/setup/gui$ ls -l
total 100
-rw-rw-r-- 1 liuxg liuxg   325 7月  14 13:18 rssreader.desktop
-rw-rw-r-- 1 liuxg liuxg 91353 7月  14 11:50 rssreader.png

加入这两个文件后的项目结构为:

liuxg@liuxg:~/snappy/desktop/rssreader$ tree -L 3
.
├── setup
│   └── gui
│       ├── rssreader.desktop
│       └── rssreader.png
├── snapcraft.yaml
└── src├── manifest.json.in├── po│   └── rssreader.liu-xiao-guo.pot├── rssreader│   ├── components│   ├── main.cpp│   ├── Main.qml│   ├── rssreader.apparmor│   ├── rssreader.desktop│   ├── rssreader.png│   ├── rssreader.pro│   ├── rssreader.qrc│   └── tests└── rssreader.pro

有了上面的两个文件,我们再次重新打包并安装我们的snap应用.安装完后,在我们的dash里:

我们可以找到我们的RSS Reader应用了.
最终我们的代码在:https://github.com/liu-xiao-guo/rssreader_dash

7)在不需要安装的情况下运行我们的应用

我们知道在开发的过程中,我们每次安装的时候都会产生一个新的版本.这个新的安装不仅花费很多的时间,而且占用系统的空间(每个版本占用的空间比较大).在开发时,我们能否不用安装而直接使用已经在打包过程中生产的文件呢?答案是肯定的.我们可以通过:
liuxg@liuxg:~$ snap --help
Usage:snap [OPTIONS] <command>The snap tool interacts with the snapd daemon to control the snappy software platform.Application Options:--version  print the version and exitHelp Options:-h, --help     Show this help messageAvailable commands:abort        Abort a pending changeack          Adds an assertion to the systemchange       List a change's taskschanges      List system changesconnect      Connects a plug to a slotcreate-user  Creates a local system userdisconnect   Disconnects a plug from a slotfind         Finds packages to installhelp         Helpinstall      Install a snap to the systeminterfaces   Lists interfaces in the systemknown        Shows known assertions of the provided typelist         List installed snapslogin        Authenticates on snapd and the storelogout       Log out of the storerefresh      Refresh a snap in the systemremove       Remove a snap from the systemrun          Run the given snap commandtry          Try an unpacked snap in the system

我们在上面的命令中,发现一个叫做try的命令.在我们运行完snapcraft命令后,snapcraft会帮我们生产相应的.snap文件.同时它也帮我们生产如下的目录:

liuxg@liuxg:~/snappy/desktop/rssreader$ ls -d */
parts/  prime/  setup/  src/  stage/

这其中有一个叫做prime的目录,其实它的里面就是我们安装snap后的文件.我们可以使用如下的方法:

$ sudo snap try prime/

通过上面的命令我们就可以把我们的snap安装到系统中.重新展示我们的snap安装目录:

liuxg@liuxg:/var/lib/snapd/snaps$ ls -al
total 287200
drwxr-xr-x 2 root root      4096 7月  15 11:13 .
drwxr-xr-x 7 root root      4096 7月  15 11:13 ..
-rw------- 1 root root  98439168 7月  14 13:10 mpv_x1.snap
-rw------- 1 root root 127705088 7月  14 17:30 photos-app_x1.snap
lrwxrwxrwx 1 root root        42 7月  15 11:13 rssreader-app_x1.snap -> /home/liuxg/snappy/desktop/rssreader/prime
-rw------- 1 root root     16384 7月  13 15:53 snappy-debug_22.snap
-rw------- 1 root root  67899392 7月  13 12:26 ubuntu-core_122.snap

我们看见其实就是一个软链接.通过这样的方法,我们很快地部署了我们的snap应用.同时避免每次安装时各个不同版本之间带来的不同安装.snap包实际上是一个使用squashfs打包而生产的.通过snap try,我们不需要创建一个squashfs文件.他的另外一个好处是我们可以随时修改这个snap的应用的内容,这是因为它本身是read-write的.我们可以甚至加上--devmode选项来取消应用对安全的限制.

8)利用devmode免除在开发时的安全考虑

我们知道在开发的过程中,我们有时不知道需要使用哪写plug来保证我们的软件正常运行.这个时候,我们可以在我们的snapcraft中定义修改我们的confinement使之成为devmode:

snapcraft.yaml

name: rssreader-app
version: 1.0
summary: A snap app from Ubuntu phone app
description: This is an exmaple showing how to convert a Ubuntu phone app to a desktop snap app
confinement: devmodeapps:rssreader:command: desktop-launch $SNAP/lib/x86_64-linux-gnu/bin/rssreaderplugs: [network,network-bind,network-manager,home,unity7,opengl]parts:rssreader:source: src/plugin: qmakeqt-version: qt5build-packages:- cmake- gettext- intltool- ubuntu-touch-sounds- suru-icon-theme- qml-module-qttest- qml-module-qtsysteminfo- qml-module-qt-labs-settings- qtdeclarative5-u1db1.0- qtdeclarative5-qtmultimedia-plugin- qtdeclarative5-qtpositioning-plugin- qtdeclarative5-ubuntu-content1- qt5-default- qtbase5-dev- qtdeclarative5-dev- qtdeclarative5-dev-tools- qtdeclarative5-folderlistmodel-plugin- qtdeclarative5-ubuntu-ui-toolkit-plugin- xvfbstage-packages:- ubuntu-sdk-libs- qtubuntu-desktop- qml-module-qtsysteminfo- ubuntu-defaults-zh-cnsnap:- -usr/share/doc- -usr/includeafter: [desktop/qt5]

请注意上面的这一行:

confinement: devmode

当我们这样设计后,plugs里定义的所有的plug将不起任何的作用(我们甚至可以把它们全部删除).在我们安装我们的snap时,我们使用如下的命令:

$ sudo snap install rssreader-app_1.0_amd64.snap --devmode

注意我们上面加入--devmode选项.它表明,我们的应用将不受任何的安全沙箱的限制.它可以做它任何喜欢做的事情.在运行的过程中将不再生产任何的安全错误的提示.这样的设计对于我们开发者来说,我们可以很快地开发出我们所需要的应用,而不用先考虑我们的安全问题.

如何把一个qmake的Ubuntu手机应用打包为一个snap应用相关推荐

  1. 在Ubuntu手机平台上创建一个HTML 5的应用

    无论你是互联网世界的一个高手或是一个从来没有接触过互联网的新手,这篇文章将给你带来完整的在Ubuntu平台上开发HTML 5的应用.我们将慢慢地通过这个练习让你很自然地进入并熟悉整个的HTML 5应用 ...

  2. Ubuntu 手机开发培训准备

    在这篇文章中,我们将介绍学生如何做培训准备前的准备工作.提前准备并安装好自己的环境是做好一个培训非常重要的步骤.否则我们将浪费我们自己很多的宝贵的时间在课堂上!                   1 ...

  3. Ubuntu手机系统会成为第四大手机系统吗

    啊蛋评论:先不说哪令人"卧槽"的配置!就冲着这双系统..也要体验一下!(lz不是给ubuntu写软文的.完全是个人评论.)   Ubuntu开发商Canonical发布了代号为Ed ...

  4. anbox android 镜像,Anbox将使Ubuntu手机能运行Android应用程序

    Anbox 就在支持Ubuntu Phone设备的OTA-3软件更新发布两天之后,Ubuntu Touch维护者UBports现在开始为用户提供即将到来的Android应用程序支持. [UBports ...

  5. 怎么在Ubuntu手机上发送短信及拨打电话

    由于一些平台安全性的原因,Ubuntu手机目前暂时没有提供供第三方开发者发送短信及拨打电话的接口,但是在实际的应用中,我们也许会需要用到发送短信息或拨打电话.这个时候我们怎么办呢?我们在前面的文章&q ...

  6. oppo手机刷linux,Ubuntu手机系统介绍及Ubuntu刷机教程分享

    Ubuntu是知名的Linux操作系统,2013宣布要做手机系统之后吸引到了不少的注意力,虽然有一部分来自开发者,但相信更大的一部分是来自刷机爱好者.此前就有消息称Ubuntu版手机ROM将会在这几天 ...

  7. 手机linux系统介绍,Ubuntu手机系统介绍及Ubuntu刷机教程分享

    Ubuntu是知名的Linux操作系统,2013宣布要做手机系统之后吸引到了不少的注意力,虽然有一部分来自开发者,但相信更大的一部分是来自刷机爱好者.此前就有消息称Ubuntu版手机ROM将会在这几天 ...

  8. 三星手机安装linux系统下载,ubuntu手机系统安装教程【详细步骤】

    导语: 看到这个题目大家可能不太清楚吧,现在随着科技技术的进步和大范围的普及,普通大众也渐渐掌握了各种新技术.对于手机换系统,大家应该都听说过,网上经常能够看到某某大神把 苹果 手机的 ios 系统刷 ...

  9. ubuntu安装android应用程序,Anbox将使Ubuntu手机能运行Android应用程序

    就在支持Ubuntu Phone设备的OTA-3软件更新发布两天之后,Ubuntu Touch维护者UBports现在开始为用户提供即将到来的Android应用程序支持. 还记得Anbox(Andro ...

最新文章

  1. 启明云端分享|ESP32-C3 IO口控制灯的应用及串口通信示例
  2. Linux 给用户添加sudo权限
  3. Python、数据分析、机器学习、区块链大牛在偷偷看的9本书
  4. 1、Docker部署及基础理论
  5. 同步工具之Semaphore信号量
  6. 雨林木风与微软数年博弈:蚂蚁和大象共舞
  7. 【转】numpy.random.randn()与rand()的区别
  8. 基于.net平台的Windows窗体应用程序——(已附源码)学生管理系统
  9. 序 - 致“正当时”
  10. ARCGIS空间自相关技术的实现
  11. windows配置路由表办公网和外网自动切换
  12. 排坑·IPhoneIOS中不兼容正则中的断言匹配
  13. CUDA进阶资料专题(一)pinned memory 和 unified memory
  14. 这里告诉大家,“后端开发工程师”到底是做什么的。
  15. ”35岁没500万存款就是失败?“,35岁职场人真实存款流出!
  16. 第十二章:email-mailbox:管理email归档-imaplib:IMAP4客户库-连接服务器(认证失败)
  17. 教大家如何利用电脑发射wifi信号 供其他设备免费高速上网!
  18. 常州2021高考成绩查询,常州2021高考成绩排名榜单,常州各高中高考成绩喜报
  19. 华中科技大学计算机上机,华中科技大学_2010___考研计算机_复试上机
  20. 野子 - 苏运莹蓝鲸

热门文章

  1. 计算机查询网络连接,本地网络查询方法大全
  2. dt.Select()
  3. java模糊查询、自动补全的实现
  4. 程序化广告实战分享系列 - 程序化广告入门
  5. 移动互联网终端策略研究
  6. Kettle使用笔记
  7. thinkpadt410接口介绍_转:联想ThinkPad T410笔记本DisplayPort接口详解
  8. 2020年网络搭建与应用——国赛samba答案
  9. [转]关于计算机研究生报考方向的简要介绍
  10. SpringBoot JPA 批量插入实现,使用原生sql解决SaveAll插入慢的问题