AOP 是什么?
网上很多人在介绍 AOP 时都这样说: 面向切面编程, 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. 个人认为这句话是错误. AOP 和 OOP 一样, 是一种程序设计思想, 而非技术手段.
程序设计有六大原则, 其中第一原则就是单一职责原则. 意思就是一个类只负责一件事情. 这与 OOP 的封装特性相得益彰. 在这个条件下, 我们的程序会被分散到不同的类, 不同的方法中去. 这样做的好处是降低了类的复杂性, 提高了程序的可维护性. 但是同时, 它也使代码变得啰嗦了. 例如, 我们要为方法添加调用日志, 那就必须为所有类的所有方法添加日志调用, 尽管它们都是相同的. 为了解决上述问题, AOP 应运而生了.
AOP 旨在将横切关注点与业务主体进行分类, 从而提高程序代码的模块化程度. 横切关注点是一个抽象的概念, 它是指那些在项目中贯穿多个模块的业务. 上个例子中日志功能就是一个典型的横切关注点.
AOP 的几种实现方式
动态代理
动态代理是一种设计模式. 它有以下特征:
我们不需要自己写代理类.
运行期通过接口直接生成代理对象.
运行期间才确定代理哪个对象.
以下面这个例子为例, 我们看一下动态代理的类图结构.
通常我们的 App 都有一部分功能要求用户登录之后才能访问. 如修改密码, 修改用户名等功能. 当用户打算使用这些功能时, 我们一般要对用户的登录状态进行判断, 只有用户登录了, 才能正常使用这些功能. 而如果用户未登录, 我们的 App 要跳转到登录页. 就以修改密码为例我们看一下动态代理的类图.
image
InvocationHandler 是 Java JDK 提供的动态代理的入口, 用来对被代理对象的方法做处理.
代码如下:
- public static class LoginCheckHandler implements InvocationHandler {
- private static <S, T extends S> T proxy(S source, Class<T> tClass) {
- return (T) Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{tClass}, new LoginCheckHandler(source));
- }
- private Object mSource;
- LoginCheckHandler(Object source) {
- this.mSource = source;
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- if(!checkLogin()){
- jumpToLoginActivity();
- return null;
- }
- return method.invoke(mSource, args);
- }
- private boolean checkLogin(){
- System.out.println("用户未登录");
- return false;
- }
- private void jumpToLoginActivity(){
- System.out.println("跳转到登录页");
- }
- }
- public class Client {
- public static void main(String[] args) {
- IUserSetting source = new UserSetting();
- IUserSetting iUserSetting = LoginCheckHandler.proxy(source,IUserSetting.class);
- iUserSetting.changePwd("new Password");
- }
- }
经过这样封装之后, 检查登录跳转登录页的逻辑作为横切关注点就和业务主体进行了分离. 当有新的需求需要登录检查时, 我们只需要通过 LoginCheckHandler 生成新的代理对象即可.
APT
APT(Annotation Processing Tool) 是一种编译期注解处理技术. 它通过定义注解和处理器来实现编译期生成代码的功能, 并且将生成的代码和源代码一起编译成. class 文件. 通过 APT 技术, 我们将横切关注点封装到注解处理器中, 从而实现横切关注点与业务主体的分离. 更详细的介绍请移步 Android 编译期插桩, 让程序自己写代码 (一).
AspectJ
AspectJ 就是一种编译器, 它在 Java 编译器的基础上增加了关键字识别和编译方法. 因此, AspectJ 可以编译 Java 代码. 它还提供了 Aspect 程序. 在编译期间, 将开发者编写的 Aspect 程序织入到目标程序中, 扩展目标程序的功能. 开发者通过编写 AspectJ 程序实现 AOP 功能. 更详细的介绍请移步 Android 编译期插桩, 让程序自己写代码 (二).
Transform + Javassist/ASM
Transform 是 Android Gradle 提供的, 可以操作字节码的一种方式. App 编译时, 源代码首先会被编译成 class, 然后再被编译成 dex. 在 class 编译成 dex 的过程中, 会经过一系列 Transform 处理. Javassist/ASM 是一个能够非常方便操作字节码的库. 我们通过它们可以修改编译的. class 文件. 利用这种方式, 我们将横切关注点封装到 Transform, 来达到与业务主体分离的目的. 更详细的介绍请移步 Android 编译期插桩, 让程序自己写代码 (三).
我们可以用来实现什么功能?
利用 AOP 可以做一些很有意思的事情. 一些知名的开源框架, 他们都采用了 AOP 的思想. 例如: ButterKnife,Retrofit,Hugo 等. 另外, AOP 在性能检测和埋点技术上出现了百家争鸣的局面.
性能检测与优化. 360 的 ArgusAPM, 滴滴的 booster,Jake Wharton 大神的 Hugo.
埋点技术. 罗辑思维的 DDAutoTracker, 神策数据的 Sensors Analytics, 网易的 HubbleData 等.
除此之外, 借助 AOP 我们还可以实现以下功能:
通常我们在向服务器请求数据时, 会显示一个 Loding, 等到结果返回后再隐藏它. 我们可以通过 AOP 技术把显示, 隐藏 Loding 的动作和业务主体分离开.
权限管理. 给大家推荐一个开源库 Aopermission, 用 AspectJ 解决了权限问题.
更多 Android 开发资料 + 面试架构资料 免费分享 点击链接 即可领取
《Android 架构师必备学习资源免费领取 (架构视频 + 面试专题文档 + 学习笔记)》
来源: http://www.jianshu.com/p/bf0d19dd51cf