简体   繁体   English

Aspectj与android库

[英]Aspectj with android library

I have a lib that use aspects and is available via maven, now I'm trying to use that lib in an android application. 我有一个使用方面的lib,可以通过maven获得,现在我正在尝试在Android应用程序中使用该lib。

If I include this plug-in in the app gradle file, everything works fine, but my goal is to extract the classpath 'com.uphyca.gradle:gradle-android-aspectj-plugin:0.9.+' and the apply plugin: 'android-aspectj' (required by the plugin) to the my.lib gradle file instead of declaring in my app. 如果我将这个插件包含在app gradle文件中,一切正常,但我的目标是提取classpath 'com.uphyca.gradle:gradle-android-aspectj-plugin:0.9.+'apply plugin: 'android-aspectj' (插件所需)到my.lib gradle文件而不是在我的应用程序中声明。

Is that possible? 那可能吗?

app gradle file: app gradle文件:

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

apply plugin: 'android-aspectj'

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

GOAL: 目标:

app gradle file: app gradle文件:

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

my.lib gradle file: 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'
}

I had the same problem. 我有同样的问题。 This is all I did to solve it. 这就是我所做的一切来解决它。

Root/Main Project 根/主项目

In your root project add the AspectJ tools that contains the ajc compiler that is necessary for weaving your classes. 在根项目中添加包含编织类所需的ajc编译器的AspectJ工具。 ( You can also add this to your library's build.gradle file but it's better to add it here as the gradle plugin that you will be creating to accommodate your library will be using the 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'
    }

Library Project 图书馆计划

In your library's build.gradle file ensure that it looks like this similar to this. 在库的build.gradle文件中,确保它看起来像这样。 The main additions are the import statements at the top and the code beneath the android build properties. 主要的补充是top顶部的import语句和android build属性下面的代码。

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

So what's happening is when the project is being compiled the ajc(AspectJ's weaver) command compiles and weaves AspectJ and Java source and .class files, producing .class files compliant with any Java VM. 所以正在发生的事情是在编译项目时,ajc(AspectJ的weaver)命令编译并编织AspectJ和Java源文件和.class文件,生成符合任何Java VM的.class文件。

For this to take place the task needs arguments about your library. 为此,任务需要有关您的库的参数。 That's the reason for creating the args variable. 这就是创建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)
            ]

Then the message handler that's created is simply being passed to ajc to accumulate messages of events that are taking place while ajc is compiling/weaving the classes. 然后,创建的消息处理程序被简单地传递给ajc,以累积在ajc编译/编织类时发生的事件的消息。 Then it's being passed to a project logger that then outputs any important errors or warnings that the ajc produced. 然后它被传递给项目记录器,然后输出ajc产生的任何重要错误或警告。 For example if a pointcut can't be referenced by an advice it will be detected and shown in the gradle console. 例如,如果建议无法引用切入点,则会在gradle控制台中检测并显示切入点。 在此输入图像描述

So all of what was describe above is basically taking place right here. 因此,上面描述的所有内容基本上都在这里发生。 Where the args and message handler are being passed to the Main function of the ajc(AspectJ compiler). 将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 Plugin Gradle插件

The pointcuts/advices of your library weren't being triggered because you were targeting the app module while Aspects were only being woven into your library's module with the com.uphyca.gradle:gradle-android-aspectj-plugin AspectJ plugin. 您的库的切入点/建议未被触发,因为您的目标是app模块,而Aspects只是通过com.uphyca.gradle:gradle-android-aspectj-plugin AspectJ插件编织到您的库模块中。 So in order for the Aspects of your library to be woven into your App's module you have to create a gradle plugin for your project. 因此,为了将您图书馆的Aspects编织到App的模块中,您必须为您的项目创建一个gradle插件。 So what you have defined as your goal is your question is not possible, this is the only way it can be done. 所以你定义的目标是你的问题是不可能的,这是唯一可行的方法。

This is how the plugin should look. 这就是插件的外观。 (Plugin is done in groovy). (插件是在groovy中完成的)。

Plugin's build.gradle 插件的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'
}

Then the actual 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;
                    }
                }
            }
        }
    }
}

I know this might seem like a lot but it's alot of copy and pasting because the solution remains the same. 我知道这可能看起来很多,但它有很多复制和粘贴,因为解决方案保持不变。 If you look closely at the class the same things being done in your library module is now being applied to your app's module. 如果仔细查看该类,您的库模块中正在执行的相同操作现在将应用于您的应用程序模块。 The main modification you would do to this is add your library module to the project's dependencies via the plugin that's done here. 您要对此进行的主要修改是通过此处完成的插件将库模块添加到项目的依赖项中。

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

For your library to be available to your plugin while developing you have to ensure that it's being deployed to your local maven repository. 为了使您的库在开发时可用于您的插件,您必须确保将其部署到您的本地maven存储库。 This can be done by applying this plugin( https://github.com/dcendents/android-maven-gradle-plugin ) to your library module and running the gradle install task. 这可以通过将此插件( https://github.com/dcendents/android-maven-gradle-plugin )应用于库模块并运行gradle install任务来完成。

Final Steps 最后的步骤

Once all of that is done you can then apply it to a sample app for testing by adding this to it's build.gradle file 完成所有这些后,您可以将其应用于示例应用程序,以便通过将其添加到其build.gradle文件进行测试

buildscript {
    repositories {
        mavenCentral()

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

    dependencies {             

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

Once that's done your library will be available to the app because it's being added to the project once the plugin has been applied. 完成后,您的库将可供应用程序使用,因为一旦应用了插件,它就会被添加到项目中。

If things are still confusing you are in good luck because the project I implemented this solution is on Github so you can fork it, copy the plugin's project and make the necessary changes. 如果事情仍然令人困惑,那么你好运,因为我实现这个解决方案的项目是在Github上,所以你可以分叉它,复制插件的项目并进行必要的更改。

The project is called Flender and it's used to annotate methods that require connectivity checking. 该项目称为Flender,它用于注释需要连接检查的方法。 Here's the link https://github.com/jd-alexander/flender 这是链接https://github.com/jd-alexander/flender

Hope this answer helps. 希望这个答案有所帮助

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM