Android热补丁动态修复实践,腾讯&字节&网易&华为Android面试题分享

简介: Android热补丁动态修复实践,腾讯&字节&网易&华为Android面试题分享

上一小节创建的module提供相应的方法来让我们对项目的类进行代码注入,我们需要在build.gradle来配置让它自动来做这件事:

apply plugin: ‘com.android.application’
task(‘processWithJavassist’) << {
String classPath = file(‘build/intermediates/classes/debug’)// 项目编译class所在目录
com.devilwwj.patch.PatchClass.process(classPath, project(‘:hackdex’).buildDir.absolutePath + “/intermediates/classes/debug”) // 第二个参数是hackdex的class所在目录
}
android {
compileSdkVersion 23
buildToolsVersion “23.0.1”
defaultConfig {
applicationId “com.devilwwj.hotfixdemo”
minSdkVersion 14
targetSdkVersion 23
versionCode 1
versionName “1.0”
}
buildTypes {
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’
}
}
applicationVariants.all { variant ->
variant.dex.dependsOn << processWithJavassist // 在执行dx命令之前将代码打入到class中
}
}
dependencies {
compile fileTree(dir: ‘libs’, include: [‘*.jar’])
testCompile ‘junit:junit:4.12’
compile ‘com.android.support:appcompat-v7:23.1.1’
compile ‘com.android.support:design:23.1.1’
compile project(‘:hotfixlib’)
}
这时候我们run项目,反编译build/output/apk下的app-debug.apk文件,你就可以看到代码已经成功植入了。
mac下的反编译工具:
https://sourceforge.net/projects/jadx/?source=typ_redirect
反编译的结果如下图:
其实你也可以直接在项目中看:
创建hotfixlib模块,并关联到项目中
这差不多是最后一步了,也是最核心的一步,提供将heck_dex.jar动态插入到dexElements的方法。
核心代码:
package com.devilwwj.hotfixlib;
import android.annotation.TargetApi;
import android.content.Context;
import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import dalvik.system.DexClassLoader;
import dalvik.system.PathClassLoader;
/**
• com.devilwwj.hotfixlib
• Created by devilwwj on 16/3/9.
*/
public final class HotFix {
public static void patch(Context context, String patchDexFile, String patchClassName) {
if (patchDexFile != null && new File(patchDexFile).exists()) {
try {
if (hasLexClassLoader()) {
injectAliyunOs(context, patchDexFile, patchClassName);
} else if (hasDexClassLoader()) {
injectAboveEqualApiLevel14(context, patchDexFile, patchClassName);
} else {
injectBelowApiLevel14(context, patchDexFile, patchClassName);
}
} catch (Throwable th) {
}
}
}
private static boolean hasLexClassLoader() {
try {
Class.forName(“dalvik.system.LexClassLoader”);
return true;
} catch (ClassNotFoundException e) {
e.printStackTrace();
return false;
}
}
private static boolean hasDexClassLoader() {
try {
Class.forName(“dalvik.system.BaseDexClassLoader”);
return true;
} catch (ClassNotFoundException e) {
e.printStackTrace();
return false;
}
}
private static void injectAliyunOs(Context context, String patchDexFile, String patchClassName) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
PathClassLoader obj = (PathClassLoader) context.getClassLoader();
String replaceAll = new File(patchDexFile).getName().replaceAll(“\.[a-zA-Z0-9]+”, “.lex”);
Class cls = Class.forName(“dalvik.system.LexClassLoader”);
Object newInstance = cls.getConstructor(new Class[]{String.class, String.class, String.class, ClassLoader.class}).newInstance(
new Object[]{context.getDir(“dex”, 0).getAbsolutePath()
• File.separator + replaceAll, context.getDir(“dex”, 0).getAbsolutePath(), patchDexFile, obj});
cls.getMethod(“loadClass”, new Class[]{String.class}).invoke(newInstance, new Object[]{patchClassName});
setField(obj, PathClassLoader.class, “mPaths”, appendArray(getField(obj, PathClassLoader.class, “mPaths”), getField(newInstance, cls, “mRawDexPath”)));
setField(obj, PathClassLoader.class, “mFiles”, combineArray(getField(obj, PathClassLoader.class, “mFiles”), getField(newInstance, cls, “mFiles”)));
setField(obj, PathClassLoader.class, “mZips”, combineArray(getField(obj, PathClassLoader.class, “mZips”), getField(newInstance, cls, “mZips”)));
setField(obj, PathClassLoader.class, “mLexs”, combineArray(getField(obj, PathClassLoader.class, “mLexs”), getField(newInstance, cls, “mDexs”)));
}
@TargetApi(14)
private static void injectBelowApiLevel14(Context context, String str, String str2) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
PathClassLoader obj = (PathClassLoader) context.getClassLoader();
DexClassLoader dexClassLoader = new DexClassLoader(str, context.getDir(“dex”, 0).getAbsolutePath(), str, context.getClassLoader());
dexClassLoader.loadClass(str2);
setField(obj, PathClassLoader.class, “mPaths”,
appendArray(getField(obj, PathClassLoader.class, “mPaths”), getField(dexClassLoader, DexClassLoader.class, “mRawDexPath”)));
setField(obj, PathClassLoader.class, “mFiles”,
combineArray(getField(obj, PathClassLoader.class, “mFiles”), getField(dexClassLoader, DexClassLoader.class, “mFiles”)));
setField(obj, PathClassLoader.class, “mZips”,
combineArray(getField(obj, PathClassLoader.class, “mZips”), getField(dexClassLoader, DexClassLoader.class, “mZips”)));
setField(obj, PathClassLoader.class, “mDexs”,
combineArray(getField(obj, PathClassLoader.class, “mDexs”), getField(dexClassLoader, DexClassLoader.class, “mDexs”)));
obj.loadClass(str2);
}
/**
• 将dex注入dexElements数组中
• @param context
• @param str
• @param str2
• @throws ClassNotFoundException
• @throws NoSuchFieldException
• @throws IllegalAccessException
*/
private static void injectAboveEqualApiLevel14(Context context, String str, String str2) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
PathClassLoader pathClassLoader = (PathClassLoader) context.getClassLoader();
Object a = combineArray(getDexElements(getPathList(pathClassLoader)), getDexElements(getPathList(new DexClassLoader(str, context.getDir(“dex”, 0).getAbsolutePath(), str, context.getClassLoader()))));
Object a2 = getPathList(pathClassLoader);
setField(a2, a2.getClass(), “dexElements”, a);
pathClassLoader.loadClass(str2);
}
/**
• 通过PathClassLoader拿到pathList
• @param obj
• @return
• @throws ClassNotFoundException
• @throws NoSuchFieldException
• @throws IllegalAccessException
*/
private static Object getPathList(Object obj) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
return getField(obj, Class.forName(“dalvik.system.BaseDexClassLoader”), “pathList”);
}
/**
• 通过pathList取得dexElements对象
• @param obj
• @return
• @throws NoSuchFieldException
• @throws IllegalAccessException
*/
private static Object getDexElements(Object obj) throws NoSuchFieldException, IllegalAccessException {
return getField(obj, obj.getClass(), “dexElements”);
}
/**
• 通过反射拿到指定对象
• @param obj
• @param cls
• @param str
• @return
• @throws NoSuchFieldException
• @throws IllegalAccessException
*/
private static Object getField(Object obj, Class cls, String str) throws NoSuchFieldException, IllegalAccessException {
Field declaredField = cls.getDeclaredField(str);
declaredField.setAccessible(true);
return declaredField.get(obj);
}
/**
• 通过反射设置属性
• @param obj
• @param cls
• @param str
• @param obj2
• @throws NoSuchFieldException
• @throws IllegalAccessException
*/
private static void setField(Object obj, Class cls, String str, Object obj2) throws NoSuchFieldException, IllegalAccessException {
Field declaredField = cls.getDeclaredField(str);
declaredField.setAccessible(true);
declaredField.set(obj, obj2);
}
/**
• 合并数组
• @param obj
• @param obj2
• @return
*/
private static Object combineArray(Object obj, Object obj2) {
Class componentType = obj2.getClass().getComponentType();
int length = Array.getLength(obj2);
int length2 = Array.getLength(obj) + length;
Object newInstance = Array.newInstance(componentType, length2);
for (int i = 0; i < length2; i++) {
if (i < length) {
Array.set(newInstance, i, Array.get(obj2, i));
} else {
Array.set(newInstance, i, Array.get(obj, i - length));
}
}
return newInstance;
}
/**
• 添加到数组
• @param obj
• @param obj2
相关文章
|
18天前
|
Android开发 Kotlin
Android经典面试题之Kotlin的==和===有什么区别?
本文介绍了 Kotlin 中 `==` 和 `===` 操作符的区别:`==` 用于比较值是否相等,而 `===` 用于检查对象身份。对于基本类型,两者行为相似;对于对象引用,`==` 比较值相等性,`===` 检查引用是否指向同一实例。此外,还列举了其他常用比较操作符及其应用场景。
171 93
|
21天前
|
存储 缓存 编解码
Android经典面试题之图片Bitmap怎么做优化
本文介绍了图片相关的内存优化方法,包括分辨率适配、图片压缩与缓存。文中详细讲解了如何根据不同分辨率放置图片资源,避免图片拉伸变形;并通过示例代码展示了使用`BitmapFactory.Options`进行图片压缩的具体步骤。此外,还介绍了Glide等第三方库如何利用LRU算法实现高效图片缓存。
40 20
Android经典面试题之图片Bitmap怎么做优化
|
9天前
|
安全 Android开发 数据安全/隐私保护
探索安卓与iOS的安全性差异:技术深度分析与实践建议
本文旨在深入探讨并比较Android和iOS两大移动操作系统在安全性方面的不同之处。通过详细的技术分析,揭示两者在架构设计、权限管理、应用生态及更新机制等方面的安全特性。同时,针对这些差异提出针对性的实践建议,旨在为开发者和用户提供增强移动设备安全性的参考。
|
9天前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
讲解Activity的启动流程了,Activity的启动流程相对复杂一下,涉及到了Activity中的生命周期方法,涉及到了Android体系的CS模式,涉及到了Android中进程通讯Binder机制等等, 首先介绍一下Activity,这里引用一下Android guide中对Activity的介绍:
25 4
|
16天前
|
缓存 Android开发 开发者
Android RecycleView 深度解析与面试题梳理
本文详细介绍了Android开发中高效且功能强大的`RecyclerView`,包括其架构概览、工作流程及滑动优化机制,并解析了常见的面试题。通过理解`RecyclerView`的核心组件及其优化技巧,帮助开发者提升应用性能并应对技术面试。
40 8
|
16天前
|
存储 缓存 Android开发
Android RecyclerView 缓存机制深度解析与面试题
本文首发于公众号“AntDream”,详细解析了 `RecyclerView` 的缓存机制,包括多级缓存的原理与流程,并提供了常见面试题及答案。通过本文,你将深入了解 `RecyclerView` 的高性能秘诀,提升列表和网格的开发技能。
39 8
|
13天前
|
ARouter 测试技术 API
Android经典面试题之组件化原理、优缺点、实现方法?
本文介绍了组件化在Android开发中的应用,详细阐述了其原理、优缺点及实现方式,包括模块化、接口编程、依赖注入、路由机制等内容,并提供了具体代码示例。
30 2
|
9天前
|
Android开发 开发者
Android面试之Activity启动流程简述
每个Android开发者都熟悉的Activity,但你是否了解它的启动流程呢?本文将带你深入了解。启动流程涉及四个关键角色:Launcher进程、SystemServer的AMS、应用程序的ActivityThread及Zygote进程。核心在于AMS与ActivityThread间的通信。文章详细解析了从Launcher启动Activity的过程,包括通过AIDL获取AMS、Zygote进程启动以及ActivityThread与AMS的通信机制。接着介绍了如何创建Application及Activity的具体步骤。整体流程清晰明了,帮助你更深入理解Activity的工作原理。
16 0
|
存储 消息中间件 算法
补:《Android面试题思考与解答》2021年3月刊(四)
回来啦,《Android面试题思考与解答21年3月刊》送给大家。
196 0
补:《Android面试题思考与解答》2021年3月刊(四)
|
存储 设计模式 缓存
补:《Android面试题思考与解答》2021年3月刊(二)
回来啦,《Android面试题思考与解答21年3月刊》送给大家。
114 0
补:《Android面试题思考与解答》2021年3月刊(二)
下一篇
无影云桌面