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
相关文章
|
编解码 安全 Android开发
如何修复 Android 和 Windows 不支持视频编解码器的问题?
视频播放时遇到“编解码器不支持”错误(如0xc00d36c4或0xc00d5212)是常见问题,即使文件格式为MP4或MKV。编解码器是编码和解码数据的工具,不同设备和版本支持不同的编解码器。解决方法包括:1) 安装所需编解码器,如K-Lite Codec Pack;2) 使用自带编解码器的第三方播放器,如VLC、KMPlayer等。这些方法能帮助你顺利播放视频。
|
10月前
|
关系型数据库 MySQL Java
字节面试: MySQL 百万级 导入发生的 “死锁” 难题如何解决?“2序4拆”,彻底攻克
字节面试: MySQL 百万级 导入发生的 “死锁” 难题如何解决?“2序4拆”,彻底攻克
字节面试: MySQL 百万级 导入发生的 “死锁” 难题如何解决?“2序4拆”,彻底攻克
|
11月前
|
SQL Java 数据库连接
阿里腾讯互联网公司校招 Java 面试题总结及答案解析
本文总结了阿里巴巴和腾讯等互联网大厂的Java校招面试题及答案,涵盖Java基础、多线程、集合框架、数据库、Spring与MyBatis框架等内容。从数据类型、面向对象特性到异常处理,从线程安全到SQL优化,再到IOC原理与MyBatis结果封装,全面梳理常见考点。通过详细解析,帮助求职者系统掌握Java核心知识,为校招做好充分准备。资源链接:[点击下载](https://pan.quark.cn/s/14fcf913bae6)。
487 2
|
人工智能 自然语言处理 架构师
字节面试: es怎么提升性能和精准度?(尼恩独家,史上最全)
本文由40岁老架构师尼恩撰写,针对ES(Elasticsearch)提升搜索性能和精准度的面试题进行详细解析。文章首先指出,提升ES速度和精准度是两个独立的问题,分别涉及性能优化和精准度优化。这些内容不仅有助于应对面试中的难题,还能帮助开发者在实际项目中构建更高效的搜索系统。尼恩强调,掌握这些知识后可以在面试中“吊打”面试官,轻松获得理想Offer。同时,他还提供了《尼恩Java面试宝典PDF》等资源供读者学习参考。
|
Arthas Kubernetes Java
字节面试:CPU被打满了,CPU100%,如何处理?
尼恩,一位拥有20多年经验的老架构师,针对近期读者在一线互联网企业面试中遇到的CPU 100%和红包架构等问题,进行了系统化梳理。文章详细解析了CPU 100%的三大类型问题(业务类、并发类、内存类)及其九种常见场景,提供了使用jstack和arthas两大工具定位问题的具体步骤,并分享了解决死锁问题的实战案例。尼恩还强调了面试时应先考虑回滚版本,再使用工具定位问题的重要性。此外,尼恩提供了丰富的技术资料,如《尼恩Java面试宝典》等,帮助读者提升技术水平,轻松应对面试挑战。
字节面试:CPU被打满了,CPU100%,如何处理?
|
算法 前端开发 Java
数据结构与算法学习四:单链表面试题,新浪、腾讯【有难度】、百度面试题
这篇文章总结了单链表的常见面试题,并提供了详细的问题分析、思路分析以及Java代码实现,包括求单链表中有效节点的个数、查找单链表中的倒数第k个节点、单链表的反转以及从尾到头打印单链表等题目。
235 1
数据结构与算法学习四:单链表面试题,新浪、腾讯【有难度】、百度面试题
|
NoSQL 中间件 Java
字节面试:聊聊 CAP 定理?哪些中间件是AP? 哪些是CP? 说说 为什么?
45岁老架构师尼恩在其读者交流群中分享了关于CAP定理的重要面试题及其解析,包括CAP定理的基本概念、CAP三要素之间的关系,以及如何在分布式系统设计中权衡一致性和可用性。文章还详细分析了几种常见中间件(如Redis Cluster、Zookeeper、MongoDB、Cassandra、Eureka、Nacos)的CAP特性,并提供了高端面试技巧,帮助读者在面试中脱颖而出。尼恩还推荐了其团队编写的《尼恩Java面试宝典PDF》等资料,助力求职者准备面试,提升技术水平。
|
Java 程序员 开发工具
Android|修复阿里云播放器下载不回调的问题
虽然 GC 带来了很多便利,但在实际编码时,我们也需要注意对象的生命周期管理,该存活的存活,该释放的释放,避免因为 GC 导致的问题。
233 2
|
Java API 对象存储
JVM进阶调优系列(2)字节面试:JVM内存区域怎么划分,分别有什么用?
本文详细解析了JVM类加载过程的关键步骤,包括加载验证、准备、解析和初始化等阶段,并介绍了元数据区、程序计数器、虚拟机栈、堆内存及本地方法栈的作用。通过本文,读者可以深入了解JVM的工作原理,理解类加载器的类型及其机制,并掌握类加载过程中各阶段的具体操作。
|
负载均衡 算法 Java
腾讯面试:说说6大Nginx负载均衡?手写一下权重轮询策略?
尼恩,一位资深架构师,分享了关于负载均衡及其策略的深入解析,特别是基于权重的负载均衡策略。文章不仅介绍了Nginx的五大负载均衡策略,如轮询、加权轮询、IP哈希、最少连接数等,还提供了手写加权轮询算法的Java实现示例。通过这些内容,尼恩帮助读者系统化理解负载均衡技术,提升面试竞争力,实现技术上的“肌肉展示”。此外,他还提供了丰富的技术资料和面试指导,助力求职者在大厂面试中脱颖而出。
腾讯面试:说说6大Nginx负载均衡?手写一下权重轮询策略?

热门文章

最新文章