美文网首页Android 知识AOP
谈谈对Android上AspectJ使用的想法

谈谈对Android上AspectJ使用的想法

作者: JasonChen8888 | 来源:发表于2019-10-12 16:20 被阅读0次

    AOP是什么

    概念:AOP是Aspect Oriented Programming的缩写,即『面向切面编程』;切面编程,就是在你项目原有的功能基础上,通过AOP去添加新的功能,这些功能是建立在原有功能的基础上的,而且原有的功能并不知道你已经添加了新的功能;AOP就是在某一个类或方法执行前后打个标记,声明在执行到这里之前要先执行什么,执行完这里之后要接着执行什么。插入了新的执行方法。

    AOP和OOP的不同

    OOP,即『面向对象编程』,它提倡的是将功能模块化,对象化,而AOP的思想,则不太一样,它提倡的是针对同一类问题的统一处理,当然,我们在实际编程过程中,不可能单纯的安装AOP或者OOP的思想来编程,很多时候,可能会混合多种编程思想,大家也不必要纠结该使用哪种思想,取百家之长,才是正道。

    AOP的使用场景

    主要用于不想侵入原有代码的场景中,例如SDK需要无侵入的在宿主中插入一些代码,做日志埋点、性能监控、数据校验、持久化、动态权限控制、甚至是代码调试等等。

    什么是AspectJ

    AspectJ实际上是对AOP编程思想的一个实践,当然,除了AspectJ以外,还有很多其它的AOP实现,例如ASMDex,但目前最好、最方便的,依然是AspectJ。

    原理图

    图片来自https://www.jianshu.com/p/0fa8073fd144

    工作原理.png

    首先谈谈AspectJ相关的几个概念点

    • Join Points(连接点)
      Join Points可以看作是程序运行时的一个执行点,比如:一个函数的调用可以看作是个Join Points,如Log.d()这个函数,d()可以看作是个Join Points,而调运d()的函数也可以认为是一个Join Points;设置一个变量,或者读取一个变量也可以是个Join Points;for循环也可以看作是Join Points。可以是函数,构造方法等


      Join Points.png
    • Pointcuts(切入点)
      一个程序会有多个Join Points,即使同一个函数,也还分为call和execution类型的Join Points,但并不是所有的Join Points都是我们关心的,Pointcuts就是提供一种使得开发者能够选择自己需要的JoinPoints的方法;或者说是程序中可能作为代码注入目标的特定的点,例如一个方法调用或者方法入口。


      image.png

      以上的 Signature 都是由一段表达式组成,且每个关键词之间都有“空格”,下面是对关键词的解释:


      image.png
    • Advice
      Advice,也就是具体的插入点。典型的 Advice 类型有 before、after 和 around,分别表示在目标方法执行之前、执行后和完全替代目标方法执行的代码。


      image.png

      看个例子:

    @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 onActivityMethodBefore:实际切入的代码。

    应用

    在Android项目中使用AspectJ

    在android中配置aspectj是特别麻烦的,目前市场上流行的一款在Android使用的插件 gradle_plugin_android_aspectjx

    如何在Android studio配置gradle_plugin_android_aspectjx的插件

    • 项目根目录的build.gradle中增加依赖:classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'
    buildscript {
        
        repositories {
            google()
            jcenter(){
                url 'http://jcenter.bintray.com/'
            }
        }
        dependencies {
            classpath 'com.android.tools.build:gradle:3.1.2'
            classpath 'com.jakewharton:butterknife-gradle-plugin:8.8.1'  //添加这一行
            classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'
            // NOTE: Do not place your application dependencies here; they belong
            // in the individual module build.gradle files
        }
    }
    
    allprojects {
        repositories {
            google()
            jcenter(){
                url 'http://jcenter.bintray.com/'
            }
        }
    }
    
    task clean(type: Delete) {
        delete rootProject.buildDir
    }
    
    • 在主项目或者库的build.gradle中增加AspectJ的依赖:api 'org.aspectj:aspectjrt:1.8.9' //相关api的引入
    dependencies {
      implementation fileTree(dir: 'libs', include: ['*.jar'])
      api 'org.aspectj:aspectjrt:1.8.9'
                  ...
    }
    
    • 在主项目的build.gradle中添加:apply plugin: 'android-aspectjx'
    apply plugin: 'com.android.application'
    apply plugin: 'android-aspectjx'
    
    • Aspect的实现类
    package com.jason.aspectj;
    
    import android.content.Context;
    import android.util.Log;
    import android.widget.Toast;
    
    import com.jason.aspectj.tools.StopWatch;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    
    /**
    * Description:AspectActivityTest
    *
    * @author Chenby
    * @create 2019/10/10 14: 25
    */
    @Aspect
    public class AspectActivityTest {
    
     /**
      * 重要的概念知识
      */
     //Join Points,简称JPoints,是AspectJ的核心思想之一,它就像一把刀,把程序的整个执行过程切成了一段段不同的部分
     //Advice,Advice其实是最好理解的,也就是我们具体插入的代码,以及如何插入这些代码
     //@Before:Advice,也就是具体的插入点, 常见的有:Before,After,Around
     //execution:处理Join Point的类型,例如call、execution
     //execution是在被切入的方法中,call是在调用被切入的方法前或者后
    
     //Call(Before)
     //Pointcut{
     //    Pointcut Method
     //}
     //Call(After)
    
     //Pointcut{
     //  execution(Before)
     //    Pointcut Method
     //  execution(After)
     //}
    
     //(* android.app.Activity.on**(..)):这个是最重要的表达式,第一个『*』表示返回值,『*』表示返回值为任意类型,后面这个就是典型的包名路径,
     // 其中可以包含『*』来进行通配,几个『*』没区别。同时,这里可以通过『&&、||、!』来进行条件组合。()代表这个方法的参数,你可以指定类型,
     // 例如android.os.Bundle,或者(..)这样来代表任意类型、任意个数的参数。
    
    
     private static final String TAG = "Chenby";
    
     /**
      * advice 采用before的例子, Joint Point的类型是execution  Before 表示在方法之后插入代码
      *
      * @throws Throwable
      */
     @Before("execution(* android.app.Activity.on**(..))")
     public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable {
       String key = joinPoint.getSignature().toString();
       Log.d(TAG, "onActivityMethodBefore: " + key);
     }
    
     /**
      * advice 采用after的例子, Joint Point的类型是execution  After 表示在方法之后插入代码
      *
      * @throws Throwable
      */
     @After("execution(* android.app.Activity+.on*(android.os.Bundle))")
     public void onActivityMethodAfter(JoinPoint joinPoint) throws Throwable {
       String key = joinPoint.getSignature().toString();
       Log.d(TAG, "onActivityMethodAfter: " + key);
     }
    
     /**
      * advice 采用 Around的例子, Joint Point的类型是execution, Around表示在方法前后各插入代码
      *
      * @throws Throwable
      */
     @Around("execution(* com.cby.mvvmdemo.ui.mine.MinePageFragment.testAOP(*))")
     public void onFragmentMethodAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
       String key = proceedingJoinPoint.getSignature().toString();
       Log.d(TAG, "onFragmentMethodAround: " + key);
       //表示原有方法的执行
       proceedingJoinPoint.proceed();
       Log.d(TAG, "onFragmentMethodAround: " + key);
     }
    
     @Around("execution(* com.cby.mvvmdemo.ui.mine.MinePageFragment.testAOP(android.content.Context))")
     public void onMethodAroundToast(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
       Context mContext = null;
       //proceedingJoinPoint.getThis()可以获取到调用该方法的对象
       if (proceedingJoinPoint.getThis() instanceof Context) {
         mContext = (Context) proceedingJoinPoint.getThis();
       } else {
         //proceedingJoinPoint.getArgs()可以获取到方法的所有参数
         for (Object context : proceedingJoinPoint.getArgs()) {
           if (context instanceof Context) {
             mContext = (Context) context;
             break;
           }
         }
       }
       if (mContext == null) {
         return;
       }
       proceedingJoinPoint.proceed();
       Toast.makeText(mContext.getApplicationContext(), "Test show toast for aop!", Toast.LENGTH_SHORT)
           .show();
     }
    
     private static final String POINTCUT_METHOD =
         "execution(@com.jason.aspectj.custom.DebugTool * *(..))";
    
     private static final String POINTCUT_CONSTRUCTOR =
         "execution(@com.jason.aspectj.custom.DebugTool *.new(..))";
    
     @Pointcut(POINTCUT_METHOD)
     public void methodAnnotatedWithDebugTrace(){}
    
     @Pointcut(POINTCUT_CONSTRUCTOR)
     public void constructorAnnotatedDebugTrace() {}
    
     @Around("methodAnnotatedWithDebugTrace() || constructorAnnotatedDebugTrace()")
     public Object onDebugToolMethodAround(ProceedingJoinPoint joinPoint) throws Throwable {
    
       MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
       String className = methodSignature.getDeclaringType().getSimpleName();
       String methodName = methodSignature.getName();
    
       final StopWatch stopWatch = new StopWatch();
       stopWatch.start();
       Object result = joinPoint.proceed();
       stopWatch.stop();
    
       Log.i(className, buildLogMessage(methodName, stopWatch.getTotalTimeMillis()));
    
       return result;
     }
    
     /**
      * Create a log message.
      *
      * @param methodName A string with the method name.
      * @param methodDuration Duration of the method in milliseconds.
      * @return A string representing message.
      */
     private static String buildLogMessage(String methodName, long methodDuration) {
       StringBuilder message = new StringBuilder();
       message.append("Chenby --> ");
       message.append(methodName);
       message.append(" --> ");
       message.append("[");
       message.append(methodDuration);
       message.append("ms");
       message.append("]");
    
       return message.toString();
     }
    
     @AfterReturning(value = "execution(* com.cby.mvvmdemo.z_test.activities.AopTestActivity.AfterReturning*(..))", returning = "num")
     public void testAspectAfterReturning(int num) {
       Log.e(TAG, "AfterReturning-num:" + num);
     }
    }
      /**
       * proceed(ibject[] args)的使用
       */
      @Around("execution(* com.jason.aspectj*.many*(..))")
      public Object process(ProceedingJoinPoint point) throws Throwable {
           System.out.println("@Around:执行目标方法之前...");
           //访问目标方法的参数:
           Object[] args = point.getArgs();
           if (args != null && args.length > 0 && args[0].getClass() == String.class) {
               args[0] = "改变后的参数1";
           }
           //用改变后的参数执行目标方法
           Object returnValue = point.proceed(args);
           System.out.println("@Around:执行目标方法之后...");
           System.out.println("@Around:被织入的目标对象为:" + point.getTarget());
           return "原返回值:" + returnValue + ",这是返回结果的后缀";
       }
       
    
    • 自定义的注解
    package com.jason.aspectj.custom;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
    * Description:DebugTool :自定义AOP的注解
    *
    * @author Chenby
    * @create 2019/10/10 16: 31
    */
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.CONSTRUCTOR, ElementType.METHOD})
    public @interface DebugTool {
    }
    
    

    结语

    以上是借鉴了网上的文章和自己的一些理解关于AOP的AspectJ的编程的想法,如有错误欢迎评论留言指出

    参考文献

    https://www.jianshu.com/p/0fa8073fd144
    https://blog.csdn.net/eclipsexys/article/details/54425414
    https://www.cnblogs.com/yxx123/p/6665736.html

    相关文章

      网友评论

        本文标题:谈谈对Android上AspectJ使用的想法

        本文链接:https://www.haomeiwen.com/subject/nipqmctx.html