NDK、SDK、APP、Framework、Native 到底怎么区分?

0 阅读19分钟

NDK、SDK、APP、Framework、Native 到底怎么区分?

学 Android 时,很容易被几个词绕进去:SDK、NDK、APP、Framework、Native。

有时候你看到的是:

用 Android SDK 开发 App。
用 Android NDK 写 C/C++。
App 调 Framework API。
Framework 最后进 native service。
Java 里有 native 方法。
Native 崩溃了,看 tombstone。

每句话看起来都对,但放在一起就容易乱:

SDK 是不是 Framework?
NDK 是不是 Native 层?
用了 C++ 的 App,算不算 native App?
App 里的 .so 和系统里的 native service 是不是一回事?
Framework 是 Java 层,Native 是 C++ 层,这样理解够不够?
系统开发说的 native,和 Java 里的 native 关键字,是不是同一个概念?

这篇先把问题问清楚:这些词不是同一个维度上的概念。 01_concept_coordinate_map.png

有些词描述的是开发工具包,比如 SDK、NDK。

有些词描述的是交付形态和运行身份,比如 APP。

有些词描述的是Android 系统分层,比如 Framework、Native、HAL、Kernel。

有些词描述的是代码语言或运行实现,比如 Java/Kotlin、C/C++、JNI、native 方法。

如果把这些维度混在一起,就会出现一种很典型的误解:以为用了 NDK 就是在做 Android Native 层开发;以为 SDK 就等于 Framework;以为 App 只要带了 .so 就变成系统 native 模块;以为 native 崩溃一定就是 NDK 问题。

先把结论放前面:

SDK:
    Android 提供给开发者的工具包和 API 契约,主要用于编译、调试、打包、运行 Android 应用。

NDK:
    Android 提供给开发者写 C/C++ 代码的 native 开发工具包,通常把 C/C++ 编译成 .so 放进 App。

APP:
    交付给设备安装和运行的应用单位,表现为 APK/AAB,运行时是一个或多个进程,里面可以有 Java/Kotlin,也可以有 native .so。

Framework:
    Android 系统向上提供能力的框架层,包括公开 API、系统 API、Framework Java 代码、system_server 中的系统服务等。

Native:
    一个上下文相关的词。它可以表示 C/C++ 代码,可以表示系统 native 层的 daemon/library/service,也可以表示 Java native 方法背后的 JNI 实现。

真正要先问的是:

我现在讨论的是开发工具,还是运行层级?
我现在讨论的是代码语言,还是系统模块?
我现在讨论的是编译时看到的东西,还是设备运行时真正执行的东西?

只要这三个问题问清楚,NDK、SDK、APP、Framework、Native 就不会再混成一团。

一、先建立坐标系:这些词不在同一条线上

很多时候不是定义有多难,而是我们把几个不同视角的词放在了一条线上。

先按四个视角拆开看:

工具包维度:
    SDK、NDK
​
交付物维度:
    APP、APK、AAB、library、.so
​
系统分层维度:
    App、Framework、System Service、Native Service、HAL、Kernel
​
语言和运行实现维度:
    Java/Kotlin、C/C++、ART、JNI、native method、native crash

同一个对象可以同时落在多个维度里。

比如一个普通 Android App:

从交付物看:
    它是 APK/AAB。
​
从系统分层看:
    它运行在 App 层。
​
从开发工具看:
    它通常使用 Android SDK 编译、打包和调试。
​
从语言实现看:
    它可以主要由 Kotlin/Java 写成,也可以包含 C/C++ 编译出来的 .so。

再比如一个使用 NDK 的视频处理 App:

它仍然是 App。
它仍然运行在 App 进程和 App 沙箱里。
它仍然会使用 Android SDK 访问 Activity、Camera、MediaCodec 等 API。
它额外使用 NDK 把 C/C++ 编译成 native library。
它的 .so 是 App native code,不自动等于 Android 系统 native service。

所以,别急着用一句“这是 native”解决所有问题,最好先补一句:

这是 App 里的 native library?
还是 AOSP 里的 native daemon?
还是 Framework JNI 的 native 实现?
还是 Java 方法声明里的 native 关键字?
还是产品形态里说的 native App?

一个词能不能说清楚,取决于上下文有没有说清楚。

二、SDK 是什么?为什么它不是 Framework?

02_sdk_vs_framework.png

SDK 的全称是 Software Development Kit,软件开发工具包。

放到 Android 里,Android SDK 可以理解为:Android 提供给应用开发者的一套开发入口

它通常包含这些东西:

platforms/android-xx/android.jar:
    编译时使用的 Android API 声明。

build-tools:
    aapt2、d8、zipalign、apksigner 等构建和打包工具。

platform-tools:
    adb、fastboot 等和设备交互的工具。

cmdline-tools:
    sdkmanager、avdmanager、lint 等命令行工具。

emulator 和 system images:
    用于创建和运行模拟器。

文档、samples、API reference:
    用于理解 API 行为和开发方式。

这里最容易误解的是 android.jar

很多人看到 App 代码里写:

val manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager

或者:

startActivity(intent);

就会觉得“我在调用 SDK”。这个说法在开发者视角下可以成立,但更准确地说:

编译时:
    你的代码依赖 SDK 里的 API 声明。

运行时:
    你的代码调用的是设备系统里真正的 Framework 实现。

android.jar 更像一个编译契约。它告诉编译器:

这些类存在。
这些方法可以调用。
这些参数和返回值是什么。
这些 API 属于当前 compileSdk 能看到的范围。

但它不是设备上真正执行的完整 Framework。

设备运行时,真正提供能力的是系统镜像里的 Framework、ART、system_server、native service、HAL、Kernel 等一整套系统组件。

所以 SDK 和 Framework 的关系可以这样压住:

SDK 是开发者编译和调用 Android 能力时看到的工具包和 API 契约。
Framework 是 Android 系统在设备上提供这些能力的实现和抽象层。

再换一句更直接的话:

SDK 是“你怎么编译、怎么引用、能看到哪些 API”。
Framework 是“设备里谁真正实现这些 API,谁真正处理这些请求”。

因此,SDK 不是 Framework。SDK 只是把 Framework 的一部分能力以稳定 API 的形式暴露给 App 开发者。

三、NDK 是什么?为什么它不是 Android Native 层?

03_ndk_vs_native_layer_v2.png

NDK 的全称是 Native Development Kit,原生开发工具包。

Android 官方对 NDK 的核心定位很明确:它是一套工具,让你可以用 C/C++ 实现 App 的一部分代码。

NDK 通常包含这些东西:

Clang/LLVM toolchain:
    用来把 C/C++ 编译成 Android 可运行的机器码。

sysroot、headers、libraries:
    提供 Android 支持的 native API 声明和链接入口。

CMake / ndk-build 集成:
    帮助 Gradle 构建 native library。

ABI 支持:
    arm64-v8a、armeabi-v7a、x86_64 等。

调试和符号工具:
    lldb、addr2line、objdump、readelf 等相关能力。

使用 NDK 后,常见结果是生成一个或多个 .so

app/src/main/jniLibs/arm64-v8a/libxxx.so
app/build/intermediates/merged_native_libs/...
APK/lib/arm64-v8a/libxxx.so

App 运行时通过 JNI 或其他 native 入口加载这些 .so,然后在 App 进程里执行 C/C++ 代码。

关键点来了:NDK 不是 Android 系统的 Native 层本身

NDK 是工具包。Native 层是运行实现或系统分层。

这两个概念的关系更像这样:

NDK:
    面向 App 开发者的 native 开发工具包。

App native library:
    用 NDK 编译出来、随 App 一起安装的 .so。

Android native layer:
    AOSP 中的 C/C++ 系统组件、native library、native service、daemon、HAL 相关实现等。

比如你用 NDK 写了一个图像滤镜库:

Kotlin/Java UI
  -> JNI
  -> libfilter.so
  -> C++ 算法

这叫 App 使用 NDK 写了一部分 native code。

但它不等于你在修改 SurfaceFlinger、AudioFlinger、netd、vold、init、libbinder、libcutils 这些 Android 系统 native 组件。

所以看到“NDK 开发”时,要先问:

这是 App 里的 C/C++ 代码?
还是 AOSP 系统源码里的 C/C++ 模块?

前者通常是应用 native 开发。后者才更接近整机系统开发里说的 native 层开发。

四、APP 是什么?为什么 APP 里可以同时有 SDK 和 NDK?

APP 不是语言概念,也不是工具包概念。

在 Android 里,APP 更接近一个交付和运行单位。

它通常表现为:

开发阶段:
    一个 Gradle module 或多个 module 组成的应用工程。

构建产物:
    APK 或 AAB。

安装后:
    一个 packageName,对应 UID、权限、数据目录、组件声明等。

运行时:
    一个或多个 Linux 进程,通常运行在应用沙箱中。

一个 App 里可以包含很多东西:

classes.dex:
    Kotlin/Java 编译后的字节码。

resources.arsc 和 res/:
    资源文件。

AndroidManifest.xml:
    组件、权限、特性、provider、service 等声明。

lib/arm64-v8a/*.so:
    native library。

assets/:
    原始资源。

所以 App 和 SDK/NDK 的关系不是并列关系,而是:

App 是最终交付物和运行主体。
SDK 是开发这个 App 时使用的主要 Android 工具包和 API 契约。
NDK 是当这个 App 需要 C/C++ 时额外使用的 native 工具包。

放到常见场景里就是:

只用 Kotlin/Java 的 App:
    使用 SDK,不一定使用 NDK。

包含 C/C++ 的 App:
    使用 SDK,也使用 NDK。

系统预装 App:
    仍然是 App,但可能使用普通 Android API、System API,甚至访问部分隐藏接口。

平台 Framework 模块:
    不是普通 App,通常随系统镜像构建和更新。

一个 App 带了 .so,并不会改变它作为 App 的本质。

它只是说明:

这个 App 的一部分逻辑运行在 native code 中。

它仍然受 App UID、SELinux 域、权限模型、进程沙箱、API 限制约束。

这也是排查问题时很重要的一点。比如一个三方 App 出现 native crash:

现象:
    tombstone 里看到 SIGSEGV、libxxx.so。

结论:
    这是 native crash。

但不能直接得出:
    这是 Android 系统 native 层的问题。

更准确的判断:
    先看崩溃进程是谁,崩溃库是谁,库来自 /data/app 还是 /system/lib64、/vendor/lib64。

如果崩在 /data/app/.../lib/arm64/libxxx.so,大概率是 App 自己的 native library。

如果崩在 /system/bin/surfaceflinger/system/bin/netd/system/lib64/libbinder.so/vendor/bin/hw/...,才更可能进入系统 native、HAL 或 vendor 侧分析。

五、Framework 是什么?为什么它比 SDK 更大?

Framework 是 Android 系统向 App 和上层模块提供能力的框架层。

它不是单个 jar,也不是单个进程,更不是 SDK 的另一个名字。

从 App 开发者视角看,Framework 像一组 API:

Activity
Service
ContentProvider
BroadcastReceiver
View
Window
PackageManager
ActivityManager
PowerManager
ConnectivityManager
TelephonyManager
CameraManager

从系统开发者视角看,Framework 是一套横跨多进程、多模块的系统能力抽象:

frameworks/base/core:
    App 进程中也会加载的 Framework Java 类。

frameworks/base/services:
    system_server 中大量系统服务的实现。

Binder 接口:
    App 进程和 system_server、native service 通信的边界。

System API / hidden API:
    给系统 App、厂商、平台内部使用的非公开能力。

JNI bridge:
    Framework Java 代码进入 native 实现的桥。

所以 Framework 的核心不是“Java 层”三个字,而是:

它把底层复杂能力抽象成上层可调用的系统能力。

比如你在 App 里调用:

val pm = context.packageManager
val info = pm.getPackageInfo(packageName, 0)

表面上看只是调用一个 Java/Kotlin API,但真实链路可能是:

App 进程
  -> Framework API / proxy
  -> Binder
  -> system_server
  -> PackageManagerService
  -> settings/packages.xml、权限状态、安装状态等系统数据
  -> Binder 返回
  -> App 得到结果

这里 SDK 只解决编译时“能不能调用 getPackageInfo”的问题。

Framework 才是设备运行时真正处理 Package 信息、权限、安装状态、跨进程通信和结果返回的系统实现。

这里可以先记住这层关系:

SDK 是 Framework 对 App 开发者公开出来的 API 窗口和工具集合。
Framework 是系统里真正承载这些 API 的实现、服务和抽象层。

六、Native 是什么?为什么它最容易被说乱?

Native 是这里最容易混乱的词,因为它在不同上下文里有不同含义。

常见至少有五种:

第一种:语言层面的 native code
    指 C/C++ 这类编译成本机机器码运行的代码。

第二种:Java 语法里的 native 方法
    指 Java 方法没有 Java 实现,具体实现通过 JNI 映射到 C/C++。

第三种:App 里的 native library
    指 App 随包携带的 .so,比如 libfoo.so。

第四种:Android 系统 native 层
    指 AOSP 里的 native daemon、native service、C/C++ library,比如 init、logd、netd、SurfaceFlinger、libbinder。

第五种:产品形态里的 native App
    指相对 Web App、Hybrid App、跨平台壳而言,更贴近平台原生能力开发的 App。

这五种都可能被简称为 native,但它们不是一回事。

比如:

private native long nativeInit();

这里的 native 是 Java 方法修饰符,意思是:

这个 Java 方法的实现不在 Java 代码里,而在 native 代码里。

它通常通过 JNI 绑定到 C/C++ 函数。

再比如:

/system/bin/surfaceflinger
/system/bin/netd
/system/lib64/libbinder.so
/vendor/bin/hw/android.hardware.camera.provider-service

这里说的 native 更接近 Android 系统 native 层或 vendor native 实现。

再比如:

APK/lib/arm64-v8a/libopencv_java4.so
APK/lib/arm64-v8a/libgame.so

这里说的是 App 携带的 native library。

所以,看到 native 这个词时,必须补一句上下文:

native method?
native library?
native crash?
native service?
native layer?
native app?

没有上下文的 native,信息量其实是不够的。

七、放到一条真实调用链里看

04_runtime_call_chain.png

抽象概念说完,最好放到一条调用链里。

假设一个 App 打开相机,里面还有一段 C++ 图像处理逻辑:

App UI:
    Kotlin/Java 写 Activity、View、权限申请、预览界面。
​
SDK:
    编译时使用 Camera2、Activity、Permission 等 Android API 声明。
​
Framework:
    运行时 App 调用 CameraManager 等 Framework API。
​
Binder:
    Framework 代理把请求跨进程发给系统服务。
​
System / Native Service:
    camera service 等系统组件处理请求。
​
HAL:
    标准接口连接厂商 camera provider 实现。
​
Kernel / Driver:
    驱动和硬件完成真正的数据采集。
​
NDK:
    App 用 NDK 编译 C++ 图像算法,产出 libimage_filter.so。
​
App native library:
    App 通过 JNI 调用 libimage_filter.so 处理图像帧。

整理成一条链:

开发阶段:
    Android SDK 提供 App API、构建工具、adb 等。
    Android NDK 提供 C/C++ 编译和链接能力。

安装阶段:
    App 被打包成 APK/AAB,里面可能包含 dex、资源、manifest、.so。

运行阶段:
    App 进程加载 Framework 类和自己的 native library。
    App 通过 Framework API 请求系统能力。
    Framework 通过 Binder 进入 system_server 或 native service。
    系统服务继续访问 HAL、vendor、kernel、driver。

这条链里,每个词的位置都不一样:

SDK:
    主要站在开发和编译视角。

NDK:
    主要站在 C/C++ 构建和 App native library 视角。

APP:
    主要站在安装包、进程、权限、沙箱和业务功能视角。

Framework:
    主要站在系统能力抽象和运行实现视角。

Native:
    需要看上下文,可能是 App .so,也可能是系统服务,也可能是 JNI 方法实现。

这样就不会把“我用了 NDK”误读成“我在改 Android 系统 native 层”。

也不会把“我调用了 SDK API”误读成“SDK 在设备上帮我完成了所有事情”。

八、如何快速判断一个问题属于哪一类?

05_debug_decision_tree.png

排查问题时,可以用下面这组问题快速定位概念边界。

1. 这是开发工具问题吗?

如果你遇到的是:

compileSdk 配置
minSdk / targetSdk
Gradle 构建失败
adb 连接失败
Android SDK Platform 没安装
Build Tools 版本问题
签名、打包、aapt2、d8、R8、lint

先看 SDK、Android Studio、Gradle 和构建工具这条线。

如果问题发生在编译、打包、安装、调试工具链阶段,先看 SDK 和构建链。

2. 这是 C/C++ 构建或 ABI 问题吗?

如果你遇到的是:

CMakeLists.txt
ndkVersion
abiFilters
arm64-v8a / armeabi-v7a
undefined symbol
dlopen failed
JNI 方法找不到
libxxx.so 没被打进 APK
native debug symbols

先看 NDK、native library、ABI 和 JNI 这条线。

如果问题发生在 App 自己的 C/C++ 编译、链接、加载、JNI 绑定阶段,先看 NDK 和 App native library。

3. 这是 App 运行身份问题吗?

如果你遇到的是:

权限不够
后台限制
前台服务限制
应用数据目录
UID / packageName
AndroidManifest 声明
组件启动失败
targetSdk 行为变化

先按 App 的运行身份来分析。

如果问题和 package、UID、权限、组件、manifest、targetSdk 行为有关,先按 App 运行身份分析。

4. 这是 Framework 状态或系统服务问题吗?

如果你遇到的是:

ActivityManagerService
PackageManagerService
WindowManagerService
PowerManagerService
ConnectivityService
system_server
Binder 调用异常
系统 API 行为异常
frameworks/base 修改

先看 Framework、system_server 和 Binder 调用链。

如果问题发生在系统能力抽象、系统服务状态机、跨进程调用链里,先看 Framework 和 system_server。

5. 这是系统 native 或 HAL 问题吗?

如果你遇到的是:

surfaceflinger 崩溃
netd 异常
vold 挂掉
logd、init、servicemanager
/system/lib64/*.so
/vendor/lib64/*.so
HAL service
vendor process
tombstone 指向系统 native 进程

这时再进入 Android native layer、HAL、vendor 或 kernel 边界去查。

如果崩溃进程或核心库来自 /system、/vendor,且属于系统服务、native daemon、HAL service,再进入系统 native 分析。

九、几个最常见的混淆

混淆一:SDK 等于 Framework

不是。SDK 是开发工具包和 API 契约,Framework 是设备端系统能力的实现和抽象层。

App 编译时依赖 SDK 暴露的 API。
App 运行时调用设备里的 Framework 实现。

混淆二:NDK 等于 Native 层

不是。NDK 是 App 开发者写 C/C++ 的工具包,Android Native 层是系统分层里的 C/C++ daemon、service、library、HAL 等。

使用 NDK 可以写 App native library。
修改 Android native layer 通常是在 AOSP、vendor、HAL 或系统 native service 里工作。

混淆三:用了 C++ 的 App 就不是 App 了

不是。

App 是否是 App,主要看它是不是以 Android 应用形式安装、运行、授权和管理。

里面用 Kotlin、Java、C++、Rust 或其他语言,不改变它作为 App 的基本身份。

更合适的说法是:

这是一个包含 native library 的 App。

混淆四:native 崩溃就是 NDK 崩溃

不一定。

native 崩溃只说明崩在 native 代码路径里。它可能来自:

App 自己的 .so
三方 SDK 的 .so
系统 native library
native daemon
HAL service
vendor library

判断入口不是“有没有 SIGSEGV”,而是:

崩溃进程是谁?
崩溃线程在做什么?
backtrace 里的 so 来自哪里?
库路径是 /data/app,还是 /system、/vendor?
符号属于 App、三方库、Framework JNI、native service,还是 HAL?

混淆五:Framework 就是 Java,Native 就是 C++

太粗。

Framework 里确实有大量 Java 代码,但 Framework 也会通过 JNI 进入 native,实现中也有 native bridge 和底层服务协作。

Native 里确实有大量 C/C++,但不是所有 C/C++ 都属于系统 native 层。App 自己的 .so 也是 C/C++,但它运行在 App 进程里。

更稳妥的说法是:

Framework 是系统能力抽象层,不只是语言标签。
Native 是实现方式或系统层级标签,必须结合进程、路径、模块一起判断。

十、用一张表压住边界

名词主要维度它是什么常见位置最容易误解成
SDK工具包 / API 契约App 开发、编译、调试、打包所需工具和公开 APIAndroid SDK 目录、Android Studio、Gradle 配置Framework 本身
NDK工具包 / C/C++ 构建用 C/C++ 实现 App 部分逻辑的工具链ndk/、CMake、.so、JNIAndroid 系统 Native 层
APP交付物 / 运行身份APK/AAB、package、UID、进程、权限、组件/data/app、App 进程、manifest某种固定语言实现
Framework系统分层 / 能力抽象Android API、系统服务、Framework Java、system_server 等frameworks/basesystem_server、Manager/ServiceSDK
Native语言 / 实现 / 系统层级C/C++ 代码、native library、native service、JNI 实现等.so/system/bin/vendor/bin、JNINDK 或单纯 C++

这张表最重要的不是记住每个格子,而是记住第一列和第二列的关系:

SDK、NDK 是“怎么开发”。
APP 是“交付和运行的是什么”。
Framework、Native 是“系统运行时处在哪一层、由谁实现”。

十一、实际工作里怎么说才不容易误会?

如果你想表达“我在写 Android 应用”,可以说:

我在做 App 开发,主要使用 Android SDK 和 Kotlin/Java。

如果你想表达“我在 App 里写 C++”,可以说:

这个 App 使用 NDK 编译了一部分 native library,通过 JNI 调用。

如果你想表达“我在改系统服务”,可以说:

我在改 Framework 层的系统服务,比如 PackageManagerService / ActivityManagerService。

如果你想表达“我在改系统 C++ 服务”,可以说:

我在改 Android native service,比如 SurfaceFlinger / netd / vold。

如果你想表达“我在看硬件抽象层”,可以说:

我在看 HAL service 或 vendor 实现,它处在 Framework/native service 和 kernel driver 之间。

如果你想表达“某个 native 崩溃了”,最好补完整:

这是 App 进程里的 native crash,崩在 /data/app 下的 libxxx.so。

或者:

这是系统 native 进程崩溃,进程是 surfaceflinger,backtrace 主要在 /system/lib64 和 vendor 库之间。

这样沟通会比一句“native 有问题”清楚得多。

十二、延伸阅读

总结:最终答案是什么?

这篇文章的核心问题是:

NDK、SDK、APP、Framework、Native 到底怎么区分?

最后可以收成三层意思。

第一,SDK 和 NDK 是工具包,不是系统层级。

SDK 面向 Android App 的普通开发、编译、调试和打包。
NDK 面向 App 中 C/C++ native library 的编译、链接和调试。

第二,APP 是交付和运行单位,不代表固定语言。

一个 App 可以只用 Kotlin/Java,也可以包含 C/C++ .so。
用了 NDK 的 App 仍然是 App,只是多了一部分 native code

第三,Framework 和 Native 是系统运行时的分层和实现语境。

Framework 负责把系统能力抽象成上层 API 和系统服务。
Native 需要看上下文,可能是 App .so、JNI 实现、系统 native service、native library、HAL 或 vendor 代码。

所以真正的区分方法不是死记名词,而是每次先问:

这是工具包,还是运行层?
这是交付物,还是实现代码?
这是 App 自己的 native,还是 Android 系统 native?
这是编译时看到的 SDK API,还是设备运行时的 Framework 实现?

只要这几个问题问对,NDK、SDK、APP、Framework、Native 的边界就会非常清楚。