Android 引入AspectJ

 AspectJ 编译  

      AspectJ 编译是通过AJC (AspectJ Complier) 编译工具编译Aspect代码,Android 项目中引入AspectJ 需要设置AJC 相关编译参数才能编译。
        
  • AspectJ比较强大,除了支持对source文件(即aj文件、或@AspectJ注解的Java文件,或普通java文件)直接进行编译外,
  • 还能对Java字节码(即对class文件)进行处理。有感兴趣的同学可以对aspectj-test小例子的class文件进行反编译,你会发现AspectJ无非是在被选中的JPoint的地方加一些hook函数。当然Before就是在调用JPoint之前加,After就是在JPoint返回之前加。
  • 更高级的做法是当class文件被加载到虚拟机后,由虚拟机根据AOP的规则进行hook。
       以下对第二种使用方式进行说明

    一、在需要添加的Module 中的build.gradle中添加gradle脚本

        1、新建aopconfig.gradle 文件

            

import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main

buildscript {
    repositories {
        jcenter()
        google()
    }
    dependencies {
        classpath 'org.aspectj:aspectjtools:1.8.9'
    }
}

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
            }
        }
    }
}

dependencies {
    implementation 'org.aspectj:aspectjrt:1.8.9'
}

        2、在app 中的build.gradle 中引入aopconfig.gradle,整个配置完成

            

apply from : "aopconfig.gradle"

   二、使用Gradle Plugin设置编译参数
  •                 自定义Gradle Plugin
  •                 在apply 方法中实现参数设置
                
import com.android.build.gradle.LibraryPlugin
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.compile.JavaCompile

public class AopPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        //判断应用此Module的Target类型是AppPlugin还是LibraryPlugin
        def hasApp = project.plugins.withType(AppPlugin)
        def hasLibrary = project.plugins.withType(LibraryPlugin)
        if (!hasApp && !hasLibrary) {
            throw new IllegalStateException("'android' or 'android-library' plugin required.")
        }

        final def log = project.logger
        final def variants
        if (hasApp) {
            variants = project.android.applicationVariants
        } else {
            variants = project.android.libraryVariants
        }

        //添加依赖
        project.dependencies {
            compile 'org.aspectj:aspectjrt:1.8.9'
        }

        variants.all { variant ->
            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)
                printf("Aspectj compiler 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.info message.message, message.thrown
                            break;
                    }
                }
            }
        }
    }
}


       参数说明:
        
参数 说明
-showWeaveInfo 输入AJC 编译信息
-1.5 声明AJC 支持AspectJ 1.5 版本
-inpath 需要编译的.class 文件目录(包含Jar文件)
-classpath 指定哪里可以找到用户的class 文件
-aspectpath aspect 编译的Jar文件或者目录路径
-d 编译后输出的目录
-bootclasspath 编译时修改本地的bootcloasspath

AspectJ-specific options:
    -inpath <list>      use classes in dirs and jars/zips in <list> as source
                        (<list> uses platform-specific path delimiter)
    -injars <jarList>  use classes in <jarList> zip files as source
                        (<jarList> uses classpath delimiter)
                        deprecated - use inpath instead.
    -aspectpath <list>  weave aspects in .class files from <list> dirs and jars/zip into sources
                        (<list> uses classpath delimiter)
    -outjar <file>      put output classes in zip file <file>
    -outxml            generate META-INF/aop.xml
    -outxmlfile <file>  specify alternate destination output of -outxml
    -argfile <file>    specify line-delimited list of source files
    -showWeaveInfo      display information about weaving
    -incremental        continuously-running compiler, needs -sourceroots
                        (reads stdin: enter to recompile and 'q' to quit)
    -sourceroots <dirs> compile all .aj and .java files in <dirs>
                        (<dirs> uses classpath delimiter)
    -crossrefs          generate .ajsym file into the output directory
    -emacssym          generate .ajesym symbol files for emacs support
    -Xlint              same as '-Xlint:warning'
    -Xlint:<level>      set default level for crosscutting messages
                        (<level> may be ignore, warning, or error)
    -Xlintfile <file>  specify properties file to set per-message levels
                        (cf org/aspectj/weaver/XlintDefault.properties)
    -X                  print help on non-standard options

Standard Eclipse compiler options:
Options enabled by default are prefixed with '+'

Classpath options:
    -cp -classpath <directories and zip/jar files separated by ;>
                      specify location for application classes and sources
    -bootclasspath <directories and zip/jar files separated by ;>
                      specify location for system classes
    -d <dir>          destination directory (if omitted, no directory is created)
    -d none            generate no .class files
    -encoding <enc>    specify custom encoding for all sources. Each file/directory can override it
                      when suffixed with '['<enc>']' (e.g. X.java[utf8])

Compliance options:
    -1.3              use 1.3 compliance level (implicit -source 1.3 -target 1.1)
    -1.4            + use 1.4 compliance level
    -1.5 -5 -5.0      use 1.5 compliance (-source 1.5 -target 1.5)
    -1.6 -6 -6.0      use 1.6 compliance (-source 1.6 -target 1.6)
    -1.7 -7 -7.0      use 1.7 compliance (-source 1.7 -target 1.7)
    -1.8 -8 -8.0      use 1.8 compliance (-source 1.8 -target 1.8)
    -source <version>  set source level: 1.3 to 1.8 (or 5, 5.0, etc)
    -target <version>  set classfile target: 1.1 to 1.8 (or 5, 5.0, etc)

Warning options:
    -deprecation        + deprecation outside deprecated code
    -nowarn -warn:none disable all warnings
    -warn:<warnings separated by ,>    enable exactly the listed warnings
    -warn:+<warnings separated by ,>  enable additional warnings
    -warn:-<warnings separated by ,>  disable specific warnings
      allDeadCode          dead code including trivial if(DEBUG) check
      allDeprecation      deprecation including inside deprecated code
      allJavadoc          invalid or missing javadoc
      allOver-ann          all missing @Override annotations
      all-static-method    all method can be declared as static warnings
      assertIdentifier  + 'assert' used as identifier
      boxing              autoboxing conversion
      charConcat        + char[] in String concat
      compareIdentical  + comparing identical expressions
      conditionAssign      possible accidental boolean assignment
      constructorName    + method with constructor name
      deadCode          + dead code excluding trivial if (DEBUG) check
      dep-ann              missing @Deprecated annotation
      deprecation        + deprecation outside deprecated code
      discouraged        + use of types matching a discouraged access rule
      emptyBlock          undocumented empty block
      enumIdentifier      'enum' used as identifier
      enumSwitch          incomplete enum switch
      fallthrough          possible fall-through case
      fieldHiding          field hiding another variable
      finalBound          type parameter with final bound
      finally            + finally block not completing normally
      forbidden          + use of types matching a forbidden access rule
      hashCode              missing hashCode() method when overriding equals()
      hiding              macro for fieldHiding, localHiding, typeHiding and
                          maskedCatchBlock
      includeAssertNull    raise null warnings for variables
                          that got tainted in an assert expression
      indirectStatic      indirect reference to static member
      intfAnnotation    + annotation type used as super interface
      intfNonInherited  + interface non-inherited method compatibility
      intfRedundant        find redundant superinterfaces
      javadoc              invalid javadoc
      localHiding          local variable hiding another variable
      maskedCatchBlock  + hidden catch block
      nls                  string literal lacking non-nls tag //$NON-NLS-<n>$
      noEffectAssign    + assignment without effect
      null                potential missing or redundant null check
      nullDereference    + missing null check
      over-ann            missing @Override annotation (superclass)
      paramAssign          assignment to a parameter
      pkgDefaultMethod  + attempt to override package-default method
      raw                + usage of raw type
      semicolon            unnecessary semicolon, empty statement
      serial            + missing serialVersionUID
      specialParamHiding  constructor or setter parameter hiding a field
      static-method        method can be declared as static
      static-access        macro for indirectStatic and staticReceiver
      staticReceiver    + non-static reference to static member
      super                overriding a method without making a super invocation
      suppress          + enable @SuppressWarnings
                          When used with -err:, it can also silent optional
                          errors and warnings
      syncOverride        missing synchronized in synchr. method override
      syntheticAccess      synthetic access for innerclass
      tasks(<tags separated by |>) tasks identified by tags inside comments
      typeHiding        + type parameter hiding another type
      unavoidableGenericProblems + ignore unavoidable type safety problems
                                  due to raw APIs
      unchecked          + unchecked type operation
      unnecessaryElse      unnecessary else clause
      unqualifiedField    unqualified reference to field
      unused              macro for unusedAllocation, unusedArgument,
                              unusedImport, unusedLabel, unusedLocal,
                              unusedPrivate, unusedThrown, and unusedTypeArgs
      unusedAllocation    allocating an object that is not used
      unusedArgument      unread method parameter
      unusedImport      + unused import declaration
      unusedLabel        + unused label
      unusedLocal        + unread local variable
      unusedPrivate      + unused private member declaration
      unusedThrown        unused declared thrown exception
      unusedTypeArgs    + unused type arguments for method and constructor
      uselessTypeCheck    unnecessary cast/instanceof operation
      varargsCast        + varargs argument need explicit cast
      warningToken      + unsupported or unnecessary @SuppressWarnings

AspectJ Api 相关核心类

Android 引入AspectJ

   org.aspectj.lang.annotation
        包含了AspectJ 使用的注解
    org.aspectj.lang
        JoinPoint:表示切入点,带有切入点的相关信息
        ProceedingJoinPoint: JoinPoint 子类,提供执行切入点源代码的能力,只能与Advice 的Around 配合使用。
        Signature: PointCut 签名信息。

       org.aspectj.lang.reflect
            包含JoinPoint 具体的Signature接口类 
       org.aspectj.runtime.reflect
       包含JoinPoint 具体的Signature接口类的实现类
            
       org.aspectj.tools.ajbrowser
                AspectJ 搜索核心了
       org.aspectj.tools.ajc
                Main: AspectJ AJC 入口