【Android】从一个AndroidRuntime看类的加载
app中如果存在一个和系统中同名的jar包,在执行的时候会使用系统的jar,下面的crash就是在系统的jar中没有找到相应的方法,出现的AndroidRuntime异常
01-14 16:23:48.484 3969 3969 E AndroidRuntime: Process: com.example.testapp, PID: 3969
01-14 16:23:48.484 3969 3969 E AndroidRuntime: java.lang.NoSuchMethodError: No interface method setData(ILandroid/os/ParcelFileDescriptor;)I in class Lcom/test/ITestManager; or its super classes (declaration of 'com.test.ITestManager' appears in /system/framework/miscmdm.jar)
在 Android 系统中,“系统中的 JAR 覆盖 App 中的同名 JAR” 这种说法并不完全准确,需要从 Android 的类加载机制、系统类库和应用类路径的设计来理解其背后的原理。
简单来说:不是“覆盖”,而是系统类优先加载,应用无法重新定义系统已提供的类。
一、Android 的类加载机制
Android 使用的是 DexClassLoader
(或其父类 BaseDexClassLoader
)来加载 .dex
文件或 .apk
中的类。它遵循 双亲委派模型(Parent Delegation Model),但略有不同。
类加载顺序大致如下:
- BootClassLoader(系统引导类加载器)
- 加载系统核心类库(如
android.jar
,core-oj.jar
等) - 这些类在
/system/framework/
目录下
- 加载系统核心类库(如
- PathClassLoader / DexClassLoader
- 加载应用自身的
.dex
文件(即你的 APK 或依赖的库)
- 加载应用自身的
当应用尝试加载一个类时,会先委派给父加载器(系统加载器)去查找。如果系统已经提供了这个类,就不会再加载应用中的同名类。
二、为什么“系统 JAR 优先”?
1. 系统预加载核心类库
Android 系统在启动时会预加载一系列核心 JAR 包,例如:
/system/framework/framework.jar
/system/framework/core-oj.jar
/system/framework/telephony-common.jar
- 以及 OEM 厂商添加的私有 JAR
这些 JAR 包包含了 Android SDK 的实现类(不仅仅是接口),比如 Activity
, Context
, TelephonyManager
等。
2. 安全与稳定性
如果允许应用随意“替换”系统类(比如自己打包一个修改版的 Activity
类),就会导致:
- 安全漏洞(可劫持系统行为)
- 兼容性问题(不同应用使用不同版本的“系统类”)
- 系统崩溃风险
因此,系统类是“只读”且“不可覆盖”的。
三、具体例子说明
假设你有一个 JAR 文件,里面包含一个类:
package android.app;
public class Activity {// 你自定义的逻辑
}
你把这个 JAR 打包进你的 APK。
结果:
- ✅ 编译时可能通过(因为编译用的是
android.jar
,只包含方法声明) - ❌ 运行时:系统会使用
/system/framework/framework.jar
中真正的android.app.Activity
,你打包的这个类会被忽略 - ⚠️ 严重时可能导致
ClassNotFoundException
或VerifyError
,因为类结构不一致
四、例外情况:非系统包名的第三方库
如果你使用的是第三方库(如 okhttp
, gson
, rxjava
),它们的包名是 com.squareup.okhttp3
、com.google.gson
等,不属于 android.*
、java.*
、javax.*
等系统包名空间,那么:
- 系统没有预定义这些类
- 应用可以自由打包和加载这些 JAR
- 不会发生“覆盖”问题
但如果系统 ROM 厂商在 /system/framework/
中预装了某个第三方库,而你的应用也打包了不同版本的 gson
,就可能出现 类加载冲突或版本不一致问题。
这种情况较少见,但某些定制 ROM 确实会这么做。
五、如何避免问题?
不要尝试替换系统类
- 不要打包
android.*
、java.*
等命名空间下的类 - 不要试图“修复”或“增强”系统类
- 不要打包
使用 SDK 兼容方式开发
- 使用 Support Library 或 AndroidX
- 避免反射调用隐藏 API(除非你清楚后果)
管理第三方库版本
- 使用 Gradle 正确管理依赖
- 避免重复打包相同的库
- 使用
provided
或compileOnly
避免打包系统已提供的库
系统应用(System App)例外
- 如果你的 APK 被烧录到
/system/priv-app/
,并且签名与系统一致 - 可能可以替换或扩展系统 JAR(需系统支持)
- 普通应用无法做到
- 如果你的 APK 被烧录到
总结
问题 | 解释 |
---|---|
“系统 JAR 覆盖 App JAR”? | ❌ 不准确 |
实际机制是什么? | ✅ 系统类加载器优先加载系统类,应用无法重新定义系统类 |
为什么? | 安全、稳定、兼容性 |
能否替换系统类? | ❌ 普通应用不能 |
第三方库会冲突吗? | ⚠️ 一般不会,除非系统预装了同名库 |
✅ 结论: Android 中并不存在“系统 JAR 覆盖 App JAR”的文件级覆盖,而是 类加载机制决定了系统类优先于应用类加载。应用不能重新定义 android.*
等系统命名空间下的类,这是 Android 安全架构的一部分。