前言
因业务需要,过去一年从熟悉的Android开发开始涉及嵌入式Linux开发,编程语言也从Java/Kotlin变成难上手的C++,这里面其实有很多差异点,特此整理本文来详细对比这两者开发的异同,便于对嵌入式Linux开发感兴趣的同学一些参考。
适用人群
- 有一定Android开发经验
- 想了解嵌入Linux开发的同学
思维导图
架构对比
注:左边是Android的平台架构,右边是目前我们Linux的平台架构。
由下往上看:
- 硬件层:硬件层是操作系统与硬件设备之间的桥梁,它使得操作系统和应用程序能够与各种硬件设备进行通信,从而实现设备的控制和管理。设备类型Android对应的比如智能手机、平板、物联网设备等,Linux对应的比如嵌入式设备、物联网设备等。
- Linux内核:Linux内核是Linux操作系统的核心组件,它负责管理系统的硬件资源、提供程序运行所需的环境以及协调程序之间的相互作用。比如Linux会负责进程管理、内存管理、文件系统、设备驱动、网络协议栈、系统调用和安全和权限管理等。
- 系统层:这一层包含了一系列用于实现基本的系统功能和服务的库。比如通过libc或glibc来访问操作系统提供的服务。
- 应用框架层:这一层就是我们常说的Framework,在Android中提供的是用于开发Android应用程序的API和组件,比如Activity、Service、Broadcast Receiver等。在Linux中也有相应的组件和API,一般情况下是通过DBus这种跨进程通信来调用服务,比如日志服务,网络服务等。
- 应用层:这一层就是最上层我们能看见的应用层,我们在手机能看到的Android App和在嵌入设备看到的Linux应用程序。我们通常使用Java来开发Android应用程序,使用C/C++来开发Linux应用程序。
基础差异对比
项目 | Android开发 | 嵌入式Linux开发 |
---|---|---|
基础平台 | 基于Linux内核 | 基于Linux内核 |
开发语言 | Java/Kotlin(应用层),C/C++(底层库和JNI接口) | C/C++,其他语言(如Python) |
开发环境 | Android Studio,Eclipse等 | Visual Studio Code,Eclipse,Code::Blocks等,或自定义开发环境 |
用户界面 | Android UI框架(如XML布局、Activity等) | 需自选或开发图形界面库(如LVGL、Qt、GTK+等) |
系统组件 | Activity、Service、Broadcast Receiver等 | 无统一系统组件,根据项目需求自行设计和实现 |
资源管理 | 严格的资源管理规定(如内存、电源等) | 无统一资源管理规定,需要根据需求进行优化 |
应用分发 | Google Play或其他应用市场 | 通过设备制造商或系统集成商进行部署和升级 |
设备驱动开发 | Android HAL层设备驱动开发 | 基于Linux内核的设备驱动开发 |
系统定制和移植 | Android系统定制和移植 | 嵌入式Linux系统定制和移植 |
目标设备 | 主要针对移动设备(如手机、平板等) | 针对各种嵌入式设备(如路由器、工控设备等) |
这个表格展示了Android开发和嵌入式Linux开发的主要异同点。虽然它们在底层都基于Linux内核,但在应用开发、用户界面、系统组件等方面有很大的差异。嵌入式Linux的GUI框架就不像Android那么完善和便捷,比如想要实现嵌入式的用户界面,使用C语言开发的LVGL框架来手写界面代码,UI交互代码会显得冗余
example:
#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_BTN
static void btn_event_cb(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * btn = lv_event_get_target(e);
if(code == LV_EVENT_CLICKED) {
static uint8_t cnt = 0;
cnt++;
/*Get the first child of the button which is the label and change its text*/
lv_obj_t * label = lv_obj_get_child(btn, 0);
lv_label_set_text_fmt(label, "Button: %d", cnt);
}
}
/**
* Create a button with a label and react on click event.
*/
void lv_example_get_started_1(void)
{
lv_obj_t * btn = lv_btn_create(lv_scr_act()); /*Add a button the current screen*/
lv_obj_set_pos(btn, 10, 10); /*Set its position*/
lv_obj_set_size(btn, 120, 50); /*Set its size*/
lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL, NULL); /*Assign a callback to the button*/
lv_obj_t * label = lv_label_create(btn); /*Add a label to the button*/
lv_label_set_text(label, "Button"); /*Set the labels text*/
lv_obj_center(label);
}
#endif
UI效果如下:
跨进程通信对比
在Android和Linux系统中,跨进程通信(IPC)是一种用于在不同进程之间传递数据和消息的机制。以下是Android和Linux中跨进程通信的对比:
维度 | Android IPC | Linux IPC |
---|---|---|
Binder | 提供Binder机制进行跨进程通信 | 不支持Binder机制 |
Unix套接字 | 支持Unix域套接字 | 支持Unix域套接字 |
消息队列 | 不直接支持SysV消息队列,可通过JNI使用 | 支持SysV消息队列和POSIX消息队列 |
共享内存 | 支持匿名共享内存(ashmem)和内存文件映射 | 支持SysV共享内存和POSIX共享内存 |
信号 | 受限的信号支持,不推荐用于IPC | 支持信号(signal)进行简单的进程间通信 |
管道和有名管道 | 支持管道(pipe)和有名管道(FIFO) | 支持管道(pipe)和有名管道(FIFO) |
信号量 | 不直接支持SysV信号量,可通过JNI使用 | 支持SysV信号量和POSIX信号量 |
D-Bus | 不直接支持D-Bus,可通过第三方库使用 | 支持D-Bus进行桌面环境和系统服务间的通信 |
其中Binder机制是Android开发非常重要的知识点,原理图如下所示:
图片引自:https://zhuanlan.zhihu.com/p/35519585
Binder的优势在于提供一种高性能、稳定性和安全性跨进程通信机制。基于C/S架构,职责明确、架构清晰;通信过程中仅需要进行一次内存拷贝,性能仅次于共享内存;然而它为每个APP进程分配UID,可以通过UID鉴别身份。
D-Bus
D-BUS是一种进程间通信(IPC)机制,一般主要用于基于AF_UNIX套接字的本地进程间通信(local IPC)(当然也可以基于TCP/IP)实现跨主机的通信。原理图如下所示:
图片引自:https://hustcat.github.io/getting-started-with-dbus/
D-Bus协议是一个端到端的通信协议,核心基础概念参考:
编程语言对比
参数 | Java | Kotlin | C++ |
---|---|---|---|
历史 | 1995年由James Gosling 在 Sun Microsystems 开发 | 2011年由JetBrains开发 | 1979年由Bjarne Stroustrup 在贝尔实验室开发 |
编程范式 | 面向对象 | 面向对象和函数式编程 | 面向过程和面向对象 |
平台依赖 | 平台无关 | 平台无关 | 平台相关 |
编译与解释 | 编译解释 | 编译解释 | 仅编译 |
内存管理 | 系统控制 | 系统控制 | 手动控制 |
可移植性 | 可移植 | 可移植 | 不可移植 |
指针 | 有限支持 | 不支持 | 强烈支持 |
参数传递 | 按值传递 | 按值传递 | 按值传递和按引用传递 |
重载 | 仅方法重载 | 运算符和方法重载 | 运算符和方法重载 |
线程支持 | 内置线程支持 | 内置线程支持 | 依赖第三方线程库 |
文档注释 | 支持 | 支持 | 不支持 |
兼容性 | 不兼容其他语言 | 兼容Java | 兼容C语言 |
goto语句 | 不支持 | 不支持 | 支持 |
多重继承 | 单继承 | 单继承 | 单继承和多继承 |
结构体与共用体 | 不支持 | 支持数据类 | 支持 |
虚拟关键字 | 所有非静态方法默认virtual | 不支持virtual关键字 | 支持virtual关键字 |
硬件 | 离硬件较远 | 离硬件较远 | 接近硬件 |
数据与功能 | 需在类中,可有包作用域 | 需在类中,可有包作用域 | 提供全局作用域和命名空间作用域 |
运行时错误检测 | 系统处理 | 系统处理 | 程序员处理 |
根层次结构 | 支持单根层次结构 | 支持单根层次结构 | 无根层次结构 |
输入输出 | System.in 和 System.out.println | println和readLine() | Cin和Cout |
C++、Java和Kotlin之间的最大区别在于它们的编程范式、内存管理和平台依赖性。
- 编程范式:C++支持面向过程和面向对象编程,而Java和Kotlin主要支持面向对象编程。Kotlin还支持函数式编程。
- 内存管理:C++需要程序员手动管理内存分配和释放,而Java和Kotlin使用自动内存管理(垃圾回收机制),这使得Java和Kotlin更易于使用,但可能在某些情况下牺牲了性能。
- 平台依赖性:C++是平台相关的,需要针对不同平台进行编译。Java和Kotlin则是平台无关的,可以一次编写并在任何支持Java虚拟机(JVM)的平台上运行。Kotlin还可以编译为JavaScript和本地代码,从而实现更广泛的平台兼容性。
这些区别使得C++更适合底层系统开发、性能关键应用和嵌入式系统,而Java和Kotlin更适合跨平台应用、Web应用和移动应用开发。
开发工具、编译工具对比
项目 | Android开发 | 嵌入式Linux开发 |
---|---|---|
开发工具 | Android Studio, Eclipse等 | Visual Studio Code,Eclipse, Code::Blocks等, 或自定义开发环境 |
编译工具 | Gradle (应用层), Android NDK (底层库和JNI接口) | Make, CMake, Autotools等 |
编译器 | Java编译器 (应用层), GCC (底层库和JNI接口) | GCC, Clang等 |
调试器 | Android Debug Bridge (ADB), Logcat, DDMS等 | GDB, KGDB等 |
版本控制 | Git, SVN, Mercurial等 | Git, SVN, Mercurial等 |
性能分析工具 | Android Profiler, Traceview, Systrace等 | Perf, Valgrind, OProfile等 |
静态代码分析 | Lint, SonarQube等 | Lint, cppcheck, Coverity等 |
模拟器/仿真器 | Android模拟器, Genymotion等 | QEMU, VirtualBox等 |
持续集成/部署 | Jenkins, CircleCI, GitLab CI等 | Jenkins, CircleCI, GitLab CI等 |
Android开发和嵌入式Linux开发使用的开发工具和编译工具有一些核心差异,以下是一些主要差异点:
开发工具:
Android开发:
- Android Studio:这是Google为Android开发者提供的官方集成开发环境(IDE),内置了代码编辑器、调试器、模拟器等工具,支持Java和Kotlin语言进行Android应用开发。
- ADB(Android Debug Bridge):这是一个命令行工具,用于在开发机和Android设备之间进行通信,支持安装应用、查看系统日志、调试应用等功能。
嵌入式Linux开发:
- Eclipse、Visual Studio Code等通用IDE:这些IDE支持C/C++和其他语言,可以用于嵌入式Linux应用开发。
- GDB(GNU Debugger):这是一个强大的源代码级调试器,用于调试嵌入式Linux应用程序。
编译工具:
Android开发:
- Gradle:这是Android的官方构建工具,用于编译和打包Android应用。
- Android NDK(Native Development Kit):这是一个工具集,用于编译和链接使用C/C++编写的Android应用的本地部分。
嵌入式Linux开发:
- GCC(GNU Compiler Collection):这是一个开源的编译器集合,用于编译C/C++和其他语言的代码。
- Make:这是一个构建工具,用于自动化编译和链接过程。
- CMake:这是一个跨平台的构建系统,用于生成Makefile或其他构建脚本。
包管理和依赖管理对比
项目 | Android开发 | 嵌入式Linux开发 |
---|---|---|
包管理系统 | APK (Android Package) | dpkg, RPM, ipkg等 |
包管理工具 | ADB (Android Debug Bridge) | apt-get, yum, opkg等 |
依赖管理 | Gradle, Maven等 | Conan,Makefile, autoconf等 |
应用分发 | 国内应用商店(小米、华为、OPPO、Vivo等)、Google Play, APKPure等 | 通过设备制造商或系统集成商进行部署和升级 |
应用更新 | 自建应用升级,OTA升级更新,Google Play自动更新 | OTA升级更新,也可以手动更新或通过脚本自动更新 |
在Android和嵌入式Linux开发中,包管理和依赖管理是两个相关的概念,它们共同处理应用程序或系统所需的库、组件和资源。以下是它们在包管理和依赖管理方面的主要区别:
Android包管理和依赖管理:
- APK(Android Package Kit):这是Android应用程序的安装包格式,包含了应用程序的所有代码、资源、证书以及清单文件等。
- 应用商店:Android应用程序通常通过应用商店(如Google Play、华为应用市场等)进行分发和更新。应用商店负责应用程序的审核、签名、安装、更新等功能。
- Gradle:Android Studio使用Gradle作为构建系统,它负责处理应用程序的依赖关系。开发者可以在项目的build.gradle文件中声明所需的第三方库,Gradle会自动从远程仓库(如Maven Central、JCenter等)下载并集成这些库。
- Android SDK/NDK:Android SDK提供了一套用于开发Android应用程序的API和组件,而Android NDK提供了一套用于处理本地C/C++代码依赖关系的工具。这些组件已经包含在Android系统中,无需额外处理依赖关系。
嵌入式Linux包管理和依赖管理:
- 包格式:嵌入式Linux系统的包格式取决于具体的发行版,如Debian/Ubuntu使用deb包,Red Hat/CentOS使用RPM包,OpenWrt使用opkg包等。
- 软件仓库:嵌入式Linux应用程序通常通过软件仓库进行分发和更新。软件仓库是一个包含了预编译软件包的服务器,用户可以通过包管理器(如apt、yum、opkg等)从软件仓库安装和更新软件包。
- 包管理器:嵌入式Linux发行版通常提供了一个包管理器(如apt、yum、opkg等),用于自动处理系统和应用程序的依赖关系。开发者可以通过包管理器从软件仓库安装所需的库和组件。
- 构建系统:嵌入式Linux开发中,Makefile、autoconf和CMake等构建工具可以用于处理项目的依赖关系。开发者需要在构建脚本中手动声明所需的库和组件。
可运行文件对比
Android APK(Android Package)和Linux的可执行文件是两种不同的应用程序格式,它们分别用于Android和Linux系统。以下是Android APK和Linux可执行文件的对比:
维度 | Android APK | Linux可执行文件 |
---|---|---|
文件格式 | APK(Android Package) | ELF(可执行和可链接格式) |
用途 | Android应用程序的安装包 | Linux系统上的可执行程序 |
打包内容 | 应用程序代码、资源、清单文件等 | 可执行代码、数据、符号表等 |
代码类型 | Java/Kotlin字节码、C/C++库(可选) | 通常为编译后的机器代码 |
运行环境 | Android运行时(ART)或Dalvik虚拟机 | 直接在Linux操作系统上运行 |
安装过程 | 通过应用商店或ADB安装到Android设备上 | 通过包管理器、编译安装或手动复制到系统目录 |
更新机制 | 通过应用商店自动更新或手动更新 | 通过包管理器更新或手动替换可执行文件 |
安全和权限 | Android权限模型、应用签名 | Linux用户/组权限、文件权限等 |
APK文件一览:
Android Studio 分析apk:
Linux中ELF可执行文件一览:
性能分析工具对比
项目 | Android开发 | 嵌入式Linux开发 |
---|---|---|
CPU性能分析 | Traceview, Systrace, Simpleperf等 | Perf, OProfile, GProf等 |
内存性能分析 | Android Profiler, LeakCanary等 | Valgrind, Massif等 |
磁盘I/O分析 | Android Profiler, iostat等 | iostat, blktrace等 |
网络性能分析 | Android Profiler, tcpdump等 | tcpdump, Wireshark, iperf等 |
电源性能分析 | Battery Historian, Systrace等 | PowerTOP, Intel Energy Profiler等 |
GPU性能分析 | GPU Debugging, Systrace等 | GPU PerfStudio, NVIDIA Nsight等 |
应用性能分析 | Android Profiler, Firebase Performance等 | 自定义性能分析工具或第三方库 |
系统性能分析 | Systrace, Android Profiler等 | SystemTap, LTTng, Ftrace等 |
实时性能分析 | Systrace, Android Profiler等 | PREEMPT_RT补丁, RT-Tester等 |
Android我们关注的性能指标在Linux上其实也大同小异,只是在不同的体系下分析手段和工具不一样。相比于Linux分析Android应用的性能要便捷得多,Android Studio内置了强大的性能分析工具—Android Profiler,可以分析CPU、Memory、Network、Energy和Timeline。
写在最后
本文从架构、主要差异、编程语言、IDE/编译工具、包管理、可运行文件和性能分析工具进行了详细对比,如果是有Android开发经验的要迁移到嵌入式Linux需要学习的内容确实还不少,但研发思路是大同小异的,大致就是通过开发框架和编程语言组织代码,通过跨进程通信来实现服务之间的调用,通过编译工具编译成能在系统运行环境的可执行文件,然后你需要关注如何进行应用更新,需要针对跑起来的应用进行性能分析等等。当然实际的研发工作会更加复杂,要实现一个可商用的产品需要结合业务做更多的能力拓展,比如增加日志上报、崩溃捕获、网络组件、存储组件、异步编程组件等等。