一、使用
添加依赖,配置build脚本
//1.全局
dependencies {
classpath 'org.aspectj:aspectjtools:1.8.9' //aspectJ
classpath 'org.aspectj:aspectjweaver:1.8.9'
}
//2.模块中
dependencies{
api 'org.aspectj:aspectjrt:1.8.9'
}
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
final def log = project.logger
final def variants = project.android.applicationVariants
variants.all { variant ->
if (!variant.buildType.isDebuggable()) {
log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
return;
}
JavaCompile javaCompile = variant.javaCompile
javaCompile.doLast {
String[] args = ["-showWeaveInfo",
"-1.5",
"-inpath", javaCompile.destinationDir.toString(),
"-aspectpath", javaCompile.classpath.asPath,
"-d", javaCompile.destinationDir.toString(),
"-classpath", javaCompile.classpath.asPath,
"-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
log.debug "ajc args: " + Arrays.toString(args)
MessageHandler handler = new MessageHandler(true);
new Main().run(args, handler);
for (IMessage message : handler.getMessages(null, true)) {
switch (message.getKind()) {
case IMessage.ABORT:
case IMessage.ERROR:
case IMessage.FAIL:
log.error message.message, message.thrown
break;
case IMessage.WARNING:
log.warn message.message, message.thrown
break;
case IMessage.INFO:
log.info message.message, message.thrown
break;
case IMessage.DEBUG:
log.debug message.message, message.thrown
break;
}
}
}
}
实现切入
//1.注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LoginFilter {
int loginDefine() default 0;
}
//2.对应的Aspect处理业务类,可以简单理解为拦截后怎么处理,放行或者其他等等
@Aspect
public class LoginFilterAspect {
private static final String TAG = "LoginFilterAspect";
//告诉代码注入工具,在LoginFilter的所有方法注入该userIntercept方法
@Pointcut("execution(@com.example.dailytest.filter.LoginFilter * *(..))")
public void userIntercept(){
}
//在activity的所有on开头的方法执行后执行该方法
@After("execution(* android.app.Activity.on**(..))")
public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable{
String key = joinPoint.getSignature().toString();
Log.d(TAG,"onActivityMethodBefore " + key +"\n"+joinPoint.getThis());
}
//在方法前各插入代码,也就是在proceed()放行前、放行后都可以处理一些逻辑
@Around("userIntercept()&&@annotation(userFilter)")
public void userFilter(ProceedingJoinPoint joinPoint, LoginFilter userFilter) throws Throwable{
Log.d(TAG,"userFilter " + joinPoint.getSignature().toString() +"\n"+joinPoint.getThis());
Log.d(TAG,"userFilter " + userFilter.loginDefine() +"\n"+joinPoint.getThis());
//放行
joinPoint.proceed();
//放行后
Log.i(TAG,"放行后执行的操作");
}
}
//3.简单操作,运行
public class MainActivity extends AppCompatActivity {
private static final String TAG = "LoginFilterAspect";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
test();
}
@LoginFilter(loginDefine = 1)
private void test() {
Log.d(TAG, "test");
}
}
二、注解含义及原理分析
基本概念
注解
@Aspect 表示这是一个切面类
@Before 表示可以在方法前插入代码
@After 表示可以在方法后插入代码
@Around 表示可以在方法前,方法后插入代码
execution(表达式) 如 execution(* android.app.Activity.on**(…)) 第一个表示返回值, * 是通配符(任意类型),可以通过&&、||、!来进行条件组合,第二个中(…)代表任意个数任意类型参数,可以指定。例如execution(@com.example.dailytest.filter.LoginFilter * *(…))") 指定返回值是LoginFilter,方法和参数任意
再例如 execution(* *..Activity+.*(..) || execution(* * *..Fragment+.*(..) ) )
截取任何包中任何Activitiy和Fragment结尾的所有方法
call / execution 区别
Call(Before)
Pontcut{
Pointcut Method
}
Call(After)
Pointcut{
Execution(Before)
Pointcut Method
Execution(After)
}
接下来分析一下实现切入,这个过程是怎么做到切入的
MainActivity.class源码
public class MainActivity extends AppCompatActivity {
private static final String TAG = "LoginFilterAspect";
public MainActivity() {
}
protected void onCreate(Bundle savedInstanceState) {
JoinPoint var2 = Factory.makeJP(ajc$tjp_0, this, this, savedInstanceState);
try {
super.onCreate(savedInstanceState);
this.setContentView(2131296284);
this.test();
} catch (Throwable var5) {
LoginFilterAspect.aspectOf().onActivityMethodBefore(var2);
throw var5;
}
LoginFilterAspect.aspectOf().onActivityMethodBefore(var2);
}
@LoginFilter(
loginDefine = 1
)
private void test() {
JoinPoint var1 = Factory.makeJP(ajc$tjp_1, this, this);
LoginFilterAspect var10002 = LoginFilterAspect.aspectOf();
ProceedingJoinPoint var10003 = (ProceedingJoinPoint)var1;
Annotation var10004 = ajc$anno$0;
if (ajc$anno$0 == null) {
var10004 = ajc$anno$0 = MainActivity.class.getDeclaredMethod("test").getAnnotation(LoginFilter.class);
}
test_aroundBody1$advice(this, var1, var10002, var10003, (LoginFilter)var10004);
}
static {
ajc$preClinit();
}
}
首先看onCreate方法,它确保会执行 LoginFilterAspect.aspectOf().onActivityMethodBefore(var2);
,对应于切面类中我们使用的@After注解,含义就是在onCreate方法执行完后再执行该切面方法
再看test方法,使用了@LoginFilter注解,对应于切面内@Around注解,含义是在该test方法前后都执行,也就是目标程序插入了Aspect所在的包获取了引用,再通过在目标来里面加入闭包实现
接下来将Aspect的execution换成call,源码如下
public class MainActivity extends AppCompatActivity {
private static final String TAG = "LoginFilterAspect";
public MainActivity() {
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(2131296284);
JoinPoint var3 = Factory.makeJP(ajc$tjp_0, this, this);
LoginFilterAspect var10003 = LoginFilterAspect.aspectOf();
ProceedingJoinPoint var10004 = (ProceedingJoinPoint)var3;
Annotation var10005 = ajc$anno$0;
if (ajc$anno$0 == null) {
var10005 = ajc$anno$0 = MainActivity.class.getDeclaredMethod("test").getAnnotation(LoginFilter.class);
}
test_aroundBody1$advice(this, this, var3, var10003, var10004, (LoginFilter)var10005);
}
@LoginFilter(
loginDefine = 1
)
private void test() {
Log.d("LoginFilterAspect", "test");
}
static {
ajc$preClinit();
}
}
从源码中得以证实call和execution得区别
三、通过切面编程,是不是就可以自定义一些类来检测系统性能情况呢?答案是肯定可以的,这里不再累赘,直接仿造上面实现就可以,对自己写的方法加个注解就好了。