内容简介:出品 | 滴滴技术作者 | 江义旺
出品 | 滴滴技术
作者 | 江义旺
▍前言 近日,滴滴发布的开源项目 DroidAssist ,提供了一种简单易用、无侵入、配置化、轻量级的 Java 字节码操作方式,只需要在 XML 配置中添加简单的 Java 代码即可实现编译期对 Class 文件的动态修改。
DroidAssist 和其他 AOP 方案不同,它提供了一种简单易用、无侵入、配置化、轻量级的 Java 字节码操作方式,你不需要 Java 字节码的相关知识,只需要在 XML 配置中添加简单的 Java 代码即可实现编译期对 class 文件的动态修改,同时不需要引入其他额外的依赖。
▍起源
作为大型 APP 的代表,滴滴出行乘客端集成了较多的业务线,包含了大量的依赖库,每个版本都有多个团队向乘客端集成大量的代码,而且这些代码都是难以直接追溯到源码的,同时乘客端还有用户量大,日活高,迭代快等特点,这些情况对乘客端的开发和维护形成很大的挑战,主要体现在:问题防范难度大、问题规模大、后期维护成本高。
2018年5月,乘客端团队进行卡顿专项优化, 其中有个问题是:由于安卓系统 SharedPreferences自身机制,当频繁调用 SharedPreferences.apply() 方法时,可能会出现由 QueuedWork.waitToFinish() 造成的卡顿和 ANR。主要原因是系统在 Activity 的 onPause、onStop,以及 Service 的 start 和 stop 生命周期时会执行阻塞等待 QueuedWork 清空,推测系统是为了保证持久化成功率,从而确保用户离开组件之前完成 SharedPreferences 的文件写入。
分析原因之后,我们认为,乘客端 APP 相对处于单一的进程环境,去掉这个持久化阻塞也是可以的。为了解决这个问题,我们决定对系统的 SharedPreferences 进行改造,实现我们自己的 SharedPreferences。
但是随之而来的问题是,我们自定义的 SharedPreferences 怎么以最小的成本接入到乘客端呢?很容易想到以下两种方案:
-
修改所有调用 Context.getSharedPreferences() 的代码,返回我们自己的 SharedPreferences 对象,缺点:改动太多,工作量太大,修改、还原成本太高。
-
所有的 Application、Activity、Service 类都从统一的的 Base 基类派生,在基类中重写 getSharedPreferences 方法返回自定义 SharedPreferences 对象,和方法一相比,此方法代码改动较小,但是也存在是无法修改第三方库,而且工作量也比较大,修改、还原成本也很高的问题。
以上两种方式都具有较大的侵入性,会涉及到大量的源码以及依赖库的代码改动,后期维护和升级成本也比较高,为了寻找更加理想的解决方案,我们希望找到一种无侵入的 Mock 工具,能做到不修改代码就能 Mock 所有 getSharedPreferences()方法的调用返回结果,初步有如下两种实现思路:
-
Hook :Hook 技术需要一直处理各种厂商和机型的兼容性问题,有较大的稳定性风险。
-
AOP :AOP 类框架在编译期实现字节码操作,比较成熟稳定,可以考虑采用,但是经过分析发现,现有的 AOP 框架包括 AspectJ 并不能实现我们需要的 Mock 功能。
类似 SharedPreferences 替换这样的需求还有很多,于是我们决定自己开发一个Android 平台 Mock 工具,经过调研之后,我们确定了字节码修改的技术方向,通过修改字节码实现这样的需求,由此 DroidAssist 应运而生。
项目地址: github.com/didi/DroidA…
▍ 示例
下面例子是背景中提到的 SharedPreferences 改造,添加如下 DroidAssist 配置,在项目编译后,所有调用Context.getSharedPreferences() 的代码,将全部会被修改为返回自定义的 SharedPreferences 实例的代码:
1 <Replace> 2 <MethodCall> 3DroidAssist 4<Source>android.content.SharedPreferences android.content.Context.getS 5haredPreferences(java.lang.String,int)</Source> 6 <Target>{$_= com.didi.quicksilver.QuicksilverPreferencesHelper.getShar 7edPreferences($0,$$);}</Target> 8 </MethodCall> 9</Replace> 复制代码
处理前的 class:
1public class MainActivity extends Activity { 2@Override 3 protected void onCreate(Bundle savedInstanceState) { 4 super.onCreate(savedInstanceState); 5 SharedPreferences sp = getSharedPreferences("test", MODE_PRIVATE); 6} } 复制代码
处理后的 class:
1public class MainActivity extends Activity { 2 protected void onCreate(Bundle savedInstanceState) { 3 super.onCreate(savedInstanceState); 4 SharedPreferences sp = PreferencesHelper.getSharedPreferences(this 5, "test", MODE_PRIVATE); // The target method return custom SharedPreferen 6ces. 7} } 复制代码
具体的使用方式及原理可参见 DroidAssist WIKI 。
▍特性
经过不断的打磨完善,DroidAssist 已经从最开始的 Mock 工具扩展成为具有完整 AOP 框架功能的工具,有如下特性。
▍简单易用
采用灵活的配置化方式,使用者只需要依赖一个插件,然后在配置文件中定义字节码处理方式,DroidAssist 就可以根据配置文件处理项目中所有的 class 文件。处理过程以及处理后的代码中都不需要添加额外的依赖,并且不会修改原始代码行号。
▍丰富的字节码处理功能
除了解决我们最初遇到的代码替换问题外,还扩展了其他的 AOP 功能,目前有 4 类 28 种代码修改方式。
-
替换 :把指定位置代码替换为指定代码
-
插入 :在指定位置的前后插入指定代码
-
环绕 :在指定位置环绕插入指定代码
-
增强
-
TryCatch 对指定代码添加 try catch 代码
-
Timing 对指定代码添加耗时统计代码
▍ 简单易用
支持增量构建,处理速度快,只占用很少的构建时间。
▍Q&A
1. DroidAssist 可以实现什么功能?
DroidAssist 可以轻易实现诸如代码替换,代码插入等功能,滴滴出行 APP 利用 DroidAssist 实现了日志输出替换,系统 SharedPreferences 替换,SharedPreferences commit 替换为 apply,Dialog 展示保护,getDeviceId 接口替换,getPackageInfo 接口替换,getSystemService 接口替换,startActivity 保护,匿名线程重命名,线程池创建监控,主线程卡顿监控,文件夹创建监控,Activity 生命周期耗时统计,APP启动耗时统计等功能。
2. DroidAssist 和 AspectJ 有什么区别?
DroidAssist 采用配置化方案,编写相关配置就可以实现 AOP 的功能,可以完全不用修改 Java 代码;DroidAssist 在使用上使用比较简单,不需要复杂的注解配置;DroidAssist 可以比较方便的实现 AspectJ 不容易实现的代码替换功能。一般情况下使用 DroidAssist 可以完成大部分功能,较复杂情况可以和 AspectJ 配合使用。
有关安装、使用过程以及常见问题解答,请查看以下链接:
GitHub: github.com/didi/DroidA…
Wiki: github.com/didi/DroidA…
同时欢迎加入 「DroidAssist 用户交流群」
请在滴滴技术公众号后台回复 「DroidAssist」 即可加入
▍END
以上所述就是小编给大家介绍的《滴滴开源 DroidAssist : 轻量级 Android 字节码编辑插件》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
数据结构与算法分析
韦斯 (Mark Allen Weiss) / 机械工业出版社 / 2009-1-1 / 55.00元
本书是国外数据结构与算法分析方面的经典教材,使用卓越的Java编程语言作为实现工具讨论了数据结构(组织大量数据的方法)和算法分析(对算法运行时间的估计)。 随着计算机速度的不断增加和功能的日益强大,人们对有效编程和算法分析的要求也不断增长。本书把算法分析与最有效率的Java程序的开发有机地结合起来,深入分析每种算法,内容全面、缜密严格,并细致讲解精心构造程序的方法。一起来看看 《数据结构与算法分析》 这本书的介绍吧!