版权声明:本文为博主原创文章,未经博主容许不得转载。html
转载请代表出处:http://www.cnblogs.com/cavalier-/p/8888459.htmljava
AOP是 Aspect Oriented Programming 的缩写,即面向切面编程,和日常遇到的面向对象OOP编程不同的是,OOP是将功能模块化对象化,AOP是针对同一类的问题统一化处理。例如作日志埋点,性能监控,动态权限控制等。android
AspectJ其实是对AOP编程的实践,目前还有不少的AOP实现,如ASMDex,但笔者选用的是AspectJ。git
若是使用原生AspectJ在项目中配置会很是麻烦,在GitHub上有个开源的SDK gradle_plugin_android_aspectjx基于gradle配置便可。github
请自行查看开源项目中的接入配置过程编程
Join Points在AspectJ中是关键的概念。Join Points能够看作是程序运行时的一个执行点,好比:一个函数的调用能够看作是个Join Points,至关于代码切入点。但在AspectJ中,只有下面几种执行点是认为是Join Points:app
Join Points | 说明 | 实例 |
---|---|---|
method call | 函数调用 | 好比调用Log.e(),这是一个个Join Point |
method execution | 函数执行 | 好比Log.e()的执行内部,是一处Join Points。注意这里是函数内部 |
constructor call | 构造函数调用 | 和method call 相似 |
constructor execution | 构造函数执行 | 和method execution 相似 |
field get | 获取某个变量 | 好比读取DemoActivity.debug成员 |
field set | 设置某个变量 | 好比设置DemoActivity.debug成员 |
pre-initialization | Object在构造函数中作的一些工做。 | - |
initialization | Object在构造函数中作的工做。 | - |
static initialization | 类初始化 | 好比类的static{} |
handler | 异常处理 | 好比try catch 中,对应catch内的执行 |
advice execution | 这个是AspectJ 的内容 | - |
一个程序会有多个Join Points,即便同一个函数,也还分为call 和 execution 类型的Join Points,但并非全部的Join Points 都是咱们关心的,Pointcuts 就是提供一种使得开发者可以值选择所需的JoinPoints的方法。ide
Advice就是咱们插入的代码能够以何种方式插入,有Before 还有 After、Around。
下面看个例子:模块化
@Before(“execution(* android.app.Activity.on**(..)))”) public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable{ }
这里会分红好几个部分,咱们依次来看:函数
(* android.app.Activity.on**(..))
: 这个是最重要的表达式,第一个*
表示返回值,*
表示返回值为任意类型,后面这个就是典型的包名路径,其中能够包含 *
来进行通配,几个 *
没有区别。同时这里能够经过&&、||、!
来进行条件组合。()表明这个方法的参数,你能够指定类型,例如android.os.Bundle,或者 (..) 这样来表明任意类型、任意个数的参数。Before 和 After 其实仍是很好理解的,也就是在Pointcuts以前和以后,插入代码,那么Android呢,从字面含义上来说,也就是在方法先后各插入代码,他包含了 Before和 After 的所有功能,代码以下:
@(“execution(* com.xys.aspectjxdemo.MainActivity.testAOP()))”) public void onActivityMethodAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ String key = proceedingJoinPoint.getSignature().toString(); Log.d(TAG,”onActivityMethodAroundFirst:”+key); proceedingJoinPoint.proceed(); Log.d(TAG,”onActivityMethodAroundSecond:”+key); }
以上代码中,proceedingJoinPoint.proceed()表明执行原始的方法,在这以前、以后,均可以进行各类逻辑处理。
自定义Pointcuts可让咱们更加精准的切入一个或多个指定的切入点。
首先咱们要定义一个注解类
@Retention(RetentionPolicy.CLASS) @Target({ElementType.CONSTRUCTOR, ElementType.METHOD}) public @interface DebugTrace { }
在须要插入代码的地方加入这个注解,例如在MainActivity中加入:
public class MainActivity extends AppCompatActivity{ final String TAG = MainActivity.class.getSimpleName(); @Override protedcted void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); logTest(); } @DebugTrace public void logTest(){ Log.e(TAG,”log test"); } }
最后建立切入代码
@Pointcut(“execution(@com.kun.aspectjtest.aspect.DebugTrace * *..*.*(..))”) public void DebugTraceMethod(){} @Before(“DebugTraceMethod()”) public void beforeDebugTraceMethod(JoinPoint joinPoint) throws Throwable{ String key = joinPoint.getSignature().toString(); Log.e(TAG, “beforeDebugTraceMethod:”+key); }
在AspectJ的切入点表达式中,咱们前面都是使用的execution,实际上还有一种类型—call,那么这两种语法有什么区别呢?对call来讲:
Call (Before) Pointcut{ Pointcut Method } Call (After)
对Execution来讲:
Pointcut{ execution (Before) Pointcut Method execution (After) }
这个语法一般来进行一些切入点条件的过滤,做更加精确的切入控制,以下:
public class MainActivity extends AppCompatActivity{ final String TAG = MainActivity.class.getSimpleName(); @Orveride protected void onCreate(Bundle savedInstanceState){ super.onCreate(saveInstanceState); setContentView(R.layout.activity_main); aspectJ1(); aspectJ2(); aspectJ3(); } public void aspectJTest(){ Log.e(TAG,”execute aspectJTest"); } public void aspectJ1(){ aspectJTest(); } public void aspectJ2(){ aspectJTest(); } public void aspectJ3(){ aspectJTest(); } }
aspectJ1(),aspectJ2(),aspectJ3()都调用了aspectJTest方法,但只想在aspectJ2调用aspectJTest时插入代码,这个时候就须要使用到Pointcut和withcode组合的方式,来精肯定位切入点。
@Pointcut(“(call(* *..aspectJTest()))&&withincode(* *..aspectJ2())”) public void invokeAspectJTestInAspectJ2(){ } @Before(“invokeAspectJTestInAspectJ2()”) public void beforeInvokeaspectJTestInAspectJ2(JoinPoint joinPoint) throws Throwable{ Log.e(TAG,”method:”+getMethodName(joinPoint).getName()); } private MethodSignature getMethodName(JoinPoint joinPoint){ if(joinPoint == null) return null; return (MethodSignature) joinPoint.getSignature(); }
execution()是最经常使用的切点函数,其语法以下所示:
例以下面这段语法:
@Around(“execution(* *..MainActivity+.on*(..))")
整个表达式能够分为五个部分:
*
号表明返回类型,*号表明全部的类型。*(..)
最后这个星号表示方法名,+.表明具体的函数名,*
号通配符,包括括弧号里面表示方法的参数,两个dot表明任意参数。Error:Execution failed for task ':app:transformClassesWithDexBuilderForDebug'. > Unexpected scopes found in folder '/Users/ram/WorkSpace/AndroidWorkSpace/MyDemo/app/build/intermediates/transforms/AspectTransform/debug'. Required: PROJECT, SUB_PROJECTS, EXTERNAL_LIBRARIES. Found: EXTERNAL_LIBRARIES, PROJECT, PROJECT_LOCAL_DEPS, SUB_PROJECTS, SUB_PROJECTS_LOCAL_DEPS