
这个故事是关于Android应用程序开发的非标准方法的。 安装Android Studio并用Java或Kotlin编写“ Hello,World”是一回事。 但是,我将展示如何以不同的方式执行同一任务。
我们提醒您: 对于所有“哈勃”读者来说,使用“哈勃”促销代码注册任何Skillbox课程时均可享受10,000卢布的折扣。
Skillbox建议:在线教育课程“专业Java开发人员” 。
我的智能手机如何与Android OS配合使用?
首先,有一点背景。 一个晚上,一个叫Ariella的朋友给我打电话。 她问我:“听着,我的智能手机如何工作? 里面有什么? 电能,普通单位和零如何使其全部起作用?”
我认识的不是开发人员,她在Arduino上创建了几个项目,其中包括软件和硬件。 也许这就是为什么她想了解更多。 我设法借助在大学开设的其中一门计算机科学课程中获得的知识来回答。
然后,我们一起工作了几个星期,因为Ariella希望了解电子技术的基础知识,即包括晶体管在内的半导体元素的工作原理。 接下来,我们进入更高的层次:我向她展示了如何使用晶体管的特定组合创建逻辑门,例如,NAND(逻辑AND)加NOR(逻辑OR)。
我们研究了不同类型的逻辑元素,将它们组合以执行计算(例如,添加两个二进制数)和存储单元(触发)。 当一切变得清晰时,他们开始开发一个简单的处理器(虚构),其中有两个通用寄存器和两个简单指令(添加这些寄存器)。 我们甚至编写了一个简单的程序,将这两个数字相乘。
顺便说一句,如果您对此主题感兴趣,请阅读有关从头开始创建8位计算机的说明。 它从最基本的方面解释了几乎所有内容。 我希望我能早点读它!
您好,Android!
完成研究的所有阶段之后,在我看来Ariella拥有足够的知识来了解智能手机处理器的工作原理。 她的智能手机是Galaxy S6 Edge,其基础是ARM架构(事实上,对于大多数智能手机而言)。 我们决定为Android编写一个“ Hello,World”应用程序,但使用汇编语言。
.text .globl _start _start: mov %r0, $1 // file descriptor number 1 (stdout) ldr %r1, =message mov %r2, $message_len mov %r7, $4 // syscall 4 (write) swi $0 mov %r0, $0 // exit status 0 (ok) mov %r7, $1 // syscall 1 (exit) swi $0 .data message: .ascii "Hello, World\n" message_len = . - message
如果您以前从未遇到过汇编代码,则此块可能会使您感到恐惧。 没关系,让我们一起解析代码。
因此,我们的程序包括两个部分。 第一个是带有机器代码指令的文本,第二个是变量,行和其他信息(从第15行开始)。 .text部分通常是只读的,.data也是可写的。
在第2行中,我们定义了一个名为_start的全局函数。 它是应用程序的入口点。 操作系统从这一点开始执行代码。 函数定义在第4行中声明。
此外,该功能还可以做两件事。 在第5-9行中,消息显示在屏幕上;在第11-13行中,程序结束。 即使您删除第11–13行,程序也会输出“ Hello,World”行并退出。 但是,输出将不正确,因为程序将以错误结尾。 没有第11–13行,应用程序将尝试执行无效的指令。
使用操作系统的系统功能“系统调用”显示打印。 在应用程序中,我们调用write()函数。 当我们将值4加载到名称为r7的处理器寄存器中时(第8行),我们将进行指示。 接下来,执行swi $ = 0指令(第9行),其中直接过渡到Linux内核,这是Android的基础。
至于系统调用的参数,它们通过其他寄存器传输。 例如,r0显示我们需要打印的文件描述符的编号。 我们将值1放在此处(第5行),指示标准输出(stdout),即输出到屏幕。
r1指示我们要写入的数据的内存地址,因此我们只需将字符串“ Hello,World”(第6行)的地址加载到该区域中,寄存器r2显示我们要写入的字节数。 在我们的程序中,将其设置为message_len(第7行),该行使用特殊语法在第18行上计算:点符号表示当前内存地址。 由于这个原因。 -消息表示当前内存地址减去消息地址。 好吧,由于我们在消息之后立即声明了message_len,因此所有这些都将作为消息的长度进行计算。
如果使用C编写第5–9行的代码,则会得到以下信息:
#define message "Hello, World\n" write(1, message, strlen(message));
关闭程序要容易一些。 为此,我们只需要在寄存器r0(第11行)中注册退出代码,然后在r7(第12行)中将值1(即exit()系统函数调用的编号)加到r7(第12行),然后再次调用内核(第13行)。
Android系统调用及其编号的完整列表可以
在操作系统的源
代码中找到。 还有一个
write()和
exit()的实现 ,可以调用相应的系统函数。
将程序放在一起
为了编译我们的项目,您将需要Android NDK(本机开发套件)。 它包含用于ARM平台的一组编译器和构建工具。 您可以从官方网站下载它,例如通过Android Studio安装它。

安装NDK之后,我们需要arm-linux-androideabi-as文件,这是ARM的汇编程序。 如果您是通过Android Studio下载的,请在Android SDK文件夹中查找。 通常它的位置是
ndk-bundle \工具链\ arm-linux-androideabi-4.9 \ prebuilt \ Windows-x86_64 \ bin。找到汇编器后,将写的内容保存在名为hello.s的文件中,然后运行以下命令将其转换为机器代码:
arm-linux-androideabi-as -o hello.o hello.s此操作将创建一个名为hello.o的ELF对象文件。 为了将其转换为可以在您的设备上运行的二进制文件,请调用链接器:
arm-linux-androideabi-ld -o你好hello.o现在,我们有了一个hello文件,其中包含一个随时可以使用的程序。
在您的设备上启动应用程序
Android应用程序通常以.apk格式分发。 这是一种特殊的压缩文件,其中包含Java类(是的,可以使用C / C ++编写,但是Java必须是入口点)。
为了避免启动应用程序时出现问题,本示例中使用了adb,这使我们可以将其复制到Android设备的临时文件夹中。 之后,我们启动adb shell以启动应用程序并评估结果:
亚行推送你好/数据/本地/ tmp /你好
亚行外壳chmod + x / data / local / tmp / hello最后,运行该应用程序:
亚行外壳程序/数据/本地/ tmp /你好你写什么
现在您拥有一个与Ariella相似的工作环境。 她花了几天的时间研究ARM汇编程序,然后提出了一个简单的项目-游戏Sven Boom(最初是来自以色列的Fizz Buzz的变体)。 玩家依次进行计数,每次数字除以7或包含数字7时,他们都必须说“ boom”(因此游戏名称)。
值得注意的是,游戏程序并不是一件容易的事。 Ariella编写了一种显示数字的完整方法,一次只能显示一位数字。 由于她无需调用标准C库函数就可以用汇编程序编写所有内容,因此花了几天的时间才能解决。
第一个适用于Adnroid的Ariella程序在此处提供。 顺便说一句,应用程序中的某些代码标识符实际上是希伯来语单词(例如,_sifra_ahrona)。
在汇编器中编写Android应用程序是更好地了解ARM架构以及更好地了解您日常使用的小工具的内部厨房的好方法。 我建议您仔细执行此操作,并尝试为设备创建一个小型汇编程序。 它可能是一个简单的游戏或其他东西。
Skillbox建议: