赞
踩
什么是AOP
AOP是 Aspect Oriented Programming 的缩写,即面向切面编程,和日常遇到的面向对象OOP编程不同的是,OOP是将功能模块化对象化,AOP是针对同一类的问题统一化处理。例如作日志埋点,性能监控,动态权限控制等。android
AspectJ
AspectJ其实是对AOP编程的实践,目前还有不少的AOP实现,如ASMDex,但笔者选用的是AspectJ。git
在Android项目中使用AspectJ
若是使用原生AspectJ在项目中配置会很是麻烦,在GitHub上有个开源的SDK gradle_plugin_android_aspectjx基于gradle配置便可。github
接入说明
请自行查看开源项目中的接入配置过程编程
AspectJ 之 Join Points介绍
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 的内容
-
Pointcuts 介绍
一个程序会有多个Join Points,即便同一个函数,也还分为call 和 execution 类型的Join Points,但并非全部的Join Points 都是咱们关心的,Pointcuts 就是提供一种使得开发者可以值选择所需的JoinPoints的方法。ide
Advice
Advice就是咱们插入的代码能够以何种方式插入,有Before 还有 After、Around。 下面看个例子:模块化
@Before(“execution(* android.app.Activity.on**(..)))”)
public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable{
}
这里会分红好几个部分,咱们依次来看:函数
@Before: Advice, 也就是具体的插入点
execution:处理Join Point的类型,例如call、execution
(* android.app.Activity.on**(..)): 这个是最重要的表达式,第一个*表示返回值,*表示返回值为任意类型,后面这个就是典型的包名路径,其中能够包含 *来进行通配,几个 *没有区别。同时这里能够经过&&、||、!来进行条件组合。()表明这个方法的参数,你能够指定类型,例如android.os.Bundle,或者 (..) 这样来表明任意类型、任意个数的参数。
public void onActivityMehodBefore: 实际切入的代码。
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
自定义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);
}
Call
在AspectJ的切入点表达式中,咱们前面都是使用的execution,实际上还有一种类型—call,那么这两种语法有什么区别呢?对call来讲:
Call (Before)
Pointcut{
Pointcut Method
}
Call (After)
对Execution来讲:
Pointcut{
execution (Before)
Pointcut Method
execution (After)
}
Withincode
这个语法一般来进行一些切入点条件的过滤,做更加精确的切入控制,以下:
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 语法
execution()是最经常使用的切点函数,其语法以下所示: 例以下面这段语法: @Around(“execution(* *..MainActivity+.on*(..))") 整个表达式能够分为五个部分:
execution()是表达式主体
第一个*号表明返回类型,*号表明全部的类型。
包名 表示须要拦截的包名,这里使用*.表明匹配全部的包名。
第二个*号表示类名,后面跟.MainActivity是指具体的类名叫MainActivity。
*(..) 最后这个星号表示方法名,+.表明具体的函数名,*号通配符,包括括弧号里面表示方法的参数,两个dot表明任意参数。
遇到的错误
如下错误可使用gradle2.2.3解决,因为目前还不适配gradle3.0致使的
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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。