Android 热修复原理
2016-04-06 14:52
483 查看
当安卓项目需要更新小bug时热修复是一个选择。原文:http://hanks.xyz/2016/01/05/android-myhotfix/
创建工程
├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── xyz │ │ └── hanks │ │ └── fix │ │ ├── BugClass.java │ │ ├── FixApplication.java │ │ └── MainActivity.java |
BugClass类是需要修复的类,
MainActivity是主Activity,
FixApplication是自定义的Application. 初始的MainActivity如下
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView textView = (TextView) findViewById(R.id.text); textView.setText(new BugClass().showToast("Happy new year")); } } |
/** * Created by hanks on 16-1-2. */ public class BugClass { public String showToast(String content){ return content; } } |
MainActivity类会被加上CLASS_ISPREVERIFIED 标志,因为BugClass 和 MainActivity 都属于同一个dex. 如果现在直接加载补丁包中的BugClass 类,那么就会出现
Class ref in pre-verified class resolved to unexpected implementation错误.
引用hack.dex,防止类加上CLASS_ISPREVERIFIED
因为我们要修复BugClass类,而调用是在
MainActivity中,也就是说,当打上补丁包之后,
MainActivity调用的
BugClass将会是补丁包中的
BugClass(也就是来自于其他的dex),那么我就就需要防止
MainActivity被加上CLASS_ISPREVERIFIED 标志. 那么怎么防止呢?
需要在
MainActivity中引用别的dex(hack.dex)中的一个类.那么代码如下:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView textView = (TextView) findViewById(R.id.text); System.out.print(Hack.class); // 引用 hack.dex中的Hack类 textView.setText(new BugClass().showToast("Happy new year")); } }
上面代码只是简单的引用了一下
Hack.class, 这样程序运行起来就不会把
MainActivity加上CLASS_ISPREVERIFIED. 注意现在的代码是编译不过的. 引用我们的程序中没有
Hack.class, 要想编译通过,那么我们就得有
Hack.class, 于是新建一个library, 然后app这个依赖与这个library, 但是注意不要使用compile,
使用provided 关键字,这样标示这个library这是提供引用,并不被编译到apk中(不在MainActivity的dex中).这样就解决了编译问题.
现在运行起来程序还是有错误, 因为
MainActivity引用了
Hack.class,虽然编译通过了,但是实际上是没有这个类的,所以这个时候就需要在调用Hack这个类之前,先动态加载进来.
先加载Hack.dex,保证引用不会出错
/** * 加载 * Created by hanks on 16-1-3. */ public class FixApplication extends Application { @Override public void onCreate() { super.onCreate(); try { PathClassLoader pathClassLoader = (PathClassLoader) getClassLoader(); String hackFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() +"/hack.dex"; // hack.dex的路径 Object a = combineArray(getDexElements(getPathList(pathClassLoader)), // 原有的 dex getDexElements(getPathList(new DexClassLoader(hackFilePath, getDir("dex", 0).getAbsolutePath(), hackFilePath, getClassLoader())))); // 将新的dex插入到dexElements数组的前面 Object a2 = getPathList(pathClassLoader); setField(a2, a2.getClass(), "dexElements", a); // 通过反射修改dexElements数组 pathClassLoader.loadClass("xyz.hanks.Hack"); } catch (Exception e) { e.printStackTrace(); } } } |
生成补丁包
现在BugClass出现bug了. 修改一下, 然后将修改后的
BugClass导出jar包,然后通过
dx工具转换成dex,就叫做patch.dex 吧.然后放入到sdcard目录下.
现在可以加载补丁包了.
/** * 加载 * Created by hanks on 16-1-3. */ public class FixApplication extends Application { @Override public void onCreate() { super.onCreate(); try { PathClassLoader pathClassLoader = (PathClassLoader) getClassLoader(); String hackFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() +"/hack.dex"; // hack.dex的路径 Object a = combineArray(getDexElements(getPathList(pathClassLoader)), // 原有的 dex getDexElements(getPathList(new DexClassLoader(hackFilePath, getDir("dex", 0).getAbsolutePath(), hackFilePath, getClassLoader())))); // 将新的dex插入到dexElements数组的前面 Object a2 = getPathList(pathClassLoader); setField(a2, a2.getClass(), "dexElements", a); // 通过反射修改dexElements数组 pathClassLoader.loadClass("xyz.hanks.Hack"); // 加载补丁包 String patchFilePath = Environment.getExternalStorageDirectory() .getAbsolutePath() + "/patch.dex"; Object a3 = combineArray(getDexElements(getPathList(pathClassLoader)), // 原有的 dex getDexElements(getPathList(new DexClassLoader(patchFilePath, getDir("dex", 0).getAbsolutePath(), patchFilePath, getClassLoader())))); Object a4 = getPathList(pathClassLoader); setField(a4, a4.getClass(), "dexElements", a3); pathClassLoader.loadClass("xyz.hanks.fix.BugClass"); } catch (Exception e) { e.printStackTrace(); } } } |
相关文章推荐
- Android中一些常用类的常用方法(Math、Random、Color、Paint、Canvas、Bitmap、BitmapFactory)
- Android list加载图片工具类
- Android ADB server didn't ACK * failed to start daemon * 简单有效的解决方案
- android系统的事件分发与消费机制
- Android框架设计之-总结
- 兼容 Android 4.4 透明状态栏与导航栏
- Android适配
- Manifest.xml 中android:windowSoftInputMode属性详解
- android 混淆
- Android 元素置于父元素底部
- 使用DatagramSocket发送、接收数据(Socket之UDP套接字)android遥控器
- Android TextView横向滚动(跑马灯效果)
- android 5.0及以上,seekbar thumb 透明效果出现父布局背景颜色的解决方法
- toolbar自定义右边的菜单注意
- Android 常用Intent封装
- android studio 下shareSDK的步骤
- Android6.0 高通平台 "is 32-bit instead of 64-bit" 问题
- Android触摸事件(三)-触摸事件类使用实例
- Android冷门知识。读写图片Exif信息,用到了JHeader jar包
- android ViewPager