android gradle plugin でAop(2016/9時点)


導入するライブラリ

  • JakeWharton神 の Hugo
  • uPhycaさんの gradle-android-aspectj-plugin

を参考にして作られたpluginらしいです。

自分が、uPhycaさんの試していたときは、依存関係が解決できなかったので

結局自前でタスク書いてました

configurations {
    ajc
    aspects
    ajInpath
}

dependencies {
    //aspectj
    ajc 'org.aspectj:aspectjtools:1.8.+'
    compile 'org.aspectj:aspectjrt:1.8.+'
}

android.applicationVariants.all { variant ->
    //aspectj compile
    variant.javaCompile.doLast {
        def iajcClasspath = files(android.getBootClasspath(), variant.javaCompile.classpath).toList().join(File.pathSeparator)

        ant.taskdef( resource:"org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties", classpath: configurations.ajc.asPath)
        ant.iajc (
                source:sourceCompatibility,
                target:targetCompatibility,
                //destDir:"${project.buildDir}/classes/${variant.dirName}",
                destDir:"${project.buildDir}/intermediates/classes/${variant.dirName}",//move destDir 1.1.x
                maxmem:"512m",
                fork:"true",
                showWeaveInfo:true,
                //complianceLevel:sourceCompatibility, //not support ant-task 
                verbose:false,
                Xlint:'ignore',
                encoding:options.encoding,//'UTF-8'
                aspectPath:configurations.aspects.asPath,
                inpath:configurations.ajInpath.asPath,
                sourceRootCopyFilter:"**/.svn/*,**/*.java",
                classpath:iajcClasspath
                
        ){
            sourceroots{
                android.sourceSets.main.java.srcDirs.each{
                    pathelement(location:it.absolutePath)
                }
                pathelement(location:"${project.buildDir}/generated/source/r/${variant.dirName}")
                pathelement(location:"${project.buildDir}/generated/source/buildConfig/${variant.dirName}")
                pathelement(location:"${project.buildDir}/generated/source/aidl/${variant.dirName}")
                pathelement(location:"${project.buildDir}/generated/source/rs/${variant.dirName}")
            }
        }
    }
}

動いた環境

  • android gradle plugin 2.1+
    • 2.2.+ では動かず
    • ここらへんはaptのpluginとかも普通に動かないのでそういうものかとは思う*1
    • ただ kotlin-gradle-plugin も2.2.+ で動かないらしいので、追跡調査は必要
  • gradle wraper
    • 2.14.1-bin.zip
    • 3.0-bin.zip は動かず。*2

動かす上での注意点など

  • app/build.gradle
   apply plugin: 'android-aspectjx'
    aspectjx {
        includeJarFilter "${rootProject.name}/library"
        excludeJarFilter 'universal-image-loader'
    }
  • ココらへんの定義は、aspectjxを適応する本体側に書かないと駄目
  • includeJarFilter
  • excludeJarFilter

たとえば下記の状態の時

  • プロジェクト名:AspectJX-Demo
  • libraryプロジェクトの中にaop対象のソースが有る

build/〜/exploded-aar/AspectJX-Demo/library

と展開されてしまうからそう参照している形になっているよう。

外部参照 aar/jar の場合は excludeJarFilter の記述のような aar名だけで良いイメージ

いじってたサンプル

このサンプルのいい所はaop用のコードがlibrary-projectの形で外だし

本体側のコードに、デバック専用コードっぽいコードを同梱する(git等で管理する)のは

結構嫌がられますしね。。

  • 動かす上での注意点など の記述コードが本体側になければ尚良でしたが。。

aspectjのruntimeだけは、library-projectの方だけにかけたりするので、この調査は凄く評価できる*3

  • library/build/gradle
dependencies {
    testCompile 'junit:junit:4.12'
    compile 'org.aspectj:aspectjrt:1.8.+'//☆
}

改変した箇所

  • build.gradle
buildscript {
    dependencies {
        //〜略〜
        classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:1.0.+'
        classpath 'com.android.tools.build:gradle:2.1.+'
        //〜略〜
    }
}


allprojects {

    //-XlintのワーニングをASのMessageタブに表示させる
    gradle.projectsEvaluated {
        tasks.withType(JavaCompile) {
            options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
            }
    }
    
    //pgradle.sh/pgradle.bat用
    //こう書かないとFlavorとか使っている場合 gradlew tasks で一覧に表示されない
    task build_tasks << {
        tasks.each{task->
                if(task.group == "build"){
                        println(task.name)
                    }
        }
    }

    tasks.whenTaskAdded { task ->
            if (task.name.indexOf("assemble")!=-1) {
                task.group = "build"
            }
    }
}

project.ext{
    compileSdkVersion=24
    buildToolsVersion="24.0.2"
    supportVersion="24.1.+"
}   
  • app/build.gradle
    • debugタスクが指定されている時だけ動くように改変。
def found_aspect_target = false
project.gradle.startParameter.taskNames.each {cmd_task ->
    if (cmd_task !=null && cmd_task.toLowerCase().indexOf("debug")!=-1) {
        found_aspect_target = true
        return
    }
}

if (found_aspect_target) {
    apply plugin: 'android-aspectjx'
    ant.echo "[AspectJX]:"+rootProject.name
    aspectjx {
        includeJarFilter "${rootProject.name}/library"
        excludeJarFilter 'universal-image-loader','appcompat-v7'
    }

    dependencies {
        compile project(':library')
    }
}
android {
    compileSdkVersion project.compileSdkVersion
    buildToolsVersion project.buildToolsVersion


}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])

    compile "com.android.support:appcompat-v7:$supportVersion"
    compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"

ココらへんのバージョンは揃えないと、check task でエラーが出る今日この頃なので仕方なし。

  • app/DemoApplication.java
    • library/LibrarySDK.java は中身空で使ってないみたいなのでコメント
//LibrarySDK.init();

触ってて気づいたこと

  • 下記のログが出ていないとaspectjのビルドタスクが正常に動いていないよう
    • debugビルドでも下記のログでていないようならcleanビルドが必要
      • 一回ビルド済みだと下記のログが出ない(aspectjコンパイルのタスク自体skipされている感じ)
    • :library(AOPのソースが有るプロジェクト) だけ弄った場合、上手く再ビルドされない??
      • この挙動は追跡調査必要か?
    • ここらへんmain側の gradleのbuild task に相乗りしていて キャッシング機構によりskipされているからかな?
      • でもandroid gradle plugin自体、変更ファイルが上手くビルドされない(同梱されない)事があるので其のせいかも
aspect do work..........

aspect jar merging..........
aspect done...................

一応AOPの同梱が成功していると、実行時にlogcatで以下のログが確認できる

f:id:kimukou_26:20160917082406p:plain

*1:割り込みタスクのタイミングとか名前変わってそうですし。。

*2:zipの展開に失敗するみたい。gradle runtime自体のDSLの変更とか影響してるのかも

*3:余計なjar同梱で