├── .gitignore ├── .idea ├── encodings.xml ├── misc.xml ├── modules.xml └── runConfigurations.xml ├── Demo1Ex ├── classes2.jar └── classes2.zip ├── Demo2Ex ├── hello_csdn_lqr │ ├── armeabi-v7a │ │ └── libLQRJni.so │ ├── armeabi │ │ └── libLQRJni.so │ └── x86 │ │ └── libLQRJni.so └── hello_lqr │ ├── armeabi-v7a │ └── libLQRJni.so │ ├── armeabi │ └── libLQRJni.so │ └── x86 │ └── libLQRJni.so ├── Demo3Ex ├── app-release-R.txt ├── app-release-mapping.txt ├── app-release.apk ├── patch_signed_7zip.apk └── readme.txt ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── lqr │ │ └── hotfixdemo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── lqr │ │ │ └── hotfixdemo │ │ │ ├── MainActivity.java │ │ │ └── simplehotfix │ │ │ ├── FixDexUtils.java │ │ │ ├── SimpleHotFixActivity.java │ │ │ └── SimpleHotFixBugTest.java │ └── res │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ └── activity_simple_hot_fix.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── lqr │ └── hotfixdemo │ └── ExampleUnitTest.java ├── build.gradle ├── config.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jnitest ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── lqr │ │ └── jnitest │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── lqr │ │ │ └── jnitest │ │ │ └── JniUtil.java │ ├── jni │ │ ├── Android.mk │ │ ├── Application.mk │ │ ├── com_lqr_jnitest_JniUtil.c │ │ ├── com_lqr_jnitest_JniUtil.h │ │ └── readme.txt │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── lqr │ └── jnitest │ └── ExampleUnitTest.java ├── settings.gradle ├── tinker-bugly ├── .gitignore ├── build.gradle ├── keystore │ ├── debug.keystore │ └── release.keystore ├── libs │ ├── armeabi-v7a │ │ └── libLQRJni.so │ ├── armeabi │ │ └── libLQRJni.so │ └── x86 │ │ └── libLQRJni.so ├── proguard-rules.pro ├── src │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── lqr │ │ │ └── ExampleInstrumentedTest.java │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── lqr │ │ │ │ ├── MainActivity.java │ │ │ │ ├── MyApplication.java │ │ │ │ ├── SampleApplication.java │ │ │ │ ├── SampleApplicationLike.java │ │ │ │ └── jnitest │ │ │ │ └── JniUtil.java │ │ └── res │ │ │ ├── drawable │ │ │ ├── ic_launcher_background.xml │ │ │ └── img_header.jpg │ │ │ ├── layout │ │ │ └── activity_main.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ │ └── xml │ │ │ └── provider_paths.xml │ └── test │ │ └── java │ │ └── com │ │ └── lqr │ │ └── ExampleUnitTest.java └── tinker-support.gradle └── tinker-local ├── .gitignore ├── build.gradle ├── keystore ├── debug.keystore └── release.keystore ├── proguard-rules.pro └── src ├── androidTest └── java │ └── com │ └── lqr │ └── tinker_local │ └── ExampleInstrumentedTest.java ├── main ├── AndroidManifest.xml ├── java │ └── com │ │ └── lqr │ │ ├── BaseBuildInfo.java │ │ ├── BuildInfo.java │ │ ├── MainActivity.java │ │ ├── TinkerApplicationLike.java │ │ ├── jnitest │ │ └── JniUtil.java │ │ └── tinker │ │ ├── Log │ │ └── MyLogImp.java │ │ ├── crash │ │ └── SampleUncaughtExceptionHandler.java │ │ ├── reporter │ │ ├── SampleLoadReporter.java │ │ ├── SamplePatchListener.java │ │ ├── SamplePatchReporter.java │ │ └── SampleTinkerReport.java │ │ ├── service │ │ └── SampleResultService.java │ │ └── utils │ │ ├── TinkerManager.java │ │ └── TinkerUtils.java ├── jniLibs │ ├── armeabi-v7a │ │ └── libLQRJni.so │ ├── armeabi │ │ └── libLQRJni.so │ └── x86 │ │ └── libLQRJni.so └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ ├── ic_launcher_background.xml │ └── img_header.jpg │ ├── layout │ └── activity_main.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml └── test └── java └── com └── lqr └── tinker_local └── ExampleUnitTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | .idea/workspace.xml 38 | .idea/tasks.xml 39 | .idea/gradle.xml 40 | .idea/dictionaries 41 | .idea/libraries 42 | 43 | # Keystore files 44 | *.jks 45 | 46 | # External native build folder generated in Android Studio 2.2 and later 47 | .externalNativeBuild 48 | 49 | # Google Services (e.g. APIs or Firebase) 50 | google-services.json 51 | 52 | # Freeline 53 | freeline.py 54 | freeline/ 55 | freeline_project_description.json 56 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 23 | 33 | 34 | 35 | 36 | 37 | 38 | 40 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /Demo1Ex/classes2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitLqr/HotFixDemo/865d684b3c1c7b820e753acc8fab298c5ca5d820/Demo1Ex/classes2.jar -------------------------------------------------------------------------------- /Demo1Ex/classes2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitLqr/HotFixDemo/865d684b3c1c7b820e753acc8fab298c5ca5d820/Demo1Ex/classes2.zip -------------------------------------------------------------------------------- /Demo2Ex/hello_csdn_lqr/armeabi-v7a/libLQRJni.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitLqr/HotFixDemo/865d684b3c1c7b820e753acc8fab298c5ca5d820/Demo2Ex/hello_csdn_lqr/armeabi-v7a/libLQRJni.so -------------------------------------------------------------------------------- /Demo2Ex/hello_csdn_lqr/armeabi/libLQRJni.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitLqr/HotFixDemo/865d684b3c1c7b820e753acc8fab298c5ca5d820/Demo2Ex/hello_csdn_lqr/armeabi/libLQRJni.so -------------------------------------------------------------------------------- /Demo2Ex/hello_csdn_lqr/x86/libLQRJni.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitLqr/HotFixDemo/865d684b3c1c7b820e753acc8fab298c5ca5d820/Demo2Ex/hello_csdn_lqr/x86/libLQRJni.so -------------------------------------------------------------------------------- /Demo2Ex/hello_lqr/armeabi-v7a/libLQRJni.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitLqr/HotFixDemo/865d684b3c1c7b820e753acc8fab298c5ca5d820/Demo2Ex/hello_lqr/armeabi-v7a/libLQRJni.so -------------------------------------------------------------------------------- /Demo2Ex/hello_lqr/armeabi/libLQRJni.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitLqr/HotFixDemo/865d684b3c1c7b820e753acc8fab298c5ca5d820/Demo2Ex/hello_lqr/armeabi/libLQRJni.so -------------------------------------------------------------------------------- /Demo2Ex/hello_lqr/x86/libLQRJni.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitLqr/HotFixDemo/865d684b3c1c7b820e753acc8fab298c5ca5d820/Demo2Ex/hello_lqr/x86/libLQRJni.so -------------------------------------------------------------------------------- /Demo3Ex/app-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitLqr/HotFixDemo/865d684b3c1c7b820e753acc8fab298c5ca5d820/Demo3Ex/app-release.apk -------------------------------------------------------------------------------- /Demo3Ex/patch_signed_7zip.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitLqr/HotFixDemo/865d684b3c1c7b820e753acc8fab298c5ca5d820/Demo3Ex/patch_signed_7zip.apk -------------------------------------------------------------------------------- /Demo3Ex/readme.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitLqr/HotFixDemo/865d684b3c1c7b820e753acc8fab298c5ca5d820/Demo3Ex/readme.txt -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 CSDN_LQR 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HotFixDemo 2 | 热修复Demo 3 | 4 | 5 | 1. [《热修复——深入浅出原理与实现》](https://juejin.im/post/5a0ad2b551882531ba1077a2) 6 | 2. [《热修复——Tinker的集成与使用》](https://juejin.im/post/5a27bdaf6fb9a044fa19bcfc) 7 | 3. [《热修复——Bugly让热修复变得如此简单》](https://juejin.im/post/5a2fa1f26fb9a0450e7616ad) 8 | 9 | ![](https://cdn.jsdelivr.net/gh/FullStackAction/PicBed@resource/image/FSA_QR_bottom.png) 10 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | 6 | defaultConfig { 7 | applicationId "com.lqr.hotfixdemo" 8 | minSdkVersion 16 9 | targetSdkVersion 26 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | 15 | buildTypes { 16 | release { 17 | minifyEnabled true 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), project.file('proguard-rules.pro') 19 | } 20 | debug { 21 | debuggable true 22 | minifyEnabled false 23 | } 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation fileTree(dir: 'libs', include: ['*.jar']) 29 | implementation 'com.android.support:appcompat-v7:26.1.0' 30 | testImplementation 'junit:junit:4.12' 31 | } 32 | 33 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/lqr/hotfixdemo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.lqr.hotfixdemo; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.lqr.hotfixdemo", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/lqr/hotfixdemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.lqr.hotfixdemo; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.View; 7 | 8 | import com.lqr.hotfixdemo.simplehotfix.SimpleHotFixActivity; 9 | 10 | public class MainActivity extends AppCompatActivity { 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_main); 16 | } 17 | 18 | public void simpleHostFix(View view) { 19 | startActivity(new Intent(this, SimpleHotFixActivity.class)); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/lqr/hotfixdemo/simplehotfix/FixDexUtils.java: -------------------------------------------------------------------------------- 1 | package com.lqr.hotfixdemo.simplehotfix; 2 | 3 | import android.content.Context; 4 | 5 | import java.io.File; 6 | import java.lang.reflect.Array; 7 | import java.lang.reflect.Field; 8 | import java.util.HashSet; 9 | 10 | import dalvik.system.DexClassLoader; 11 | import dalvik.system.PathClassLoader; 12 | 13 | /** 14 | * @创建者 CSDN_LQR 15 | * @描述 热修复工具(只认后缀是dex、apk、jar、zip的补丁) 16 | */ 17 | public class FixDexUtils { 18 | 19 | private static final String DEX_SUFFIX = ".dex"; 20 | private static final String APK_SUFFIX = ".apk"; 21 | private static final String JAR_SUFFIX = ".jar"; 22 | private static final String ZIP_SUFFIX = ".zip"; 23 | public static final String DEX_DIR = "odex"; 24 | private static final String OPTIMIZE_DEX_DIR = "optimize_dex"; 25 | private static HashSet loadedDex = new HashSet<>(); 26 | 27 | static { 28 | loadedDex.clear(); 29 | } 30 | 31 | /** 32 | * 加载补丁,使用默认目录:data/data/包名/files/odex 33 | * 34 | * @param context 35 | */ 36 | public static void loadFixedDex(Context context) { 37 | loadFixedDex(context, null); 38 | } 39 | 40 | /** 41 | * 加载补丁 42 | * 43 | * @param context 上下文 44 | * @param patchFilesDir 补丁所在目录 45 | */ 46 | public static void loadFixedDex(Context context, File patchFilesDir) { 47 | if (context == null) { 48 | return; 49 | } 50 | // 遍历所有的修复dex 51 | File fileDir = patchFilesDir != null ? patchFilesDir : new File(context.getFilesDir(), DEX_DIR);// data/data/包名/files/odex(这个可以任意位置) 52 | File[] listFiles = fileDir.listFiles(); 53 | for (File file : listFiles) { 54 | if (file.getName().startsWith("classes") && 55 | (file.getName().endsWith(DEX_SUFFIX) 56 | || file.getName().endsWith(APK_SUFFIX) 57 | || file.getName().endsWith(JAR_SUFFIX) 58 | || file.getName().endsWith(ZIP_SUFFIX))) { 59 | loadedDex.add(file);// 存入集合 60 | } 61 | } 62 | // dex合并之前的dex 63 | doDexInject(context, loadedDex); 64 | } 65 | 66 | private static void doDexInject(Context appContext, HashSet loadedDex) { 67 | String optimizeDir = appContext.getFilesDir().getAbsolutePath() + File.separator + OPTIMIZE_DEX_DIR;// data/data/包名/files/optimize_dex(这个必须是自己程序下的目录) 68 | File fopt = new File(optimizeDir); 69 | if (!fopt.exists()) { 70 | fopt.mkdirs(); 71 | } 72 | try { 73 | // 1.加载应用程序的dex 74 | PathClassLoader pathLoader = (PathClassLoader) appContext.getClassLoader(); 75 | for (File dex : loadedDex) { 76 | // 2.加载指定的修复的dex文件 77 | DexClassLoader dexLoader = new DexClassLoader( 78 | dex.getAbsolutePath(),// 修复好的dex(补丁)所在目录 79 | fopt.getAbsolutePath(),// 存放dex的解压目录(用于jar、zip、apk格式的补丁) 80 | null,// 加载dex时需要的库 81 | pathLoader// 父类加载器 82 | ); 83 | // 3.合并 84 | Object dexPathList = getPathList(dexLoader); 85 | Object pathPathList = getPathList(pathLoader); 86 | Object leftDexElements = getDexElements(dexPathList); 87 | Object rightDexElements = getDexElements(pathPathList); 88 | // 合并完成 89 | Object dexElements = combineArray(leftDexElements, rightDexElements); 90 | // 重写给PathList里面的Element[] dexElements;赋值 91 | Object pathList = getPathList(pathLoader); 92 | setField(pathList, pathList.getClass(), "dexElements", dexElements); 93 | } 94 | } catch (Exception e) { 95 | e.printStackTrace(); 96 | } 97 | } 98 | 99 | /** 100 | * 反射给对象中的属性重新赋值 101 | */ 102 | private static void setField(Object obj, Class cl, String field, Object value) throws NoSuchFieldException, IllegalAccessException { 103 | Field declaredField = cl.getDeclaredField(field); 104 | declaredField.setAccessible(true); 105 | declaredField.set(obj, value); 106 | } 107 | 108 | /** 109 | * 反射得到对象中的属性值 110 | */ 111 | private static Object getField(Object obj, Class cl, String field) throws NoSuchFieldException, IllegalAccessException { 112 | Field localField = cl.getDeclaredField(field); 113 | localField.setAccessible(true); 114 | return localField.get(obj); 115 | } 116 | 117 | 118 | /** 119 | * 反射得到类加载器中的pathList对象 120 | */ 121 | private static Object getPathList(Object baseDexClassLoader) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException { 122 | return getField(baseDexClassLoader, Class.forName("dalvik.system.BaseDexClassLoader"), "pathList"); 123 | } 124 | 125 | /** 126 | * 反射得到pathList中的dexElements 127 | */ 128 | private static Object getDexElements(Object pathList) throws NoSuchFieldException, IllegalAccessException { 129 | return getField(pathList, pathList.getClass(), "dexElements"); 130 | } 131 | 132 | /** 133 | * 数组合并 134 | */ 135 | private static Object combineArray(Object arrayLhs, Object arrayRhs) { 136 | Class componentType = arrayLhs.getClass().getComponentType(); 137 | int i = Array.getLength(arrayLhs);// 得到左数组长度(补丁数组) 138 | int j = Array.getLength(arrayRhs);// 得到原dex数组长度 139 | int k = i + j;// 得到总数组长度(补丁数组+原dex数组) 140 | Object result = Array.newInstance(componentType, k);// 创建一个类型为componentType,长度为k的新数组 141 | System.arraycopy(arrayLhs, 0, result, 0, i); 142 | System.arraycopy(arrayRhs, 0, result, i, j); 143 | return result; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /app/src/main/java/com/lqr/hotfixdemo/simplehotfix/SimpleHotFixActivity.java: -------------------------------------------------------------------------------- 1 | package com.lqr.hotfixdemo.simplehotfix; 2 | 3 | import android.os.Bundle; 4 | import android.os.Environment; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | 9 | import com.lqr.hotfixdemo.R; 10 | 11 | 12 | public class SimpleHotFixActivity extends AppCompatActivity { 13 | 14 | @Override 15 | protected void onCreate(@Nullable Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_simple_hot_fix); 18 | } 19 | 20 | public void fix(View view) { 21 | FixDexUtils.loadFixedDex(this, Environment.getExternalStorageDirectory()); 22 | } 23 | 24 | public void clac(View view) { 25 | SimpleHotFixBugTest test = new SimpleHotFixBugTest(); 26 | test.getBug(this); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/lqr/hotfixdemo/simplehotfix/SimpleHotFixBugTest.java: -------------------------------------------------------------------------------- 1 | package com.lqr.hotfixdemo.simplehotfix; 2 | 3 | import android.content.Context; 4 | import android.widget.Toast; 5 | 6 | public class SimpleHotFixBugTest { 7 | public void getBug(Context context) { 8 | int i = 10; 9 | int a = 0; 10 | Toast.makeText(context, "Hello,I am CSDN_LQR:" + i / a, Toast.LENGTH_SHORT).show(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | 16 | 21 | 26 | 31 | 36 | 41 | 46 | 51 | 56 | 61 | 66 | 71 | 76 | 81 | 86 | 91 | 96 | 101 | 106 | 111 | 116 | 121 | 126 | 131 | 136 | 141 | 146 | 151 | 156 | 161 | 166 | 171 | 172 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 |