Android Studio 官方暂时不支持aspectJ,不过这里用的一款gradle插件 aspectjx
配置在project的build.gradle里配置插件dependencies {
classpath 'com.android.tools.build:gradle:2.1.3'
//if i update the gradle plugin version above to 2.2.0,it will runs out with a problerm
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:1.0.7'
}在主module中添加 apply plugin:'android-aspectjx',在android闭包中添加aspectjx {
//指定只对含有关键字'DataBindingDemo/aspectjlibrary'的库进行织入扫描,忽略其他库,提升编译效率
includeJarFilter 'DataBindingDemo/aspectjlibrary'
//排除对universal-image-loader'库的织入扫描
// excludeJarFilter 'universal-image-loader'
}在使用了aspectj 的module中添加一下依赖:compile 'org.aspectj:aspectjrt:1.8.9'
应用
aspect 主要用于AOP编程,应用在日志,监控,性能检查方面,并可以做到无侵入。
首先是3个概念:
JoinPoint :表示程序运行过程中的一些执行点,包括函数调用,函数内部执行等。。。
PointCut : 用于定位到指定的JoinPoint
Advice : 要织入到指定位置的代码片段
新建一个Module,专门用于编写Aspectj代码,将该Module申明成android library,主Module中添加该依赖库。
新建一个类 ActivityAspect,在类名上添加注解@Aspect。声明一个PointCut:public static final String MainMethod = "* com.github.huangyouqiang.aspectjlibrary.databindingdemo.MainActivity.*(..)"
@Pointcut("execution(MainMethod)")
public void mainmethod(){}
其中MainMethod表示匹配MainActivity中的所有方法。具体语法如下:Method Signature:
语法: @注解 访问权限 返回值的类型 包名.函数名(参数)
@注解 和 访问权限public/private/protect,以及static/final)属于可选项。如果不设置它们,则默认都会选择。以访问权限 为例,如果没有设置访问权限作为条件,那么public,private,protect及static、final的函数都会进行搜索。
返回值类型就是普通的函数的返回值类型。如果不限定类型的话,就用*通配符表示
Ž 包名.函数名用于查找匹配的函数。可以使用通配符,包括和..以及+号。其中号用于匹配除.号之外的任意字符,而..则表示任 意子package,+号表示子类。
比如:
java.*.Date:可以表示java.sql.Date,也可以表示java.util.Date
Test*:可以表示TestBase,也可以表示TestDervied
java..*:表示java任意子类
java..*Model+:表示Java任意package中名字以Model结尾的子类,比如TabelModel,TreeModel等
最后来看函数的参数。参数匹配比较简单,主要是参数类型,比如:
(int, char):表示参数只有两个,并且第一个参数类型是int,第二个参数类型是char
(String, ..):表示至少有一个参数。并且第一个参数类型是String,后面参数类型不限。在参数匹配中,
..代表任意参数个数和类型
(Object …):表示不定个数的参数,且类型都是Object,这里的…不是通配符,而是Java中代表不定参数的意思
Constructor signature:
和Method Signature类似,只不过构造函数没有返回值,而且函数名必须叫new。比如:
public *..TestDerived.new(..):
Œ public:选择public访问权限
*..代表任意包名
Ž TestDerived.new:代表TestDerived的构造函数
(..):代表参数个数和类型都是任意
Field Signature:
@注解 访问权限 类型 类名.成员变量名
Œ 其中,@注解和访问权限是可选的
类型:成员变量类型,*代表任意类型
Ž 类名.成员变量名:成员变量名可以是*,代表任意成员变量
比如,
set(int test..TestBase.base):表示设置TestBase.base变量时的JPoint
Type Signature:
staticinitialization(test..TestBase):表示TestBase类的static block
handler(NullPointerException):表示catch到NullPointerException的JPoint.
@Pointcut()定义了一个PointCut,注解了一个方法public void mainmethod(){},此处这个方法不需要具体实现,此方法变代表了这个PointCut,下面会用到。execution表示为该匹配为该函数类型的JoinPoint的内部执行。
结合上述的解释,可以得出该PointCut可定位到MainActivity中任一方法的内部执行的类型的JoinPoint。
接下来,编写真正要织入的代码:@Around("method()")
public void onPreMethod(ProceedingJoinPoint joinPoint)throws Throwable{
Log.i(TAG,joinPoint.getSignature().getName()+" -->start");
long startNanos = System.nanoTime();
Object result = joinPoint.proceed();
long stopNanos = System.nanoTime();
long lengthMillis = TimeUnit.NANOSECONDS.toMillis(stopNanos - startNanos);
Log.i(TAG,joinPoint.getSignature().getName()+"-->end execute time total : "+lengthMillis+"ms")
}
这个advice使用了@Around("method()")注解,其中的method()便是上文定义的PointCut,Around,顾名思义,即包围,在这个函数内部执行的前后,该注解修饰的方法就是具体执行的代码,参数为固定的ProceedingJoinPoint,在该函数内部又这么一条Object result = joinPoint.proceed(); 这条表示调用原函数自己的是实现,返回的result也是原函数的返回值,我们就可以在这个函数的前后添加需要的功能了,比如日志记录和耗时计算。如果不调用这一句,则原函数便不会执行了。另外,通过这个JoinPoint参数,可以获取到该方法的签名对象以及该方法的调用者对象,再通过反射,可以实现更多的功能,[email protected],[email protected],@After,[email protected],所以不展开,具体用法一样,只是参数类型变为JoinPoint。
至此,就完成了一个简单的完整的AspectJ应用,可以满足基本的需求了。
上文所述是基于方法的对JoinPoint的代码织入,还有基于构造方法的,基于类的成员变量的。
以下是一个基于成员变量的例子,对该变量的赋值的joinPoint织入:
首先在MainActivit中声明一个变量int i ;,在onCreate()中赋值i = 3;,接下来编写aspectj代码,@Pointcut("set(int com.github.huangyouqiang.aspectjlibrary.databindingdemo.MainActivity.i) && args(i)")
public void seti(int i){}
@Before("seti(i)")
public void beforeI(JoinPoint joinPoint,int i){
FieldSignature signature = (FieldSignature) joinPoint.getSignature();
System.out.println(joinPoint.getStaticPart().getSourceLocation());
System.out.println(signature.getName()+ " = "+i);
}