Android AOP介绍及实现原理

l 第一,我们要认识到OOP世界中,有些功能是横跨并嵌入众多模块里的,比如打印日志,比如统计某个模块中某些函数的执行时间等。这些功能在各个模块里分散得很厉害,可能到处都能见到。

l 第二,AOP的目标是把这些功能集中起来,放到一个统一的地方来控制和管理。如果说,OOP如果是把问题划分到单个模块的话,那么AOP就是把涉及到众多模块的某一类问题进行统一管理。比如我们可以设计两个Aspects,一个是管理某个软件中所有模块的日志输出的功能,另外一个是管理该软件中一些特殊函数调用的权限检查。

二、没有AOP的例子


先来看没有AOP的情况下,代码怎么写。

public class AopDemoActivity extends Activity {

private static final String TAG = “AopDemoActivity”;

onCreate,onStart,onRestart,onPause,onResume,onStop,onDestory返回前,都输出一行日志

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.layout_main);

Log.e(TAG,“onCreate”);

}

protected void onStart() {

super.onStart();

Log.e(TAG, “onStart”);

}

protected void onRestart() {

super.onRestart();

Log.e(TAG, “onRestart”);

}

protectedvoid onResume() {

super.onResume();

Log.e(TAG, “onResume”);

checkPhoneState会检查app是否申明了android.permission.READ_PHONE_STATE权限

checkPhoneState();

}

protected void onPause() {

super.onPause();

Log.e(TAG, “onPause”);

}

protected void onStop() {

super.onStop();

Log.e(TAG, “onStop”);

}

protected void onDestroy() {

super.onDestroy();

Log.e(TAG, “onDestroy”);

}

private void checkPhoneState(){

if(checkPermission(“android.permission.READ_PHONE_STATE”)== false){

Log.e(TAG,“have no permission to read phone state”);

return;

}

Log.e(TAG,“Read Phone State succeed”);

return;

}

private boolean checkPermission(String permissionName){

try{

PackageManager pm = getPackageManager();

//调用PackageMangaer的checkPermission函数,检查自己是否申明使用某权限

int nret = pm.checkPermission(permissionName,getPackageName());

return nret == PackageManager.PERMISSION_GRANTED;

}…

}

}

代码很简单。但是从这个小例子中,你也会发现要是这个程序比较复杂的话,到处都加Log,或者在某些特殊函数加权限检查的代码,真的是一件挺繁琐的事情。

三、使用AspectJ在Android中实现Aop


AOP虽然是方法论,但就好像OOP中的Java一样,一些先行者也开发了一套语言来支持AOP。目前用得比较火的就是AspectJ了,它是一种几乎和Java完全一样的语言,而且完全兼容Java(AspectJ应该就是一种扩展Java,但它不是像Groovy[1]那样的拓展。)。当然,除了使用AspectJ特殊的语言外,AspectJ还支持原生的Java,只要加上对应的AspectJ注解就好。

用AspectJ的方法很简单,首先是定义一个DebugLog注解:

import java.lang.annotation.Retention;

import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.CONSTRUCTOR;

import static java.lang.annotation.ElementType.METHOD;

import static java.lang.annotation.ElementType.TYPE;

import static java.lang.annotation.RetentionPolicy.CLASS;

@Target({TYPE, METHOD, CONSTRUCTOR}) @Retention(CLASS)

public @interface DebugLog {

}

接下来就是使用AspectJ的方式,其设置PointCut:

@Aspect

public class Hugo {

//带有DebugLog注解的所有类

@Pointcut(“within(@com.example.aoplib.DebugLog *)”)

public void withinAnnotatedClass() {}

//在带有DebugLog注解的所有类,除去synthetic修饰的方法

@Pointcut(“execution(!synthetic * *(…)) && withinAnnotatedClass()”)

public void methodInsideAnnotatedType() {}

//在带有DebugLog注解的所有类,除去synthetic修饰的构造方法

@Pointcut(“execution(!synthetic *.new(…)) && withinAnnotatedClass()”)

public void constructorInsideAnnotatedType() {}

//在带有DebugLog注解的方法

@Pointcut(“execution(@com.example.aoplib.DebugLog * *(…)) || methodInsideAnnotatedType()”)

public void method() {}

//在带有DebugLog注解的构造方法

@Pointcut(“execution(@com.example.aoplib.DebugLog *.new(…)) || constructorInsideAnnotatedType()”)

public void constructor() {}

}

上面代码的注释中,已经说明了每个PointCut的意义,也就是说,Aspect

J会去寻找带有@DebugLog注解的类,或者带有@DebugLog注解的方法和构造方法。

接着是定义Advice:

@Around(“method() || constructor()”)

public Object logAndExecute(ProceedingJoinPoint joinPoint) throws Throwable {

//执行方法前,做些什么

enterMethod(joinPoint);

long startNanos = System.nanoTime();

//执行原方法

Object result = joinPoint.proceed();

long stopNanos = System.nanoTime();

long lengthMillis = TimeUnit.NANOSECONDS.toMillis(stopNanos - startNanos);

//执行方法后,做些什么

exitMethod(joinPoint, result, lengthMillis);

return result;

}

这些代码,和Hugo中的一模一样,我将其抽取,构成一个简单的library,叫做aoplib,看下图:

这里写图片描述

四、Android AOP实现原理全解析


我们本博客的重点是了解清楚AOP的整个实现流程,是直接使用的别人的代码,代码也是在别人的博客中直接下载的,地址如下:

使用AspectJ在Android中实现Aop

Activity就一个,运行时就直接调用TestMain.TestAll()日志打印。我们先来看一下作者分module的用意。整个project分为aoplib、app、buildsrc、libinlib、testlib五个module,各module的意思应该也比较清楚,aoplib就是实现AOP功能的模块,app是本项目的启动模块,buildsrc是用来构建项目的模块,libinlib中作者只提供了一个TestLog类,而且只有一个方法,目的是用来测试AOP功能的,最后一个testlib是作者实现自己意图的模块,所有的测试类都是在这里的。我们要分析的重点就是aoplib和testlib这两个module了。

我们从程序的运行过程来一步步分析,首先看一下MainActivity类的onCreate方法,请注意,作者为这个方法上加了一个@DebugLog注解,@DebugLog注解是自己实现的,实现代码如下,非常简单:

package com.example.aoplib;

import java.lang.annotation.Retention;

import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.CONSTRUCTOR;

import static java.lang.annotation.ElementType.METHOD;

import static java.lang.annotation.ElementType.TYPE;

import static java.lang.annotation.RetentionPolicy.CLASS;

@Target({TYPE, METHOD, CONSTRUCTOR})

@Retention(CLASS)

public @interface DebugLog {

}

从这个注释接口的定义上,我们可以看到,它的目标是使用在TYPE(接口、类、枚举、注解),METHOD(方法),CONSTRUCTOR(构造函数)三种类型上的,当前就是使用在MainActivity的onCreate方法上的。我们可以来看一下使用Aspect编译后的MainActivity的class文件:

这里写图片描述

从这里大家可以非常清楚的看到,MainActivity的onCreate方法已经被替换了,它是按照Aspect编译规则生成的,我们可以再来看看其他带有@DebugLog注解的类或者方法对应生成的class文件,比如TestMethodClass类的class文件,整个文件代码如下:

public class TestMethodClass {

public TestMethodClass() {

(new Thread() {

@DebugLog

public void run() {

JoinPoint var2 = Factory.makeJP(ajc$tjp_0, this, this);

Hugo var10000 = Hugo.aspectOf();

Object[] var3 = new Object[]{this, var2};

var10000.logAndExecute((new TestMethodClass 1 1 1AjcClosure1(var3)).linkClosureAndJoinPoint(69648));

}

static {

ajc$preClinit();

}

}).start();

}

@DebugLog

public void spendTime1ms() {

JoinPoint var2 = Factory.makeJP(ajc$tjp_0, this, this);

Hugo var10000 = Hugo.aspectOf();

Object[] var3 = new Object[]{this, var2};

var10000.logAndExecute((new TestMethodClass$AjcClosure1(var3)).linkClosureAndJoinPoint(69648));

}

@DebugLog

public static void spendTime2ms() {

JoinPoint var1 = Factory.makeJP(ajc$tjp_1, (Object)null, (Object)null);

Hugo var10000 = Hugo.aspectOf();

Object[] var2 = new Object[]{var1};

var10000.logAndExecute((new TestMethodClass$AjcClosure3(var2)).linkClosureAndJoinPoint(65536));

}

@DebugLog

public final void spendTime3ms() {

JoinPoint var2 = Factory.makeJP(ajc$tjp_2, this, this);

Hugo var10000 = Hugo.aspectOf();

Object[] var3 = new Object[]{this, var2};

var10000.logAndExecute((new TestMethodClass$AjcClosure5(var3)).linkClosureAndJoinPoint(69648));

}

static {

ajc$preClinit();

}

}

在这个class文件中,我们可以看到,run()、spendTime1ms()、spendTime2ms()、spendTime3ms()几个方法全部都是这样样式,每个方法一共四句,第一句,构建一个切点JoinPoint,第二句调用Hugo.aspectOf()获取我们自己定义的Aspect处理类,第三句构造参数数组Object[],第四句调用当前类的相应方法。看到这里我们基本就明白AOP的原理了,它就是利用我们自己实现的一个注解,将所有的切点集中在一个地方处理的,这样,就可以把多个切点放在一起统一处理了,非常的方便!

下面我们就来分析一个方法的执行过程,此项目中其他方法的实现是完全一样的,我们就以TestMethodClass类的spendTime1ms()为例来展开我们的分析,在编译完的class文件中,首先构造一个JoinPoint切点,Factory.makeJP()方法的实现就是使用传入的参数直接构造一个JoinPointImpl对象,第二句就是获取当前的Aspect处理类对象Hugo,此类必须要带有@Aspect注解,第三句就是构造方法执行的数组对象,第四句执行Aspect处理类的入口方法logAndExecute,Hugo类的完整代码如下:

@Aspect

public class Hugo {

@Pointcut(“within(@com.example.aoplib.DebugLog *)”)

public void withinAnnotatedClass() {}

@Pointcut(“execution(!synthetic * *(…)) && withinAnnotatedClass()”)

public void methodInsideAnnotatedType() {}

@Pointcut(“execution(!synthetic *.new(…)) && withinAnnotatedClass()”)

public void constructorInsideAnnotatedType() {}

@Pointcut(“execution(@com.example.aoplib.DebugLog * *(…)) || methodInsideAnnotatedType()”)

public void method() {}

@Pointcut(“execution(@com.example.aoplib.DebugLog *.new(…)) || constructorInsideAnnotatedType()”)

public void constructor() {}

@Around(“method() || constructor()”)

public Object logAndExecute(ProceedingJoinPoint joinPoint) throws Throwable {

enterMethod(joinPoint);

long startNanos = System.nanoTime();

Object result = joinPoint.proceed();

long stopNanos = System.nanoTime();

long lengthMillis = TimeUnit.NANOSECONDS.toMillis(stopNanos - startNanos);

exitMethod(joinPoint, result, lengthMillis);

return result;

}

private static void enterMethod(JoinPoint joinPoint) {

CodeSignature codeSignature = (CodeSignature) joinPoint.getSignature();

Class<?> cls = codeSignature.getDeclaringType();

String methodName = codeSignature.getName();

String[] parameterNames = codeSignature.getParameterNames();

Object[] parameterValues = joinPoint.getArgs();

StringBuilder builder = new StringBuilder("\u21E2 ");

builder.append(methodName).append(‘(’);

for (int i = 0; i < parameterValues.length; i++) {

if (i > 0) {

builder.append(", ");

}

builder.append(parameterNames[i]).append(‘=’);

builder.append(Strings.toString(parameterValues[i]));

}

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取
d(‘(’);

for (int i = 0; i < parameterValues.length; i++) {

if (i > 0) {

builder.append(", ");

}

builder.append(parameterNames[i]).append(‘=’);

builder.append(Strings.toString(parameterValues[i]));

}

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

[外链图片转存中…(img-oFdFA9zW-1718817487075)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值