Arreglo caliente
En la actualidad, se ha desarrollado la tecnología doméstica de reparación en caliente de Android. Se puede decir que florecen cien flores. Desde el método de implementación, se puede clasificar aproximadamente en:
- Implementación de capa nativa
- Implementación de la capa de Java
Alguien ha analizado brevemente el Andfix
principio de implementación del código abierto de Alibaba (basado en la capa nativa) antes, así que no hablaré más sobre esto aquí. Puedes buscarlo.
Este artículo analiza brevemente la capa de Java para implementar la lógica de reparación en caliente, y simplemente implementa el código de demostración de reparación en caliente, tomando a Tinker como ejemplo (por supuesto, Tinker
es compatible 代码修复,资源修复,so修复
, y los amigos interesados se trasladan al sitio web oficial por sí mismos ~)
Primero clasifique las ideas:
Proceso de compilación de la clase Java
Es el proceso de compilar la clase java en un archivo .class a través de javac, y luego compilarlo en un archivo .dex mediante dx.bat, sin entrar en detalles, simplemente dibuje una imagen ~
Introducción a ClassLoader
La clase java.lang.ClassLoader en Android también es diferente de la clase java.lang.ClassLoader en Java. El tipo ClassLoader en Android también se puede dividir en ClassLoader del sistema y ClassLoader personalizado. El sistema ClassLoader incluye 3 tipos:
BootClassLoader
, El sistema Android usará BootClassLoader para precargar las clases de uso común al iniciarse. A diferencia de Bootstrap ClassLoader en Java, no se implementa mediante código C / C ++, sino mediante Java. BootClassLoader es una clase interna de ClassLoader.PathClassLoader
, El nombre completo es dalvik / system.PathClassLoader, puede cargar el Apk instalado, que es el archivo apk en / data / app / package, o puede cargar nativeLibrary en / vendor / lib, / system / lib.DexClassLoader
, El nombre completo es dalvik / system.DexClassLoader, puede cargar un archivo apk desinstalado. PathClassLoader y DexClasLoader se heredan de dalviksystem.BaseDexClassLoader, y su lógica de carga de clases está escrita en BaseDexClassLoader. La siguiente figura muestra el sistema de herencia en ClassLoader en Android. Entre ellos, SecureClassLoader y UrlClassLoader son cargadores de clases en Java y no se pueden usar en Android.
carga de archivo .dex
Se sabe por el código fuente que el .dex
archivo se BaseDexClassLoader类(ClassLoader的子类)
carga a través de, hay una variable miembro en esta clase DexPathList对象
y hay una matriz en este objeto que almacena el DexElement
objeto, es decir, el archivo cargado desde el .dex
archivo, el punto de entrada es aquí
Para los proyectos, se subcontratará el proyecto general (cuando el número de métodos sea superior a 64k y cuando el número de métodos sea superior a 65535, la estrategia de subcontratación proporcionada por Google), si se utiliza código Java para implementar la reparación en caliente, subcontratación debe hacerse, porque es necesario asegurarse de que el paquete principal no tenga errores, y el subpaquete simplemente significa que el apk empaquetado generalmente tiene varios .dex
archivos
Tales como: classes.dex, classes2.dex, etc.
Entonces, por ejemplo classes2.dex
, si un método de una de nuestras clases es anormal, podemos crear un paquete de reparación ( classes2.dex
archivo reparado ) y luego classes2.dex
copiar el archivo reparado al directorio privado a través de un cargador de clases personalizado , y luego saltar a la cola 系统ClassLoader
de dexPathList对象
la dexElement
matriz, de modo que la prioridad del sistema cargue 修复后的classes2.dex
el archivo, para lograr el propósito de las correcciones urgentes, esta implementación debe realizar una lógica de reparación para reiniciar la aplicación para lograr el efecto ~
Después de comprender esta información, la idea general está ahí. Necesitamos cargar y analizar el archivo .dex reparado, y luego saltar el archivo .dex de instalación y empaquetado anterior para realizar la operación de salto, lo que equivale a engañar al sistema Android, que es aproximadamente como sigue:
Principio de implementación
Pensando en ello, necesitamos corregir un error del archivo .dex, saltar la cola BaseDexClassLoader类
en DexPathList对象
la DexElement
matriz y ordenar al frente, de modo que el sistema se cargue en el archivo .dex después de que arreglemos el error no tendrá que cargar archivos dex, Complete Jump in the queue (instrumentación), habrá un conocimiento del mecanismo de carga de clases. Este artículo no lo presentará en detalle, y escribiré un resumen más adelante ~ Los pasos generales de implementación son los siguientes:
Implementación de demostración
1. Configuración básica: configuración del paquete principal
Configurar la subcontratación. El propósito de configurar la subcontratación es principalmente empaquetar el apk que tendrá varios archivos .dex. En la aplicación del proyecto real, asegúrese de que el paquete principal no tenga errores. Al cargar el archivo .dex en la demostración, el también se excluye archivo de paquete. classes.dex
, de la siguiente manera: Crear BaseApplication
, BaseActivity
, MainActivity
colocadas en la bolsa principal, que MainActivity
es principalmente sub huella, solamente un sub-haz click para saltar en SecondActivity
la aplicación directorio lógico build.gradle
abierta sub-soporte, el android
→ defaultConfig
configuración de aumento, en el que multiDex-config.txt
se arreglado Mantenga los archivos de clase en el paquete principal
//开启分包
multiDexEnabled true
//分包的配置,将配置文件中的放置在主包
multiDexKeepFile file("multiDex-config.txt")
Agregue dependencias de subpaquetes:
//multidex分包依赖
implementation 'com.android.support:multidex:1.0.3'
Aplicación abre subcontratación:
public class BaseApplication extends MultiDexApplication {
@Override
public void onCreate() {
super.onCreate();
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
//安装分包配置
MultiDex.install(this);
}
}
2. Configuración de subcontratación
La subcontratación crea una SecondActivity类
entrada para simular anomalías y reparar anomalías, y una Calculate
anomalía simulada, 10/0
las operaciones realizadas, una vez finalizada la reparación.10/1
Nota: El
classes2.dex
archivo reparado se puede obtener descomprimiéndolo directamente por buildapk, odx.bat
ejecutando el comando en build-tools
Simplemente pegue el código después de SecondActivity
hacer clic en el fix
botón:
private void update() {
//将下载的修复包,复制到私有目录,解压从.dex文件中取到对应的.class文件
//从sd卡取修复包
File sourceFile = new File(Environment.getExternalStorageDirectory(), Constants.DEX_NAME);
//目标文件
File targetFile = new File(getDir(Constants.DEX_DIR, Context.MODE_PRIVATE).getAbsolutePath() + File.separator + Constants.DEX_NAME);
if (targetFile.exists()) {
targetFile.delete();
Log.e("update","删除原有dex文件(已使用的)");
}
//将SD卡中的修复包copy到私有目录
FileUtils.copyFile(sourceFile,targetFile);
Log.e("update","copy完成");
FixDexUtils.loadDexFile(this);
}
3 、 FixModule
Cree un nuevo módulo para manejar la lógica relacionada de reparación en caliente
Solo hay cinco archivos, el código del archivo principal está allí FixDexUtils
, los otros son clases de herramientas y hay FixDexUtils
un código que define varias constantes .
public class FixDexUtils {
//修复文件可能有多个
private static HashSet<File> loadedDex = new HashSet<>();
//不建议这么写,demo演示用
static {
loadedDex.clear();
}
public static void loadDexFile(Context context) {
//获取私有目录
File fileDir = context.getDir(Constants.DEX_DIR, Context.MODE_PRIVATE);
//遍历筛选私有目录中的.dex文件
File[] listFiles = fileDir.listFiles();
for (int i = 0; i < listFiles.length; i++) {
//文件名以.dex结尾,且不是主包.dex文件
if (listFiles[i].getName().endsWith(Constants.DEX_SUFFIX) && !"classes.dex".equalsIgnoreCase(listFiles[i].getName())) {
loadedDex.add(listFiles[i]);
}
}
//创建自定义的类加载器
createDexClassLoader(context ,fileDir);
}
/**
* @param context
* @param fileDir
* 创建自己的类加载器,加载私有目录的.dex文件,上面已经将修复包中的dex文件copy到私有目录
*/
private static void createDexClassLoader(Context context, File fileDir) {
//解压目录
String optimizedDir = fileDir.getAbsolutePath()+File.separator+"opt_dex";
File fileOpt = new File(optimizedDir);
if (!fileOpt.exists()) {
fileOpt.mkdirs();
}
for (File dex : loadedDex) {
//创建自己的类加载器,临时的
DexClassLoader classLoader = new DexClassLoader(dex.getAbsolutePath(), optimizedDir, null, context.getClassLoader());
//有一个修复文件,就插装一次
hotFix(classLoader,context);
}
}
private static void hotFix(DexClassLoader classLoader, Context context) {
try {
//获取系统的PathClassLoader类加载器
PathClassLoader pathClassLoader = (PathClassLoader)context.getClassLoader();
//获取自己的dexElements数组
Object myElements = ReflectUtils.getDexElements(ReflectUtils.getPathList(classLoader));
//获取系统的dexElements数组
Object systemElements = ReflectUtils.getDexElements(ReflectUtils.getPathList(pathClassLoader));
//合并数组,并排序,生成一个新的数组
Object dexElements=ArrayUtils.combineArray(myElements,systemElements);
//通过反射获取系统的pathList属性
Object systemPathList = ReflectUtils.getPathList(pathClassLoader);
//通过反射,将合并后新的dexElements赋值给系统的pathList
ReflectUtils.setFieldValue(systemPathList,"dexElements",dexElements);
}catch (Exception e){
e.printStackTrace();
}
}
}
El trabajo principal es: el diagrama de flujo anterior es obtener primero la .dex
colección de archivos que necesita ser reparada en caliente a través de operaciones como recorrido, descompresión, etc. , atravesar la colección, crear una temporal cada vez DexClassLoader
y luego realizar los pasos de reparación. La división es de seis pasos:
El efecto final se muestra en la figura (el teléfono móvil utilizado en la demostración es un teléfono móvil Huawei 8.0):
Nota: Para que el renderizado sea más intuitivo, la aplicación se ha reiniciado una vez.
Nota: La reparación en caliente implementada de esta manera debe reiniciar la aplicación para lograr la reparación. Esto también está determinado por el mecanismo de carga de clases. Después de la reparación es como se muestra en la siguiente figura, abra la ejecución de carga nuevamente Elclasses.dex
archivo reparado seBaseApplication
llama método de reparación en
Al final
Aquí también comparto un PDF de aprendizaje de Android + video de arquitectura + documento de entrevista + notas de origen , mapa mental avanzado de tecnología de arquitectura avanzada, materiales especiales de entrevistas de desarrollo de Android, materiales de arquitectura avanzada avanzada recopilados y organizados por varios grandes .
Si tiene una necesidad, puede señalarlo para recibir
Si te gusta este artículo, también puedes darme un pequeño me gusta, dejar un mensaje en el área de comentarios o reenviarlo y apoyarlo ~