web-dev-qa-db-ja.com

Androidライブラリを使用したAspectj

アスペクトを使用し、Maven経由で利用できるライブラリがあります。現在、そのライブラリをAndroidアプリケーションで使用しようとしています。

このプラグイン をアプリのgradleファイルに含めると、すべて正常に機能しますが、私の目標は、classpath 'com.uphyca.gradle:gradle-Android-aspectj-plugin:0.9.+'apply plugin: 'Android-aspectj'(プラグインに必要)をに抽出することです。アプリで宣言する代わりに、my.libgradleファイル。

それは可能ですか?

アプリ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'
}
24
letz

私も同じ問題を抱えていました。これは私がそれを解決するためにしたすべてです。

ルート/メインプロジェクト

ルートプロジェクトに、クラスのウィービングに必要なajcコンパイラを含むAspectJツールを追加します。 (これをライブラリの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ファイルで、次のようになっていることを確認します。主な追加は、上部のimportステートメントと、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のweaver)コマンドがAspectJとJavaソースファイルと.classファイルをコンパイルしてウィービングし、任意のJava VM。

これを実行するには、タスクにライブラリに関する引数が必要です。これが、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がクラスをコンパイル/ウィービングしている間に発生しているイベントのメッセージを蓄積します。次に、プロジェクトロガーに渡され、プロジェクトロガーは、ajcが生成した重要なエラーや警告を出力します。たとえば、アドバイスでポイントカットを参照できない場合は、ポイントカットが検出され、gradleコンソールに表示されます。 enter image description here

したがって、上記で説明したことはすべて、基本的にここで行われています。 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プラグイン

アスペクトが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'
}

次に、実際のクラス。

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

私はこれがたくさんのように見えるかもしれないことを知っていますが、解決策は同じままなので、コピーと貼り付けがたくさんあります。クラスをよく見ると、ライブラリモジュールで行われているのと同じことがアプリのモジュールに適用されています。これに対して行う主な変更は、ここで行われるプラグインを介して、ライブラリモジュールをプロジェクトの依存関係に追加することです。

 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にあるため、幸運を祈っています。フォークして、プラグインのプロジェクトをコピーし、必要な変更を加えてください。

このプロジェクトはFlenderと呼ばれ、接続チェックが必要なメソッドに注釈を付けるために使用されます。ここにリンクがあります https://github.com/jd-alexander/flender

この回答がお役に立てば幸いです。

13
Joel Dean