因业务需要,过去一年从熟悉的Android开发开始涉及嵌入式Linux开发,编程语言也从Java/Kotlin变成难上手的C++,这里面其实有很多差异点,特此整理本文来详细对比这两者开发的异同,便于对嵌入式Linux开发感兴趣的同学一些参考。
注:左边是Android的平台架构,右边是目前我们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开发非常重要的知识点,原理图如下所示:
Binder的优势在于提供一种高性能、稳定性和安全性跨进程通信机制。基于C/S架构,职责明确、架构清晰;通信过程中仅需要进行一次内存拷贝,性能仅次于共享内存;然而它为每个APP进程分配UID,可以通过UID鉴别身份。
D-Bus
D-BUS是一种进程间通信(IPC)机制,一般主要用于基于AF_UNIX套接字的本地进程间通信(local IPC)(当然也可以基于TCP/IP)实现跨主机的通信。原理图如下所示:
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更适合跨平台应用、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开发:
嵌入式Linux开发:
编译工具:
Android开发:
嵌入式Linux开发:
项目 | 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包管理和依赖管理:
嵌入式Linux包管理和依赖管理:
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需要学习的内容确实还不少,但研发思路是大同小异的,大致就是通过开发框架和编程语言组织代码,通过跨进程通信来实现服务之间的调用,通过编译工具编译成能在系统运行环境的可执行文件,然后你需要关注如何进行应用更新,需要针对跑起来的应用进行性能分析等等。当然实际的研发工作会更加复杂,要实现一个可商用的产品需要结合业务做更多的能力拓展,比如增加日志上报、崩溃捕获、网络组件、存储组件、异步编程组件等等。