安卓上完全原生应用?

5
有没有可能开发一款本地应用程序,而不依赖Dalvik运行时和任何Java库?
基本上,我想制作一个可以通过shell运行的本地二进制文件,它能够在没有system_server进程运行的情况下运行。理想情况下,我想通过OpenGL系统渲染东西来创建自己的窗口服务器,而不是依赖于SurfaceFlinger(由于system_server未运行,SurfaceFlinger也将无法使用)。
我提出这个问题的原因是我想在C/C ++中进行更低级别的Android开发,而Java是完全不必要的。 所以,基本上,我正在尝试开发一个独立的应用程序,它可以通过OpenGL + Cairo渲染东西并接收HID输入。
PS:我知道NDK是什么,但这不是我要找的。 我想创建独立的二进制文件,而不是创建在Dalvik VM内运行的东西。

我几乎可以确定标准的Linux hello-world程序可以运行,特别是如果你使用静态链接的话。我认为Android使用X,所以在经过一些与Xauth相关的操作后,你甚至可以做图形界面。但这肯定是一个非常痛苦的过程。 - Adrian Ratnapala
Android不使用x11。 - Kristina Brooks
我认为这是可能的,但当然只能在已经root过的手机上运行。这符合你的要求吗? - Tae-Sung Shin
@Taesung Shin 是的,它已经取得了root权限。并且这不是为了市场而进行的,只是一个小小的个人项目。 - Kristina Brooks
服务器程序(即:无 GUI,只有 CLI)可以毫不费力地运行。我的小型 Web 服务器可以在 Android、Raspberry Pi、Palm Pre(webOS)上运行,我指的是完全相同的 ELF(虽然它没有针对不同的 ARM 进行优化)。我想要的是一个本地应用程序,它启动一个裸浏览器画布,启动我的 Web 服务器,然后将 localhost URL 传递给浏览器,这样它就成为了一个本地后端、AJAX 前端的应用程序。就像我在 Linux 中所做的一样(使用 WebKit lib)。 - ern0
2个回答

1

在您的设备上运行本地代码有两种可能性:使用 NDK 或将应用程序嵌入框架中。据我所知,第一种方法不被考虑,因此我认为您可以看看第二种方法。这里是一个实现第二种方法的示例。

将现有代码移植到自定义 Android 设备的示例

又到了在博客上发布技术文章的时候了。这篇文章将介绍如何将现有的 C 库移植到 Android 上,这是我作为 Enea 开发板演示项目的一部分所做的。

平台添加或 NDK

将本地代码引入 Android 设备有两种方法:将其添加到平台本身并与框架集成,或将其包含在应用程序包中。后者已经得到了很大的发展,在 NDK 版本 5 的发布中甚至允许您直接从 NDK 中钩入应用程序生命周期http://developer.android.com/reference/android/app/NativeActivity.html。NDK 对于任何需要本地性能、需要重用可移植 C 库或只是需要包含某些遗留本地代码的应用程序都非常有用。NDK 与 Android SDK 集成良好,是在应用程序中包含本地功能的绝佳方式。对于需要在许多 Android 设备上重复使用的任何应用程序,它都应该是首选的方式。

另一种选择是将您的功能(可以是本地或 Java)作为 API 扩展包含在所有应用程序中以供使用。这仅适用于实现这些扩展的设备,并且可能是设备制造商的合适选择。这是我们在这里的目标。

分析现有项目

将本地代码移植到 Android 并不总是直截了当的,特别是如果我们谈论 C++ 代码,因为 Android 使用自己的 c 运行时,对异常等方面的支持有限。如果您想了解有关 bionic 的详细信息,请参阅 NDK 文档中的概述。

我想要为此项目移植的代码是 Enea LINX for Linux 框架,它是一个快速的 IPC 框架。我的目的是能够与运行我们 OSE 实时操作系统的控制系统进行交互,该操作系统也实现了这种 IPC。LINX 由几个内核驱动程序模块、用户空间库和一些配置和控制实用程序组成。它是用 C 写的。我之前已经在 Android 中使用 LINX 创建了一个小型演示,其中我单独编译了它并使用了静态链接,但是对于这个项目,我想要完全移植到 Android 构建系统中。它没有任何与 bionic 兼容性相关的问题,因此移植应该很简单。

我只想添加一个关于 LINX 的简短免责声明。我在这里使用它,因为它是将解决方案集成到 Android 中的内核驱动程序级别的良好示例。这个特定的代码片段确实向系统添加了其他 IPC 机制,这或多或少破坏了安全模型,因此除非您了解其影响,否则不要使用它。然而,本文中描述的将代码移植到 Android 的步骤适用于您可能想要包含在产品中的任何类型的驱动程序/框架/库。

添加内核驱动程序模块

第一步是将内核模块添加到 Android 构建中。一种方法是构建一个新内核并直接将它们包含在其中,但是对于这个项目,我选择将它们保持为单独的模块。

LOCAL_PATH := $(my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := linx.ko

LOCAL_MODULE_CLASS := SHARED_LIBRARY

# This will copy the file in /system/lib/modules
#
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/modules

LOCAL_SRC_FILES := $(LOCAL_MODULE)

include $(BUILD_PREBUILT)

Android系统镜像上模块的标准位置是System/lib/modules,因此我们将它们复制到那里。如果现在构建平台,构建系统将把我们预编译的模块linx.ko复制到我们用于设备的系统镜像中。下一步是确保我们在运行时安装了该模块。这可以通过shell手动完成,也可以通过在init期间运行的脚本完成。
在这种情况下,我创建了一个shell脚本,以以下内容从init.rc启动:
#linx init
insmod /lib/modules/linx.ko
insmod /lib/modules/linx_tcp_cm.ko
netcfg eth0 up
ifconfig eth0 192.168.1.12
mktcpcon --ipaddr=192.168.1.21 ControlConn
mklink --connection=tcpcm/ControlConn control_link

这包括安装模块和配置网络和LINX-link。我们通过在init.rc中添加以下内容来启动它:
...
#linx init script
service linx-setup /system/etc/linx_setup.sh
oneshot
...

安装脚本可以通过将其作为预构建目标包含在系统镜像中的方式来添加。
LOCAL_PATH := $(my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := linx_setup.sh

LOCAL_MODULE_CLASS := ETC

LOCAL_MODULE_PATH := $(TARGET_OUT)/etc

LOCAL_SRC_FILES := $(LOCAL_MODULE)

include $(BUILD_PREBUILT)

为用户空间代码创建Android make文件

现在我们已经安装好了驱动程序,下一步是考虑移植用户空间库。默认的LINX构建系统使用标准的GNU make文件,但我们需要创建适应Android构建系统的新文件。首先将所需的源文件添加到在Android源树中创建的linx目录中。这样就得到了以下结构:

Android.mk  
include  
liblinx  
linx_basic  
linxcfg  
linx_setup.sh  
modules

我有linx设置脚本和主要的Android.mk文件在顶层目录,然后我们有源文件在不同的文件夹中,包含文件在include文件夹中。为了说明每个源组件的Android make文件是如何创建的,我们可以以liblinx为例。Android.mk文件如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES := linx.c
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include
LOCAL_MODULE := liblinx
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)

我们通过指定LOCAL_SRC_FILES来设置源文件,通过指定LOCAL_MODULE来设置库的名称。我们还需要通过指定LOCAL_C_INCLUDES来提供include目录中的头文件。最后,由于我们正在移植共享库,因此使用BUILD_SHARED_LIBRARY模板。这将使用Android构建系统构建库,并将其作为名为liblinx.so的共享库添加到系统映像中。
代码的其余部分以相同的方式移动到Android构建系统中,通过创建Android.mk文件并指定类型和任何依赖项。例如,我们可以看一下构建mktcpcon配置程序的语法。这取决于我们刚刚创建的库,因此makefile的条目如下所示:
LOCAL_SRC_FILES := mktcpcon.c
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include
LOCAL_STATIC_LIBRARIES += liblinxcfg
LOCAL_SHARED_LIBRARIES += liblinx
LOCAL_MODULE := mktcpcon
include $(BUILD_EXECUTABLE)

在这里,我们使用BUILD_EXECUTABLE模板,并且还需要指定我们链接的静态和共享库。
总结
我希望这提供了一些关于如何设置现有Linux项目在Android系统上运行的构建的见解。遵循以下步骤:
- 使用正确的内核构建系统和配置为您的设备构建任何与内核相关的内容 - 将内核模块(和/或内核)添加到平台构建系统中,并使用预构建模板为它们创建Android.mk文件。 - 如果需要,请为您的驱动程序创建配置和初始化服务,并将它们添加到init。 - 将其余代码(用户空间)移动到Android源树中,并为它们创建Android.mk文件。 - 如果遇到构建错误,请在源代码中解决它们,并查看您的代码与Android C运行时的特定不兼容性。
这就是今天的帖子。通过这样做,我们现在能够在shell中运行的本地程序中使用我们添加的驱动程序和API。下一步是创建JNI层和Java库,以允许常规的Android应用程序利用我们的平台增强功能。
我休了半年的产假(美好的瑞典福利),但现在又全职从事Android黑客活动,并推动团队发布东西。希望您会在这里看到更多的活动,包括关于此帖子讨论应用程序API的后续内容。

我能否只生成一个独立的 ELF 可执行文件,链接本地框架? - Kristina Brooks
这超出了我的范围 - 我从未面对过这种方法。试着问问,也许有人可以帮助你。 - Yury

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接