热更新 阿里的Dexposed

介绍

GitHub

重要提醒:

  • 1、此方案不支持ART(Android 5.0 及之后的Android版本),这一点是致命的!
  • 2、此方案不支持Dalvik 3.0(专为平板设计的Android版本)
  • 3、只支持 ARM 架构

基于此,注定它会逐步失声,再多的优点也是徒劳。

dexposed enable ‘god‘ mode上帝视角 for single android application.

基本实现原理简介

下面是一段比较官方的介绍:

Dexposed中的AOP原理来自于Xposed。在Dalvik虚拟机下,主要是通过改变一个方法对象方法在Dalvik虚拟机中的定 义来实现,具体做法就是将该方法的类型改变为native并且将这个方法的实现链接到一个通用的Native Dispatch方法上。这个 Dispatch方法通过JNI回调到Java端的一个统一处理方法,最后在统一处理方法中调用before, after函数来实现AOP。在Art虚拟机上目前也是是通过改变一个 ArtMethod的入口函数来实现。

从上面可以知道基础的实现过程,另外上面提到了两个概念,Dalvik虚拟机和Art虚拟机,这里稍稍做做科普。

什么是Dalvik:

Dalvik是Google公司自己设计用于Android平台的Java虚拟机。Dalvik虚拟机是Google等厂商合作开发的Android移动设备平台的核心组成部分之一。它可以支持已转换为 .dex(即Dalvik Executable)格式的Java应用程序的运行,.dex格式是专为Dalvik设计的一种压缩格式,适合内存和处理器速度有限的系统。Dalvik 经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik 应用作为一个独立的Linux 进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。

什么是ART:

Android操作系统已经成熟,Google的Android团队开始将注意力转向一些底层组件,其中之一是负责应用程序运行的Dalvik运行时。Google开发者已经花了两年时间开发更快执行效率更高更省电的替代ART运行时。 ART代表Android Runtime,其处理应用程序执行的方式完全不同于Dalvik,Dalvik是依靠一个Just-In-Time (JIT)编译器去解释字节码。开发者编译后的应用代码需要通过一个解释器在用户的设备上运行,这一机制并不高效,但让应用能更容易在不同硬件和架构上运 行。ART则完全改变了这套做法,在应用安装时就预编译字节码到机器语言,这一机制叫Ahead-Of-Time (AOT)编译。在移除解释代码这一过程后,应用程序执行将更有效率,启动更快。

ART优点:

  • 系统性能的显著提升。
  • 应用启动更快、运行更快、体验更流畅、触感反馈更及时。
  • 更长的电池续航能力。
  • 支持更低的硬件。

ART缺点:

  • 更大的存储空间占用,可能会增加10%-20%。
  • 更长的应用安装时间。

总的来说ART的功效就是“空间换时间”。

google在android4.4之后的版本都用art取代了dalvik,所以要hook android4.4以后的版本就必须去适配art虚拟机的机制。这也解释了为什么会有dexposed和dexposed_l两个so。目前官方表示,为了适配art的dexposed_l只是beta版,所以最好不要在正式的线上产品中使用它。

PS:从代码上看两个 so 文件的区别:

public synchronized static boolean canDexposed(Context context) {
   if (!DeviceCheck.isDeviceSupport(context)) {
      return false;
   }
   return loadDexposedLib(context); //load xposed lib for hook.
}

6

6

1

public synchronized static boolean canDexposed(Context context) {

2

   if (!DeviceCheck.isDeviceSupport(context)) {

3

      return false;

4

   }

5

   return loadDexposedLib(context); //load xposed lib for hook.

6

}
private static boolean loadDexposedLib(Context context) {
   try {
      if (android.os.Build.VERSION.SDK_INT > 19){
         System.loadLibrary("dexposed_l");
      } else if (android.os.Build.VERSION.SDK_INT == 10
            || android.os.Build.VERSION.SDK_INT == 9 || android.os.Build.VERSION.SDK_INT > 14){
         System.loadLibrary("dexposed");
      }
      return true;
   } catch (Throwable e) {
      return false;
   }
}

13

13

1

private static boolean loadDexposedLib(Context context) {

2

   try {

3

      if (android.os.Build.VERSION.SDK_INT > 19){

4

         System.loadLibrary("dexposed_l");

5

      } else if (android.os.Build.VERSION.SDK_INT == 10

6

            || android.os.Build.VERSION.SDK_INT == 9 || android.os.Build.VERSION.SDK_INT > 14){

7

         System.loadLibrary("dexposed");

8

      }

9

      return true;

10

   } catch (Throwable e) {

11

      return false;

12

   }

13

}

可以看到,在sdk版本大于19时,会去调用dexposed_l,否则会调用dexposed。

jar包的java部分代码结构很简洁,DexposedBrigde是主要的功能调用类,XposedHelpers则是一个反射功能类,其他则为一些辅助类。

DexposedBrigde中用来hook的方法 findAndHookMethod 的执行过程大致为:

  • 首先会通过 XposedHelpers 这个反射工具类的 findMethodExact 方法来找到对应的 Method。
  • 然后会执行 hookMethod 方法。
  • 然后里面会调用 hookMethodNative 方法,我们需要给 hookMethodNative 传递的参数分别是
    • 根据反射定位到得要hook得Method;
    • 声明定义Method的class;
    • slot标记runtime的标记符;
    • 包含函数入参,返回值类型以及对应回调的自定义类。
  • 这样就完成了对应的hook。

官方文档

What is it?

Dexposed is a powerful yet并且 non-invasive非侵入性的 runtime AOP (Aspect-oriented Programming) framework for Android app development, based on the work of open-source Xposed framework project.

The AOP of Dexposed is implemented purely纯粹的 non-invasive, without any annotation processor, weaver or bytecode rewriter. The integration is as simple as loading a small JNI library in just one line of code at the initialization phase初始化阶段 of your app.

Not only the code of your app, but also the code of Android framework that running in your app process can be hooked. This feature is extremely useful in Android development as we developers heavily rely on严重依赖 the fragmented old versions of Android platform (SDK).

Together with dynamic class loading, a small piece of compiled Java AOP code can be loaded into the running app, effectively altering the behavior of the target app without restart.

结合动态类加载,可以将一小段编译好的Java AOP代码加载到正在运行的应用程序中,从而有效地改变目标应用程序的行为而无需重新启动。

Typical use-cases 典型使用场景

Classic AOP programming    典型的 AOP 编程

Instrumentation (for testing, performance monitoring and etc.)    仪表化 (测试,性能监控等等)

Online hot patch to fix critical, emergent or security bugs    在线热补丁修复关键,紧急或安全漏洞

SDK hooking for a better development experience    更好的开发体验

Integration

patchloader.jar 导入工程

Directly add dexposed aar to your project as compile libraries, it contains a jar file "dexposedbridge.jar" two so files "libdexposed.so libdexposed_l.so" from ‘dexposed‘ directory.

compile ‘com.taobao.android:dexposed:[email protected]‘

1

1

1

compile ‘com.taobao.android:dexposed:[email protected]‘

注意:这里aar文件只包含了armeabi架构的so文件。

注意:不要忘了加载so库。

System.loadLibrary("dexposed");

1

1

1

System.loadLibrary("dexposed");

Insert the following line into the initialization phase of your app, as early as possible:

// Check whether current device is supported (also initialize Dexposed framework if not yet)
if (DexposedBridge.canDexposed(this)) {
   Log.i("bqt", "Use Dexposed to kick off AOP stuffs.");
} else {
   Log.i("bqt", "Not Supported");
}

6

6

1

// Check whether current device is supported (also initialize Dexposed framework if not yet)

2

if (DexposedBridge.canDexposed(this)) {

3

   Log.i("bqt", "Use Dexposed to kick off AOP stuffs.");

4

} else {

5

   Log.i("bqt", "Not Supported");

6

}

Basic usage

There are three injection points for a given method: before, after, replace.

在指定方法前后执行方法  纯AOP

Example 1: Attach a piece of code before and after all occurrences of Activity.onCreate(Bundle).

可以不改变原函数的执行,但是在原函数执行前后去做一些其他的额外处理,例如改变入参和返回值等等的一些事情。

// Target class, method with parameter types, followed by the hook callback (XC_MethodHook).
DexposedBridge.findAndHookMethod(Activity.class, "onCreate", Bundle.class, new XC_MethodHook() {

   // To be invoked before Activity.onCreate().
   @Override
   protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
      // "thisObject" keeps the reference to the instance of target class.
      Activity instance = (Activity) param.thisObject;

      // The array args include all the parameters.
      Bundle bundle = (Bundle) param.args[0];
      Intent intent = new Intent();
      // XposedHelpers provide useful utility methods.
      XposedHelpers.setObjectField(param.thisObject, "mIntent", intent);

      // Calling setResult() will bypass the original method body use the result as method return value directly.
      if (bundle.containsKey("return"))
         param.setResult(null);
   }

   // To be invoked after Activity.onCreate()
   @Override
   protected void afterHookedMethod(MethodHookParam param) throws Throwable {
      XposedHelpers.callMethod(param.thisObject, "sampleMethod", 2);
   }
});

26

26

1

// Target class, method with parameter types, followed by the hook callback (XC_MethodHook).

2

DexposedBridge.findAndHookMethod(Activity.class, "onCreate", Bundle.class, new XC_MethodHook() {

3

   

4

   // To be invoked before Activity.onCreate().

5

   @Override

6

   protected void beforeHookedMethod(MethodHookParam param) throws Throwable {

7

      // "thisObject" keeps the reference to the instance of target class.

8

      Activity instance = (Activity) param.thisObject;

9

      

10

      // The array args include all the parameters.

11

      Bundle bundle = (Bundle) param.args[0];

12

      Intent intent = new Intent();

13

      // XposedHelpers provide useful utility methods.

14

      XposedHelpers.setObjectField(param.thisObject, "mIntent", intent);

15

      

16

      // Calling setResult() will bypass the original method body use the result as method return value directly.

17

      if (bundle.containsKey("return"))

18

         param.setResult(null);

19

   }

20

   

21

   // To be invoked after Activity.onCreate()

22

   @Override

23

   protected void afterHookedMethod(MethodHookParam param) throws Throwable {

24

      XposedHelpers.callMethod(param.thisObject, "sampleMethod", 2);

25

   }

26

});
DexposedBridge.findAndHookMethod(Log.class, "d", String.class, String.class, new XC_MethodHook() {
   @Override
   protected void afterHookedMethod(MethodHookParam arg0) throws Throwable {
      String tag = (String) arg0.args[0];
      String msg = (String) arg0.args[1];
      System.out.println(tag + "," + msg);
   }
});

8

8

1

DexposedBridge.findAndHookMethod(Log.class, "d", String.class, String.class, new XC_MethodHook() {

2

   @Override

3

   protected void afterHookedMethod(MethodHookParam arg0) throws Throwable {

4

      String tag = (String) arg0.args[0];

5

      String msg = (String) arg0.args[1];

6

      System.out.println(tag + "," + msg);

7

   }

8

});

完全替换某一指定方法  纯AOP

Example 2: Replace the original body of the target method.

可以将原有要执行的函数替换成一个我们需要的新的执行函数,如:替换 Activity.onCreate(Bundle savedInstanceState) 方法

DexposedBridge.findAndHookMethod(Activity.class, "onCreate", Bundle.class, new XC_MethodReplacement() {
   @Override
   protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
      Log.i("bqt", "Re-writing the method logic outside the original method context is a bit tricky but still viable");
      return new Object();
   }
});

7

7

1

DexposedBridge.findAndHookMethod(Activity.class, "onCreate", Bundle.class, new XC_MethodReplacement() {

2

   @Override

3

   protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {

4

      Log.i("bqt", "Re-writing the method logic outside the original method context is a bit tricky but still viable");

5

      return new Object();

6

   }

7

});

用于热修复

使用的前提是,我们需先创建一个热修复工程,打包后在我们项目中动态下载下来。(当然,因为里面基本没什么逻辑,所以包很小,不用担心网络和下载时间的影响)

补丁项目参考:dexposed/sample/patchsample

This project can generate an apk which will be run by dexposedexamples.

The patchloader.jar is export from从...导出的 dexposedexamples "com.taobao.patch" package.

原始项目参考:dexposed/sample/dexposedexamples

原始项目修复bug的核心逻辑:

File cacheDir = getExternalCacheDir();
if (cacheDir != null) {
   String fullpath = cacheDir.getAbsolutePath() + File.separator + "patch.apk";//这个是我们下载到本地的热修复包
   PatchResult result = PatchMain.load(this, fullpath, null);
   if (result.isSuccess()) {
      Log.i("bqt", "hotPath load apk success.");
   } else {
      Log.i("bqt", "hotPath load apk error.  " + result.getErrorInfo());
   }
}

10

1

File cacheDir = getExternalCacheDir();

2

if (cacheDir != null) {

3

   String fullpath = cacheDir.getAbsolutePath() + File.separator + "patch.apk";//这个是我们下载到本地的热修复包

4

   PatchResult result = PatchMain.load(this, fullpath, null);

5

   if (result.isSuccess()) {

6

      Log.i("bqt", "hotPath load apk success.");

7

   } else {

8

      Log.i("bqt", "hotPath load apk error.  " + result.getErrorInfo());

9

   }

10

}

Support

Dexposed support all dalvik runtime arm architecture devices from Android 2.3 to 4.4 (no include 3.0). The stability稳定性 has been proved证实 in our long term product practice实践.

Follow is support status.

Runtime Android Version Support
Dalvik 2.2 Not Test
Dalvik 2.3 Yes
Dalvik 3 No
Dalvik 4.0-4.4 Yes
ART 5 Testing
ART 5.1 No
ART M No

2018-6-9

来自为知笔记(Wiz)

原文地址:https://www.cnblogs.com/baiqiantao/p/9160366.html

时间: 2024-11-10 00:46:44

热更新 阿里的Dexposed的相关文章

移动端热更新方案(iOS+Android)

PPT资源包含iOS+Android 各种方案分析:https://github.com/qiyer/Share/blob/master/%E7%83%AD%E6%9B%B4%E6%96%B0%E5%88%86%E4%BA%ABPPT.pptx 一 .热更新(热修复)产品背景 这里谈到的热更新都是指APP(不包含网页).APP按大类别可以粗略分为 应用 和 游戏.APP的开发周期是极其快速的,在实际开发流程中,我们总会有一些需求迫使我们短时间内快速上线,比如需求流程出错,程序员主观导致的一些bu

RN学习1——前奏,app插件化和热更新的探索

react_native_banner-min.png React Native(以下简称RN)有大量前端开发者的追捧.前端开发是一个活跃的社区,一直尝试着一统前后端,做一个全栈开发,RN就是他们在客户端领域的尝试. 说是从零开始,但其实我还是懂一点点JS代码的,而且算是一个有经验的iOS.Android开发,对很多js和native交互的细节和特性还算了解,在QDaily里面也做过好多hybird的尝试,还经常用JSPatch做hotfix,总的来说,就是对hot update.插件化以及hy

Android 热更新——非侵入AOP框架

Android 客户端应用上线以后,一旦出现Bug,一般的解决思路是发修复包升级应用,这种方式不仅耗时,更重要的是用户需要频繁的升级版本,体验不好,所以优化的思路是在不发版本的情况下热更新,以期提高用户体验. 近期GitHub新出一种非侵入运行期AOP框架Dexposed, 下面简单了解一下这个框架,GitHub地址. 简要说明: 该框架基于AOP思想,支持经典的AOP使用场景,可应用于日志记录,性能统计,安全控制,事务处理,异常处理等方面. 针对Android平台,Dexposed支持函数级别

webpack热更新问题和antd design字体图标库扩展

附一张上周末参加jsconf的照片..... 标题也不知道怎么写好,真是尴尬.不过话说回来,距离上一次写文快两个月了,最近有点忙,一直在开发新项目, 今天刚刚闲下来,项目准备提测.借这个功夫写点东西,把新项目上学到的一些好的干活分享一下,以便之后开发的 时候能烂熟于心. 本次分享两个干货,正文从这里开始: 1.很多同学在开发项目前端项目的时候会遇到一个比较影响开发体验的事情,就是在更新样式文件的时候,页面不会热更新.当然前提是在webpack配置项里用了 'extract-text-webpac

Android 美团Robust热更新 使用入门

Android热更新方案Robust 相信很多人都认识了解过 热修复.热更新.热补丁(对于这个技术也没有特别标准的一种叫法,下面我统一叫热更新),之后的一年里,各种热更新方案如雨后春笋般出现,比较耳熟能详的就有Nuwa.Tinker.Andfix 和 Dexposed 等等,他们之间的区别以及优缺点就不在这里讨论了,鉴于它们的实际使用和局限性,美团的开发团队就脑洞大开了. 就去年 Google 高调发布了 Android Studio 2.0,其中最重要的新特性Instant Run,实现了对代

unity 热更新思路和实现

声明:本文介绍的热更新方案是我在网上搜索到的,然后自己修改了一下,相当于是借鉴了别人的思路,加工成了自己的,在此感谢无私分享经验的朋友们. 想要使用热更新技术,需要规划设计好资源比较策略,资源版本,确保增加新资源后可以下载到本地,有资源更新的时候可以替换掉本地旧资源.我在前面写了一篇"unity 打包AssetBundle"的文章,里面生成了一个资源版本文件,不多解释了,上图.至于怎么生成这个文件的,可以看一下我前面写的文章. 废话不多说. 先介绍热更新步骤,后上代码 步骤一.在Res

JSPatch热更新的利器.

如果用一句话来描述JSPatch,就是利用系统自带的JavaScriptCore.framework配合RunTime机制,进行实时的代码下载与运行.. 而且使用也很简单,启动,加载JS,运行... [JPEngine startEngine]; NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"]; NSString *script = [NSStri

unity 热更新 c# 实战经验分享(二) - 动态Protobuff协议,再也不用担心为了换条协议换包了 ~~!

先提前说明一下,如果哪位小伙伴 觉得生疏的话,可以看这篇博客的前面两个博客,里面讲述了 高大上 JSB,unity 热更新方案,用强大的宇宙第一编辑器vs 来开发 unity 热观更新脚本,更新c# 不在是梦 这次的demo 工程我已经长传了,地址 在这里http://pan.baidu.com/s/1qWQlXUW:下载后(放在D盘 放在D盘 放在D盘!!!!)里面的unity 工程直接打开然后运行main 场景 .就会出现下图,这篇博客唯一 的意义就在于这个demo,实战就是实战不跟你讲废话

unity 热更新 c# 实战经验分享(一)

demo 工程地址 :http://pan.baidu.com/s/1qWBYxqc 我们接上篇 内容继续讲 http://www.cnblogs.com/zyc-it/p/4820810.html (1)工程分离 什么叫工程分离呢,就是要把你的游戏包(后面简称 #主工程#),和你的热更新工程(后面简称 #热更新工程#) 分离开.首先我先说一下 ,我为什么要这么做: 原因:从github 上更新下来的 jsb的工程,把所有的热更新代码写在了unity 里面了,然后在 热更新工程里面 去引用这些c