Qt on Android Episode 1(翻译)

    在 KDAB ( the Qt experts ) 上看到了  BogDan Vatra 的 Qt on Android 的系列文章,生了翻译的念头,那就开始吧。

    我会跟随 BogDan Vatra  在 KDAB 上的的博客文章进行翻译,如需转载,请注明译者 foruok 。

    本文的英文链接原文:Qt on Android Episode 1 ,作者为 BogDan Vatra 。以下是译文。


    我打算针对 Qt on Android 这个主题撰写一个系列文章。

    第一篇文章的内容:它(注:指 Qt on Android)如何开始、怎样工作、当前的状态、应当对 Qt 5.2 期望什么以及我对 Qt 5.3 的计划。下一篇文章我会把重点放在如何搭建安卓开发环境上。

    我们开始吧。

    它(注:指 Qt on Android )到底是怎么开始的呢?

    2009年6月,我作为一个资深的 Linux 开发者加入了 ROUTE 66 。我的第一个任务是把现有的导航引擎移植到安卓平台上。那时候 Google  还没有释放任何的官方 NDK ,所以我必须自己从 Android 源码创建一个我自己的 NDK 。

    不久以后,我完成了一个可以在 Android 上工作的引擎。我开始喜欢 Android 了,但是我总觉得缺了点东西,而且是我非常关注在意的东西。——那就是 Qt,我钟爱的框架。这( Qt )就是缺失的那点东西!我对自己说,我必须为它做点什么。

    2009年的10月份, Nokia (嗯,那时候 Qt 归 Nokia 所有,什么日子啊……)发布了Lighthouse 项目。 Lighthouse 项目的创建,是为了让开发者们更方便的把 Qt 移植到任意的平台上。

    2009 年 12 月后期(我想是圣诞节之后),我有足够的空闲时间可以开始移植工作。我选择了  Lighthouse 项目,尽管它还是一个非常年轻的研究性项目。据我所知,我的 Android 移植,是第一个使用 Lighthouse 的移植。仅仅过了一个月( 2010 年 1 月),我在我的手机上看到了 Qt 绘制出来的第一个图形。那种感觉,实在是好极了。

    几个月后,当 Qt 达到一个相对良好的状态后,我开始了 Qt Creator 插件和 Ministro (注:Ministro 是一个全系统的 Qt 的共享库安装程序/服务提供者的服务)的工作。这个 Qt Creator 插件使得使用者可以非常方便的在安卓设备或模拟器上管理、开发、部署、运行、调试 Qt 应用程序。

    即使几乎一切都到位了,还是没多少人愿意使用,因为他们必须手动编译所有东西。因此我决定为此做点儿工作。

    2011 年,在 Nokia 宣布他们的重大战略转移的几个星期之后,我发布了第一个可用的 Qt Android SDK 。这就是 Necessitas 项目怎么开始的,之后它获得了巨大的成功,我决定加入 KDE ,在它的佑护下继续项目。为什么选择 KDE ?因为我们拥有同一个目标:保持 Qt 的强大和对所有人免费。而且我可以使用他们出色的系统来发布 Qt 库。

    最初释放的(Qt Android) SDK 只能在 Linux 下使用。很快地, Ray Donnelly 联系了我并且把 SDK 移植到了 Windows 和 Mac 上。如果你在这些平台上使用 Necessitas (和and Qt5 Android SDK  ),你应该感谢这个家伙。

    和 Ray 及其他人一起,我们完成了 Necessitas SDK 的很多很多次发布。

    因为(我们的) Qt Creator 插件非常成功,我在 2012 年提交了这个插件,那时候 Qt 仍然归 Nokia 所有!

    2012 年 11 月,为了 Qt 5 的整合,我向 Qt Project 贡献了这个安卓移植版本。这里得作个声明:只有 Qt 5 是在 Qt Project 的佑护下开发的,Qt 4 仍由 KDE Necessitas 项目所拥有。

    让我们大概地看看 Qt on Android 是如何工作的,我不会去太深入细节,但足够让你了解它是如何工作的。

    就像我之前写的,移植是基于  Lighthouse 项目的。  Lighthouse (现在更名为 QPA )是 Qt 的一个平台抽象层。基本上这一层介于 Qt 与平台之间,简化了移植工作。因为 Android 应用程序使用 Java 语言编写,不可能直接把 Qt 的事件循环和 Android 的事件循环直接连起来,我必须把 Qt 的主事件循环移动到一个线程中。如果你想扩展你的应用程序,必须明白这个事实:Qt 的事件循环和 Java UI 是运行在不同的线程中。即便在 Google 增加了 NativeActivity 之后,我们也不可能使用它,主要是因为它没有暴露 Qt 需要的所有特性。

    一个 Qt for Android 应用程序包含两个大的部分:

  • 原生部分,包含一个或多个动态库文件( .so ),实际上就是你的 Qt 应用程序。如果你选择捆绑 Qt 库,那么也包含所有需要的库文件。
  • Android 相关部分,这部分又包含下面几部分:
    • Android manifest ,这是你应用程序的入口。 Android 使用这个文件决定启动哪个应用或活动,它描述了应用需要的权限、 Android API 版本等等信息
    • 两个 Java 类,加载依赖项和你的应用。它们也是 Qt QPA 和 Android 世界之间的由 Java 实现的桥梁的一部分。
    • Ministro service .aidl 文件。这是两个用于和 Ministro service 通信的接口。 Ministro service 是部署方案之一,稍后会详细的讨论。
    • 其它的一些资源,如 assets, strings, images 等等。

    所有这些组成部分被组织在一个包中,这个包代表了你最终的应用程序。现在让我们看看这些部分如何一起工作。

    当你的应用程序启动时, Android 使用 manifest 文件启动一个活动( Activity )。这个活动是你应用程序中 Java 世界魔法的一小部分。为了在不中断现有应用的前提下更新 Qt 库, Java 部分被分割为两块:一部分携带你的应用程序(的一部分),另外一部分(一个 Java 库,一个 .jar 文件)包含了 QPA 插件的所有逻辑。

    携带你的应用程序的第一个 Java 部分负责寻找依赖(注:原文为 missing ,但我觉得翻译为依赖更好些)的库( Qt 和 Java )并加载他们。同时它也转发所有的事件(触摸、应用程序状态变更、屏幕变化等)给第二个 Java 部分。

    第二个 Java 部分负责与 Qt 通信。它包括 Android QPA 插件需要的逻辑,例如创建和管理绘图表面( drawing surface ),虚拟键盘处理等等。

    那么,第一个 Java 部分查找、加载所有依赖的库和你的应用并且转发所有事件给第二个部分,但你的 main 函数是怎样被调用的呢?好吧,QPA 插件做了这件事儿。不,我没搞错! QPA 插件确确实实在你的应用启动之前已经加载了(实际上在你的应用被加载前它已经加载了)。

    好吧,让我来解释为什么我有这样一个疯狂的设计。

    我的梦想是找到一个方法,以便开发人员可以只编译他们为 Android 开发的 Qt 应用,因此我必须找到一个途径来调用 main 方法(我不想强制你为你的应用创建其它名字的入口函数,比如 WinMain)。

    问题是,为了从 Java 中调用一个 native 方法,你必须先注册那个方法,否则你就不能调用它(注:这里指 jni 的调用方式),在这种情境下, QPA 应运而生。 QPA 插件被加载后,它注册了少数几个 native 函数,Java 部分使用这些函数作为桥梁,进一步地调用 native 世界中的更多函数。在 Java 部分加载完所有的库和你的应用之后,它只需要调用 startQtApplication 这个由 QPA 插件在之前注册过的 native 方法。这个方法搜索方法符号表找到 main 方法后,创建一个线程并在该线程中运行 main 方法。它必须创建一个线程,因为调用 main 方法会阻塞调用者直到 main 方法退出,而我们必须保持 Android  UI 线程自由(非阻塞)以便运行 Android 事件循环。

    在稍后的一篇文章中,我将介绍如何使用 JNI 在 Java 和 C/C++ 之间进行调用。

    最后,让我们看看 Qt on Android 的当前状态,你能从 Qt 5.2 得到什么,以及 Qt 5.3 for Android 的计划。

    Qt Essentials status:


    Qt Add-Ons status:


    感谢您花时间阅读。

    下次再见的时候我们讨论怎样搭建 Qt for Android 开发环境。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。