具有 android 库的 Aspectj

Aspectj with android library

我有一个使用方面的库,可通过 Maven 获得,现在我正尝试在 android 应用程序中使用该库。

如果我在应用 gradle 文件中包含 this plug-in,一切正常,但是 我的目标是将 classpath 'com.uphyca.gradle:gradle-android-aspectj-plugin:0.9.+'apply plugin: 'android-aspectj'(插件需要)提取到 my.lib gradle 文件,而不是在我的应用程序中声明。

这可能吗?

应用gradle文件:

classpath 'com.uphyca.gradle:gradle-android-aspectj-plugin:0.9.+'

apply plugin: 'android-aspectj'

dependencies { 
  compile 'my.lib:example:1.0.0'
}

目标:

应用gradle文件:

dependencies { 
  compile 'my.lib:example:1.0.0'
}

my.lib gradle 文件:

classpath 'com.uphyca.gradle:gradle-android-aspectj-plugin:0.9.+'

apply plugin: 'android-aspectj'

dependencies { 
  compile 'org.aspectj:aspectjrt:1.7.3'
}

我遇到了同样的问题。这就是我为解决它所做的一切。

Root/Main 项目

在您的根项目中添加 AspectJ 工具,其中包含编织您的 classes 所必需的 ajc 编译器。 (您也可以将其添加到您的图书馆的 build.gradle 文件中,但最好将其添加到此处,因为您将创建的 gradle 插件将使用 ajc 以适应您的图书馆。

buildscript {
    repositories {
        jcenter()


    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.2.3'
        classpath 'org.aspectj:aspectjtools:1.8.5'
    }

图书馆项目

在您图书馆的 build.gradle 文件中确保它看起来与此类似。主要添加的是顶部的导入语句和 android 构建属性下方的代码。

import com.android.build.gradle.LibraryPlugin
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main

apply plugin: 'com.android.library'


dependencies {
    compile 'org.aspectj:aspectjrt:1.8.5'
}
android {
    compileSdkVersion 22
    buildToolsVersion "22.0.1"

    defaultConfig {
        minSdkVersion 14
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

android.libraryVariants.all { variant ->
    LibraryPlugin plugin = project.plugins.getPlugin(LibraryPlugin)
    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", android.bootClasspath.join(File.pathSeparator)
        ]

        MessageHandler handler = new MessageHandler(true);
        new Main().run(args, handler)

        def log = project.logger
        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:
                case IMessage.INFO:
                    log.info message.message, message.thrown
                    break;
                case IMessage.DEBUG:
                    log.debug message.message, message.thrown
                    break;
            }
        }
    }
}

        

所以发生的事情是当项目正在编译时,ajc(AspectJ 的编织器)命令编译和编织 AspectJ 和 Java 源文件和 .class 文件,生成 .class 文件符合任何 Java 虚拟机。

为此,任务需要关于您的图书馆的参数。这就是创建 args 变量的原因。

 String[] args = [
                    "-showWeaveInfo",
                    "-1.5",
                    "-inpath", javaCompile.destinationDir.toString(),
                    "-aspectpath", javaCompile.classpath.asPath,
                    "-d", javaCompile.destinationDir.toString(),
                    "-classpath", javaCompile.classpath.asPath,
                    "-bootclasspath", android.bootClasspath.join(File.pathSeparator)
            ]

然后将创建的消息处理程序简单地传递给 ajc 以累积在 ajc compiling/weaving classes 期间发生的事件的消息。然后它被传递给一个项目记录器,然后输出 ajc 产生的任何重要错误或警告。例如,如果切入点不能被建议引用,它将被检测并显示在 gradle 控制台中。

所以上面描述的所有内容基本上都在这里发生。将 args 和消息处理程序传递给 ajc(AspectJ 编译器)的 Main 函数的位置。

 MessageHandler handler = new MessageHandler(true);
        new Main().run(args, handler)

        def log = project.logger
        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

Gradle 插件

你的库的 pointcuts/advices 没有被触发,因为你的目标是应用程序模块,而 Aspects 只是通过 com.uphyca.gradle:gradle-android-aspectj-plugin AspectJ 插件被编织到你的库的模块中。因此,为了将您的库的方面融入您的应用程序模块中,您必须为您的项目创建一个 gradle 插件。所以你定义的目标是你的问题是不可能的,这是唯一可以做到的方法。

这就是插件的外观。 (插件在 groovy 中完成)。

插件的 build.gradle

apply plugin: 'groovy'

targetCompatibility = JavaVersion.VERSION_1_7
sourceCompatibility = JavaVersion.VERSION_1_7

dependencies {
  compile gradleApi()
  compile localGroovy()
  compile 'com.android.tools.build:gradle:1.1.0-rc3'
  compile 'org.aspectj:aspectjtools:1.8.5'
  compile 'org.aspectj:aspectjrt:1.8.5'
}

那么实际的class.

import com.android.build.gradle.AppPlugin
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

public class YourPlugin implements Plugin<Project> {
    @Override void apply(Project project) {
        def hasApp = project.plugins.withType(AppPlugin)
        def hasLib = project.plugins.withType(LibraryPlugin)
        if (!hasApp && !hasLib) {
            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 'com.name:example:1.0'
            // TODO this should come transitively
            compile 'org.aspectj:aspectjrt:1.8.5'
        }

        variants.all { variant ->

            variant.dex.doFirst {
                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;
                    }
                }
            }
        }
    }
}

我知道这可能看起来很多,但它需要大量复制和粘贴,因为解决方案保持不变。如果您仔细观察 class,在您的库模块中完成的相同操作现在正应用于您的应用程序模块。 您要对此做的主要修改是通过此处完成的插件将您的库模块添加到项目的依赖项中。

 project.dependencies {
                compile 'com.letz:example-library:1.0'
                // TODO this should come transitively
                compile 'org.aspectj:aspectjrt:1.8.5'
            }

为了让您的库在开发时可供您的插件使用,您必须确保它被部署到您的本地 Maven 存储库。这可以通过将此插件 (https://github.com/dcendents/android-maven-gradle-plugin) 应用到您的库模块和 运行 gradle install 任务来完成。

最后的步骤

完成所有这些后,您可以将其添加到示例应用程序中进行测试,方法是将其添加到 build.gradle 文件

buildscript {
    repositories {
        mavenCentral()

        //Only necessary when developing locally.
        mavenLocal()
    }

    dependencies {             

        classpath 'com.letz:example-plugin:1.0'
    }
}
apply plugin: 'example-plugin'

一旦完成,您的库将可供应用程序使用,因为一旦应用插件,它就会被添加到项目中。

如果事情仍然令人困惑,那么你很幸运,因为我实现这个解决方案的项目在 Github 上,所以你可以 fork 它,复制插件的项目并进行必要的更改。

该项目称为 Flender,用于注释需要连接检查的方法。这是 link https://github.com/jd-alexander/flender

希望这个回答对您有所帮助。