public static void test(Context context) {
Toast.makeText(context.getApplicationContext(), "new apk...", Toast.LENGTH_SHORT).show();
}
最新的补丁工具会忽略掉这些情况下的变更.
新增temp全局变量
private static String temp = "new apk...";//新增temp变量不允许!
public static void test(Context context) {
Toast.makeText(context.getApplicationContext(), temp, Toast.LENGTH_SHORT).show();
}
打补丁工具直接报错, com.taobao.baichuan.exception.HotFixException: There some Field changed
修改全局变量(静态变量也一样)
private static String temp = "old apk..."; private static String temp = "new apk...";
修改构造函数
public BaseBug() {
//temp = "old apk..."; 修复前
temp = "new apk..."; 修复后
}
另一方面代码块也是不允许修改的, 也会被打补丁工具直接忽略. 包括静态代码块
{
Log.d(TAG, "old block");
}
static{
Log.d(TAG, "new block");
}
解释下: 其实原理类似, 总之目前的打包工具会把smail文件中的
打补丁的时候会抛“com.taobao.android.dex.PatchException: can’t add method:newMethod”异常
public static void test(Context context) {
newMethod(context);
}
public static void newMethod(Context context) {
Toast.makeText(context.getApplicationContext(), "new apk...", Toast.LENGTH_SHORT).show();
}
假如BaseBug有构造函数, 任何对构造函数的修改都是不允许的, 会导致加载补丁失败
public BaseBug() {
//temp = "old apk..."; 修复前
temp = "new apk..."; 修复后
}
虽然不允许直接添加属性和方法, 但是支持新增类, 可以通过新增类(可以是新增内部类)的目的来达到添加属性和方法的目的
static class NewClass{
static String temp = "new apk...";
static void test(Context context) {
Toast.makeText(context.getApplicationContext(), temp, Toast.LENGTH_SHORT).show();
}
}
public static void test(Context context) {
NewClass.test(context);
}
假设test方法被反射调用, 因为静态方法反射调用可以有两种方式, 所以会出现以下两种情况, 一一说明
Class cls = BaseBug.class;
Method method = cls.getDeclaredMethod("test", new Class[]{Context.class});
不支持,test方法不能被patch, 此时会报错类似IllegalArgumentException: Expected receiver of type com.taobao.hotfix.demo.BaseBug_CF_1476949855532, but got com.taobao.hotfix.demo.BaseBug的异常, 因为此时test方法已经被替换为BaseBug_CF_1476949855532补丁类中的test方法. 所以如果BaseBug对象invoke test方法的话, 肯定IllegalArgumentException异常
method.invoke(new BaseBug(), new Object[]{mContext});
支持, test方法能被patch, 因为参数是null
method.invoke(null, new Object[]{mContext});
非静态方法调用肯定是类似method.invoke(new BaseBug(), new Object[]{}); 参考上面, 所以肯定会有IllegalArgumentException异常的, 所以此时test方法不能被patch.
如果补丁加载过程中碰到IllegalArgumentException: Expected receiver of type com.taobao.hotfix.demo.BaseBug_CF_1476949855532, but got com.taobao.hotfix.demo.BaseBug类似的错误, 该方法多半是被反射调用了, 该方法是不支持被patch的. 可能有时候全局搜索项目, 但是没发现方法被反射调用, 那么就需要认真检查了, 是否有第三方库反射调用该方法, 比如旧版本的EvenBus框架, 通过反射调用onEventMainThread/onEvent方法实现事件总线机制, 所以onEventMainThread/onEvent方法等方法其实是不能被patch的.
假设代码中定义了这么一个运行时注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface MethodInfo {
String info();
}
这样是不支持的, 此时打补丁过程会提示没有任何类的修改
public @interface MethodInfo {
String info() default "old apk...";
}
public @interface MethodInfo {
String info() default "new apk...";
}
test方法被MethodInfo注解, 其它地方拿到test方法的注解, 此时如果需要修复test方法MethodInfo注解信息, 是支持的. annoTest方法能够被正确patch
//修复前 @MethodInfo(info="old apk...")
//修复后 @MethodInfo(info="new apk...")
public static void test(Context context {
Toast.makeText(context.getApplicationContext(), "old apk...", Toast.LENGTH_SHORT).show();
}
一般情况下来说是支持的, 但是要保证编译期注解的方法, 不能被编译框架生成的三方类反射调用, 如果被反射调用了, 那么该方法不能被patch
比如: 常见的butterknife框架注解的类是支持的, butterknife注解修饰的方法没用被反射调用
在art虚拟机(android5.0以上),会发现其实没有这个限制, 上面两种情况可以被patch成功. 但是在dalvik虚拟机上会有兼容性问题, 所以我们标注为不支持, 请不要触发这个红线. 后续我们会持续研发改进, 取消上面几个限制, 敬请期待.
上述的那些限制都不是固定的, 我们会持续研发, 逐步减少那些限制.