美文网首页
阿里系热修复-andfix

阿里系热修复-andfix

作者: 月影路西法 | 来源:发表于2020-04-06 19:32 被阅读0次

    AndFix的github地址
    AndFix是一种在线修复错误而不是重新分发Android App的解决方案。它作为Android库分发。
    Andfix是一个缩写“ android 热修复”。
    AndFix支持Android版本从2.3到7.0,ARM和X86架构,以及Dalvik和ART运行时(32位和64位)。
    AndFix补丁的压缩文件格式是.apatch。它从您自己的服务器分发到客户端,以修复您的应用程序错误。

    大致修复原理.png

    andfix使用

    在使用中,可以将整个使用过程分成两部分

    1. 原始apk和修复之后的apk的编写
    2. patch文件的生成
      3.在代码中调用生成的patch

    生成patch文件

    在下载的源码目录中,有一个Tool的目录,将你项目的apk 要修复的apk 以及签名文件放到该目录中,输入命令如下
    usage: apkpatch -f <new> -t <old> -o <output> -k <keystore> -p <> -a <alias> -e <>
    -a,--alias <alias> keystore entry alias// 用户别名
    -e,--epassword <> keystore entry password.//用户别名密码
    -f,--from <loc> new Apk file path.//新apk
    -k,--keystore <loc> keystore path.//打包所用的keystore目录
    -n,--name <name> patch name.
    -o,--out <dir> output dir.//输出文件的目录
    -p,--kpassword <
    > keystore password.//密码
    -t,--to <loc> old Apk file path.//旧apk

    C:\Users\freed>E:
    E:\>cd E:\StudioProjects\AndFix-master\AndFix-master\tools\apkpatch-1.0.3
    E:\StudioProjects\AndFix-master\AndFix-master\tools\apkpatch-1.0.3>apkpatch -f new.apk -t old.apk -o . -k iccardKey.jks -p yunmeng -a iccard -e yunmeng
    add modified Method:V  setContentView()  in Class:Lcom/yunmeng/liuy/iccard/ui/activity/WelcomeActivity;
    

    在目录中会生成diff.dex 与一个apatch的文件
    将apatch文件放到需要加载的目录中

    添加依赖

    dependencies {
        implementation 'com.alipay.euler:andfix:0.5.0@aar'
    }
    

    在代码上的使用

    public class AndFixApplication extends Application {
        private static final String TAG = "euler";
    
        private static final String APATCH_PATH = "/out.apatch";
        /**
         * patch manager
         */
        private PatchManager mPatchManager;
    
        @Override
        public void onCreate() {
            super.onCreate();
            try {
                init();
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
        }
    
        private void init() throws PackageManager.NameNotFoundException {
            // 初始化patch管理类
            mPatchManager=new PatchManager(this);
            // 初始化patch版本,如果patch包中的版本信息与应用中的不一致,则不更新
            mPatchManager.init(this.getPackageManager().getPackageInfo(getPackageName(),0).versionName);
            // 加载已经添加到PatchManager中的patch
            mPatchManager.loadPatch();
        }
    
        private void update() {
            String patchFileStr = Environment.getExternalStorageDirectory().getAbsolutePath() + APATCH_PATH;
            try {
                mPatchManager.addPatch(patchFileStr);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    在更新完成后无需再次启动

    AndFix原理

    阿里系修复方案使用的是底层替换方案
    与腾讯系的类加载方案不同的是,底层替换方案不会再次加载新类,而是直接在Native层修改原有类,由于是在原有类进行修改限制会比较多,不能够增减原有类的方法和字段,如果我们增加了方法数,那么方法索引数也会增加,这样访问方法时会无法通过索引找到正确的方法,同样的字段也是类似的情况。
    底层替换方案和反射的原理有些关联,就拿方法替换来说,方法反射我们可以调用java.lang.Class.getDeclaredMethod,假设我们要反射Key的show方法,会调用如下所示。

    Key.class.getDeclaredMethod("show").invoke(Key.class.newInstance());
    
    

    Android 8.0的invoke方法,如下所示。
    libcore/ojluni/src/main/java/java/lang/reflect/Method.java

    @FastNative
    public native Object invoke(Object obj, Object... args)
            throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
    
    

    invoke方法是个native方法,对应Jni层的代码为:
    art/runtime/native/java_lang_reflect_Method.cc

    static jobject Method_invoke(JNIEnv* env, jobject javaMethod, jobject javaReceiver,
                                 jobject javaArgs) {
      ScopedFastNativeObjectAccess soa(env);
      return InvokeMethod(soa, javaMethod, javaReceiver, javaArgs);
    

    Method_invoke函数中又调用了InvokeMethod函数:
    art/runtime/reflection.cc

    jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaMethod,
                         jobject javaReceiver, jobject javaArgs, size_t num_frames) {
    
    ...
      ObjPtr<mirror::Executable> executable = soa.Decode<mirror::Executable>(javaMethod);
      const bool accessible = executable->IsAccessible();
      ArtMethod* m = executable->GetArtMethod();//1
    ...
    }
    

    注释1处获取传入的javaMethod(Key的show方法)在ART虚拟机中对应的一个ArtMethod指针,ArtMethod结构体中包含了Java方法的所有信息,包括执行入口、访问权限、所属类和代码执行地址等等,ArtMethod结构如下所示。
    art/runtime/art_method.h

    class ArtMethod FINAL {
    ...
     protected:
      GcRoot<mirror::Class> declaring_class_;
      std::atomic<std::uint32_t> access_flags_;
      uint32_t dex_code_item_offset_;
      uint32_t dex_method_index_;
      uint16_t method_index_;
      uint16_t hotness_count_;
     struct PtrSizedFields {
        ArtMethod** dex_cache_resolved_methods_;//1
        void* data_;
        void* entry_point_from_quick_compiled_code_;//2
      } ptr_sized_fields_;
    }
    

    ArtMethod结构中比较重要的字段是注释1处的dex_cache_resolvedmethods和注释2处的entry_point_from_quick_compiledcode,它们是方法的执行入口,当我们调用某一个方法时(比如Key的show方法),就会取得show方法的执行入口,通过执行入口就可以跳过去执行show方法。
    替换ArtMethod结构体中的字段或者替换整个ArtMethod结构体,这就是底层替换方案。
    AndFix采用的是替换ArtMethod结构体中的字段,这样会有兼容问题,因为厂商可能会修改ArtMethod结构体,导致方法替换失败。Sophix采用的是替换整个ArtMethod结构体,这样不会存在兼容问题。
    底层替换方案直接替换了方法,可以立即生效不需要重启。采用底层替换方案主要是阿里系为主,包括AndFix、Dexposed、阿里百川、Sophix。
    demo地址

    相关文章

      网友评论

          本文标题:阿里系热修复-andfix

          本文链接:https://www.haomeiwen.com/subject/fcwbuhtx.html