Android Aspect切面编程简单入门

一、使用

  1. 添加依赖,配置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;
                }
            }
        }
    }
    
  2. 实现切入

    //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");
        }
    
    }
    

二、注解含义及原理分析

  1. 基本概念

    • Join Points连接点 (你想把新的代码插在程序的哪个地方,是插在构造方法中,还是插在某个方法调用前,或者是插在某个方法中,这个地方就是Join Points)
    • Pointcuts切入点 (告诉代码注入工具,在何处注入一段特定代码的表达式。在那些Join Points应用一个特定的Advice,切入点可以选择唯一一个,比如执行某一个方法)
    • Advice通知 (如何注入到class文件中的代码,例如before、after或者around)
    • Aspect切面 (Pointcut和Advice的组合,例如,我们在应用中通过定义一个Pintcut和给定advice,添加一个日志切面)
    • Weaving织入 (注入代码advices 到目标位置joint pints的过程)
  2. 注解

    • @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)
      }
      
  3. 接下来分析一下实现切入,这个过程是怎么做到切入的

    • 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所在的包获取了引用,再通过在目标来里面加入闭包实现

  4. 接下来将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得区别

三、通过切面编程,是不是就可以自定义一些类来检测系统性能情况呢?答案是肯定可以的,这里不再累赘,直接仿造上面实现就可以,对自己写的方法加个注解就好了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值