一、Android Framework架构概览
Android开源项目 (AOSP) 是公开发布且可修改的Android源代码。任何人都可以下载并修改AOSP以适配其设备。AOSP提供Android移动平台的功能完备的代码实现。
Android是Google开发的基于Linux底层实现的开源操作系统,Android平台的主要组件如图,包含了Linux内核、HAL层、Native C/C++库、Android运行时环境、Java API框架与应用等。
二、Android系统核心组件
(1).安卓系统应用
包含了电子邮件、短信、日历、浏览器和相机等核心应用。
(2).Java API框架
Java API框架位于APP应用层和Native C/C++库之间,它可以为Android App开发提供Java/Kotlin API接口,比如Activity等,也可以通过JNI(Java Native Interface)接口调用Native C/C++本地库,比如OpenGL、SQLite等。
在Android系统中,Native C/C++库是连接Linux内核与Java API框架的桥梁。
(3).Native C/C++库
Native C/C++库位于Android系统的底层,它运行在Linux内核之上,处于Android Runtime运行时环境(ART/Dalvik 虚拟机)和Android Framework框架(Java/Kotlin API)之下,Android底层许多核心组件和服务都需要使用C/C++代码编写的原生代码进行构建。
Android系统的文件系统、进程管理、内存管理、网络协议栈等核心功能主要由Native C/C++库实现。
硬件抽象层(HAL)与特定硬件(比如摄像头、传感器、音频设备、蓝牙驱动等)通信的接口通常由Native C/C++库实现,并通过JNI接口向Java API框架层提供统一的API调用接口。
Native C/C++库可以复用现有的C/C++跨平台项目代码,比如它可以移植Linux/Unix生态的成熟的OpenGL、SQLite等库文件,进行复用,避免了二次开发。
如果直接使用C/C++代码开发安卓应用,可以利用Android NDK工具链直接从本地代码访问某些原生的Native C/C++库。
(4).Android运行时环境ART
Android运行时环境ART(Android Runtime),在Android 5.0以后的版本中,ART替代了早期的Dalvik虚拟机,其核心目标是通过预编译优化来提升应用程序的启动速度和性能。
ART的核心组件如下:
1.编译器:比如dex2oat工具,支持将DEX字节码编译为本地机器码。
2.运行时库:比如libart.so,提供运行时支持。
3.垃圾回收器:支持高效地分配和回收内存。
(5).硬件抽象层 (HAL)
HAL层的核心功能是将硬件设备的操作抽象化,向Android Java框架和应用层提供统一的API接口。
例如,当安卓App请求调用Camera API拍照时,Android框架通过JNI或HIDL(Hardware Interface Definition Language)触发HAL模块的对应函数,HAL层再通知Linux内核去加载和调用相机驱动程序。
Android设备的每个硬件模块,比如音频、相机或传感器等,都有对应的HAL模块,这些HAL模块通常是以”.so”动态链接库文件的形式存放在Android系统的特定目录(比如/vendor/lib/hw/目录)中的。
HAL层提供一组标准化的C/C++接口函数来访问硬件资源。例如,音频模块的HAL层提供open_output_stream()函数接口来处理音频输出。
为了确保性能和安全性,HAL层在用户空间运行,与Linux内核隔离,减少了系统崩溃的风险。HAL层可以通过系统调用(比如ioctl)与Linux驱动交互。
(6).Linux内核
Android操作系统基于Linux内核构建,Linux内核在Android平台中负责管理底层硬件资源(如CPU、内存、存储和网络设备),并提供进程调度、内存管理、文件系统管理和设备驱动等关键功能,比如,ART依赖Linux内核来实现底层的线程管理和内存回收等功能。
Android系统针对原生Linux内核进行了大量修改和扩展以适应移动设备的特殊需求,主要扩展包括:
a.进程间通信IPC:采用Binder机制替代传统IPC。
b.电源管理:采用Wakelock唤醒锁延长Active状态时间,防止系统在关键操作时休眠。
c.内存管理:使用了高效、自动回收的匿名共享内存(Ashmem)和出现OOM时维持系统稳定的低内存终止机制(LowMemoryKiller)。
d.安全访问机制:强制使用SELinux策略进行访问控制。
e.设备驱动:专为移动设备硬件(比如传感器、摄像头)等设计的驱动程序框架。
Android内核版本通常滞后于主线Linux内核版本,但近年来正在加速与主线内核版本融合的趋势。
Android Boot启动流程:
(图源:https://gityuan.com/android/)
三、Android系统常用分区
boot分区:包含一个内核映像,使用 mkbootimg 创建。
system分区:包含 Android 框架。
odm分区:包含原始设计制造商 (ODM) 对系统芯片 (SoC) 供应商板级支持包 (BSP) 的自定义设置。
recovery分区:会存储在 OTA 过程中启动的恢复映像。
cache分区:会存储临时数据,如果设备使用无缝更新,则是可选的。cache 分区并非必须可从引导加载程序写入,但必须可清空。
userdata分区:包含用户安装的应用和数据,包括自定义数据。
metadata分区:用于在设备使用元数据加密时存储元数据加密密钥。
vendor分区:包含所有无法分发给 AOSP 的二进制文件。
vendor_dlkm分区:专门用于存储供应商内核模块。
radio分区:包含无线装置映像,只有包含无线装置且在专用分区中存储无线装置专用软件的设备才需要。
四、Android NDK简介
Android NDK(Native Development Kit)是Android官方提供的C/C++开发工具集,让开发者可以使用C/C++编程实现安卓框架下的核心功能。
1.Android NDK核心功能
(1).Android Native代码开发
支持使用C/C++编写Android应用的性能敏感模块,比如图形渲染、物理引擎等。
支持通过JNI实现Java/Kotlin与C/C++的接口交互。
(2).跨平台ABI支持
可以编译生成不同CPU架构的二进制文件,常见的ABI有armeabi-v7a、arm64-v8a等。
2.基于Android NDK的JNI开发流程
3.Android NDK主要组件
(1).Android交叉编译工具链
包含Clang编译器(默认编译器,支持C++17/20标准)、gcc(旧版使用,NDK r18后仅保留Clang)、调试器(lldb)等。
支持CMake(官方推荐)和ndk-build(基于传统Makefile)构建系统。
(2).Android原生系统库
提供对Android系统底层组件的访问,比如:
OpenGL ES/Vulkan:3D图形渲染库,性能比Java层实现高30%+。
Bionic C库:轻量级C标准库的实现
注:所有API通过””头文件引入
(3).调试与分析工具
ndk-stack:可以用于分析Android应用崩溃时的异常堆栈。
ndk-gdb:可以对JNI C++代码做断点调试。
SimplePerf:性能分析工具,可以用于生成火焰图、分析Native性能瓶颈。
Example
五、Android NDK交叉编译配置
Android NDK工具链:
基于Android NDK进行开发的难点是编译参数和原生C/C++差别很大,对此,CMake官网给的配置参数如下(官方的原版说明很详细):
1.CMAKE_SYSTEM_NAME
Set to Android. Must be specified to enable cross compiling for Android.
2.CMAKE_SYSTEM_VERSION
Set to the Android API level.
3.CMAKE_ANDROID_ARCH_ABI
Set to the Android ABI (architecture). If not specified, this variable will default to the first supported ABI in the list of armeabi, armeabi-v7a and arm64-v8a.
4.CMAKE_ANDROID_NDK
Set to the absolute path to the Android NDK root directory.
5.CMAKE_ANDROID_NDK_DEPRECATED_HEADERS
Set to a true value to use the deprecated per-api-level headers instead of the unified headers. If not specified, the default will be false unless using a NDK that does not provide unified headers.
6.CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION
On NDK r19 or above, this variable must be unset or set to clang.
7.CMAKE_ANDROID_STL_TYPE
Set to specify which C++ standard library to use.
a.CMakeList.txt配置样例:
set(CMAKE_SYSTEM_NAME Android)
set(CMAKE_SYSTEM_VERSION 21) # API level
set(CMAKE_ANDROID_ARCH_ABI arm64-v8a)
set(CMAKE_ANDROID_NDK /path/to/android-ndk)
set(CMAKE_ANDROID_STL_TYPE gnustl_static)
b.CMake编译脚本样例:
cmake ../src \
-DCMAKE_SYSTEM_NAME=Android \
-DCMAKE_SYSTEM_VERSION=21 \
-DCMAKE_ANDROID_ARCH_ABI=arm64-v8a \
-DCMAKE_ANDROID_NDK=/path/to/android-ndk \
-DCMAKE_ANDROID_STL_TYPE=gnustl_static
补充:在Android Studio开发环境中,早期的Android NDK只支持ndk-build编译(编写Android.mk/Application.mk文件),从Android Studio 2.2开始,官方引入对CMake的支持,并逐渐推荐使用CMake。
Android Studio开发环境的ndk-build & CMake 集成方式对比:
1.配置文件
ndk-build配置示例——Android.mk文件:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := native-lib # 必须与Java加载名一致
LOCAL_SRC_FILES := native-lib.cpp
include $(BUILD_SHARED_LIBRARY)
CMake配置示例——CMakeLists.txt文件:
cmake_minimum_required(VERSION 3.4.1)
add_library(native-lib SHARED #自动匹配Java加载名
native-lib.cpp)
target_link_libraries(native-lib log)
2.Android Studio集成路径
ndk-build集成:
android {
externalNativeBuild {
ndkBuild {
path “src/main/jni/Android.mk”
}
}
}
CMake集成:
android {
externalNativeBuild {
cmake {
path “src/main/cpp/CMakeLists.txt”
version “3.22.1”
}
}
}
六、基于Android NDK的JNI开发实战
案例一:Android官方样例——hello-jni
Android NDK代码官方示例:
https://github.com/android/ndk-samples/
1.交叉编译配置
-DCMAKE_FIND_ROOT_PATH=${HOME}/ndk-samples/hello-jni/app/.cxx/cmake/universalDebug/prefab/armeabi-v7a/prefab
-DCMAKE_BUILD_TYPE=Debug
-DCMAKE_TOOLCHAIN_FILE=${HOME}/Android/Sdk/ndk/22.1.7171670/build/cmake/android.toolchain.cmake
-DANDROID_ABI=armeabi-v7a
-DANDROID_NDK=${HOME}/Android/Sdk/ndk/22.1.7171670
-DANDROID_PLATFORM=android-23
-DANDROID_LINKER_FLAGS=”-landroid -llog”
-DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a
-DCMAKE_ANDROID_NDK=${HOME}/Android/Sdk/ndk/22.1.7171670
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
-DCMAKE_MAKE_PROGRAM=${HOME}/Android/Sdk/cmake/3.10.2.4988404/bin/ninja
-DCMAKE_SYSTEM_NAME=Android
-DCMAKE_SYSTEM_VERSION=23
2.CMakeLists.txt编写
cmake_minimum_required(VERSION 3.22.1)
project(“hello-jni” LANGUAGES CXX)
include(AppLibrary)
find_package(base CONFIG REQUIRED)
add_app_library(hello-jni SHARED
hello-jni.cpp
)
target_link_libraries(hello-jni
PRIVATE
base::base
android
log
)
3.hello-jni.cpp源码实现
include
include
include
jstring StringFromJni(JNIEnv* env, jobject) {
std::string hello = “Hello from JNI.”;
return env->NewStringUTF(hello.c_str());
}
extern”C”JNIEXPORT jint JNI_OnLoad(JavaVM* _Nonnull vm, void* _Nullable) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
jclass c = env->FindClass(“com/example/hellojni/HelloJni”);
if (c == nullptr) return JNI_ERR;
staticconst JNINativeMethod methods[] = {
{“stringFromJNI”, “()Ljava/lang/String;”,
reinterpret_cast(StringFromJni)},
};
int rc = env->RegisterNatives(c, methods, arraysize(methods));
if (rc != JNI_OK) return rc;
return JNI_VERSION_1_6;
}
4.Kotlin源码实现
package com.example.hellojni
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.hellojni.databinding.ActivityHelloJniBinding
class HelloJni : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityHelloJniBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.helloTextview.text = stringFromJNI()
}
external fun stringFromJNI(): String?
external fun unimplementedStringFromJNI(): String?
companion object {
init {
System.loadLibrary("hello-jni")
}
}
}
运行效果:
案例二:基于NDK生成包含简单加法接口的JNI库
Demo1:C++实现addNumbers接口
include
jint addNumbers(jint a, jint b) {
return a + b;
}
extern “C” JNIEXPORT jint JNICALL
Java_com_rb_nativetestlib_NativeModule_addNumbers(JNIEnv *env, jobject thiz, jint a, jint b) {
return addNumbers(a, b);
}
cmake构建示例:
add_library(
# Specifies the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
Demo2: Kotlin加载JNI库并调用addNumbers接口
class NativeLib {
init {
// Loads the native library at runtime
System.loadLibrary(“native-lib”)
}
// Declares a native method
external fun addNumbers(a: Int, b: Int): Int
}
val sum = NativeLib().addNumbers(15, 12)
参考阅读:
https://android.googlesource.com/platform/system/extras/+/master/simpleperf/doc/README.md
https://www.droidcon.com/2024/01/16/using-c-c-in-android-a-comprehensive-guide-for-beginners/
https://medium.com/@dev.rutwijb/android-ndk-overview-509e198645c3
https://www.clika.io/blog-posts/androidcrosscompilation1
https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-android-with-the-ndk
声明:来自程序员与背包客,仅代表创作者观点。链接:https://eyangzhen.com/3311.html