Android 热修复技术:Andfix 框架原理与源码分析
Android 热修复是一种在不重新发布应用的情况下修复线上 Bug 的技术方案。常见的实现思路包括 Dex 分包方案和 Native 方法替换。本文将深入分析基于 Native 方法的 Andfix 框架,涵盖其使用方式、核心源码逻辑及补丁生成流程。
Android 热修复通过 Andfix 框架利用 Native 层替换方法字节码实现。流程包括 PatchManager 加载补丁、签名校验、DexFile 加载修复类、反射获取 Method 对象及注解,最终调用 Native 接口替换目标方法结构体。支持 Dalvik 和 ART 虚拟机,需保证新旧方法签名一致且应用签名匹配。使用时需注意方法签名一致性、签名校验、类加载器隔离及系统兼容性限制。

Android 热修复是一种在不重新发布应用的情况下修复线上 Bug 的技术方案。常见的实现思路包括 Dex 分包方案和 Native 方法替换。本文将深入分析基于 Native 方法的 Andfix 框架,涵盖其使用方式、核心源码逻辑及补丁生成流程。
在应用中集成 Andfix 主要涉及 PatchManager 的初始化和补丁加载。以下是一个典型的应用入口代码示例:
public class MainApplication extends Application {
private static final String TAG = "euler";
private static final String APATCH_PATH = "/out.apatch";
private PatchManager mPatchManager;
@Override
public void onCreate() {
super.onCreate();
// 初始化 PatchManager
mPatchManager = new PatchManager(this);
mPatchManager.init("1.0");
Log.d(TAG, "inited.");
// 加载本地已存在的补丁
mPatchManager.loadPatch();
Log.d(TAG, "apatch loaded.");
// 运行时动态添加补丁(例如从服务器下载后)
try {
String patchFileString = Environment.getExternalStorageDirectory()
.getAbsolutePath() + APATCH_PATH;
mPatchManager.addPatch(patchFileString);
Log.d(TAG, "apatch added: " + patchFileString);
} catch (IOException e) {
Log.e(TAG, "", e);
}
}
}
PatchManager 负责管理多个补丁文件。其构造函数中会初始化 AndFixManager、补丁目录以及相关的集合容器。
public PatchManager(Context context) {
mContext = context;
mAndFixManager = new AndFixManager(mContext);
mPatchDir = new File(mContext.getFilesDir(), DIR);
mPatchs = new ConcurrentSkipListSet<Patch>();
mLoaders = new ConcurrentHashMap<String, ClassLoader>();
}
AndFixManager 是核心管理类,主要负责环境检查和签名校验。
public AndFixManager(Context context) {
mContext = context;
mSupport = Compat.isSupport();
if (mSupport) {
mSecurityChecker = new SecurityChecker(mContext);
mOptDir = new File(mContext.getFilesDir(), DIR);
if (!mOptDir.exists() && !mOptDir.mkdirs()) {
mSupport = false;
Log.e(TAG, "opt dir create error.");
}
}
}
isSupport() 方法决定了当前设备是否支持 Andfix,主要包含三个条件:
public static synchronized boolean isSupport() {
if (isChecked) return isSupport;
isChecked = true;
// 判断非 YunOs 且 Setup 成功且 SDK 版本符合
if (!isYunOS() && AndFix.setup() && isSupportSDKVersion()) {
isSupport = true;
}
if (inBlackList()) {
isSupport = false;
}
return isSupport;
}
为了防止补丁被篡改,Andfix 会对补丁包进行签名验证。SecurityChecker 获取当前应用的证书信息,并与补丁包中的签名进行比对。
private void init(Context context) {
PackageManager pm = context.getPackageManager();
PackageInfo packageInfo = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(
new ByteArrayInputStream(packageInfo.signatures[0].toByteArray()));
mPublicKey = cert.getPublicKey();
}
PatchManager 通过读取 .apatch 文件(本质是 Jar 包)来解析需要修复的类列表。
private void init() throws IOException {
JarFile jarFile = new JarFile(mFile);
Manifest manifest = new Manifest(jarFile.getInputStream(jarFile.getJarEntry("META-INF/PATCH.MF")));
Attributes main = manifest.getMainAttributes();
// 解析 PATCH-Classes 属性,获取待修复类名列表
for (Iterator<?> it = main.keySet().iterator(); it.hasNext();) {
Attributes.Name attrName = (Attributes.Name) it.next();
if (attrName.toString().endsWith("-Classes")) {
List<String> strings = Arrays.asList(main.getValue(attrName).split(","));
mClassesMap.put(name.trim().substring(0, name.length() - 8), strings);
}
}
}
修复的核心在于 fixClass 和 replaceMethod。流程如下:
ClassLoader 加载补丁中的 Dex 文件。@MethodReplace 注解的方法。private void fixClass(Class<?> clazz, ClassLoader classLoader) {
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
MethodReplace annotation = method.getAnnotation(MethodReplace.class);
if (annotation != null) {
replaceMethod(classLoader, annotation.clazz(), annotation.method(), method);
}
}
}
由于 Android 4.4 (ART) 前后虚拟机机制不同,Native 层分别实现了 dalvik_replaceMethod 和 art_replaceMethod。
以 Dalvik 为例,它通过 JNI 获取 Java 层的 Method 对象对应的 C++ 结构体 Method*,然后直接覆盖指令集 (insns) 和其他关键属性。
extern void dalvik_replaceMethod(JNIEnv* env, jobject src, jobject dest) {
// 获取目标类和方法的结构体
ClassObject* clz = (ClassObject*) dvmDecodeIndirectRef_fnPtr(dvmThreadSelf_fnPtr(), clazz);
Method* meth = (Method*) env->FromReflectedMethod(src);
Method* target = (Method*) env->FromReflectedMethod(dest);
// 替换关键属性
meth->methodIndex = target->methodIndex;
meth->prototype = target->prototype;
meth->insns = target->insns;
meth->nativeFunc = target->nativeFunc;
}
Andfix 提供了命令行工具 apkpatch.jar 用于生成补丁包。该工具对比 Release 包和 Fix 包,生成差异化的 .apatch 文件。
java -jar apkpatch.jar -f app-release-fix.apk -t app-release-online.apk -o ./output -k my.keystore -p password -a alias -e storepass
生成的 .apatch 文件内部包含 classes.dex(差异代码)和 META-INF/PATCH.MF(元数据信息)。
在使用 Andfix 时,需注意以下几点以确保稳定性:
综上所述,Andfix 通过底层字节码替换实现了高效的热修复能力,理解其源码有助于在实际项目中更好地集成和维护。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online