2013-06-20

_ Android Studio でも apt が使い物になる build.gradle の書き方(暫定版)

Gradle の android plugin では、 Build TypeProduct Flavorの組み合わせ毎に Build Variants が作成され、 Build Variant 毎に別々の apk をビルドすることができます。Gradle で apt を使ったプロジェクトを作る方法はesmasuiさんに教わったのですが、 Android Studio のBuild Variants 切り替えに対応していなかったのでなんとかしてみました。

Android Studio の Build Variants 切り替え対応版 build.gradle

以下が build.gradle の記述です。Android Annotations の例ですが、 dependencies に依存を追加すれば JSON Pull Parser などでもそのまま使えます。

// buildscript とか maven リポジトリの設定は省略

apply plugin: 'android'

configurations {
    apt
}

dependencies {
    // http://stackoverflow.com/questions/16683944/androidannotations-nothing-generated-empty-activity/16802216#16802216
    apt "com.googlecode.androidannotations:androidannotations:2.7.1"
    compile "com.googlecode.androidannotations:androidannotations-api:2.7.1"
}

android {
    compileSdkVersion 17
    buildToolsVersion "17.0.0"

    defaultConfig {
        minSdkVersion 7
        targetSdkVersion 17
    }
}

android.applicationVariants.all { variant ->
    // ↓ def を付けないと プロジェクトの Dynamic Property になってしまい、常に最後の variant 用のディレクトリが使われてしまう。
    // variant にソースフォルダを追加する方法が見当たらないので R.java 用のソースフォルダを生成場所として利用する。
    def aptOutput = file("${project.buildDir}/source/r/${variant.dirName}")

    variant.javaCompile.doFirst {
        println "*** compile doFirst ${variant.name}, aptOutput=${aptOutput}"
        aptOutput.mkdirs()
        // インクリメンタルビルドでエラーになるので古いファイルが存在する場合は消しておく
        aptOutput.eachFileRecurse groovy.io.FileType.FILES, {
            if (it.name.equals('R.java')) {
                return
            }
            it.delete()
        }
        variant.javaCompile.options.compilerArgs += [
                '-processorpath', configurations.apt.getAsPath(),
                '-AandroidManifestFile=' + variant.processResources.manifestFile,
                '-s', aptOutput
        ]
    }
}

内容の説明

esmasuiさんのエントリの内容 との違いを中心に説明します。

修正点は主に3点です。

1つ目は、

   aptOutput = file("${project.buildDir}/source/r/${variant.dirName}")

   def aptOutput = file("${project.buildDir}/source/r/${variant.dirName}")

とした点です。これは、 def をつけないとプロジェクトの Dynamic Property になってしまい、 Closure の中から参照した際に常に最後の Build Variant が設定した内容を取得してしまうバグを修正するものです。この修正を行うことで、Build Variant 毎ね別々のディレクトリにファイルが生成されるようになります。

2つ目はファイルの生成場所を

   "${project.buildDir}/source/apt_generated/${variant.dirName}"

から

   "${project.buildDir}/source/r/${variant.dirName}"

へ変更したことです。ここは R.java が生成されるディレクトリです。本来であれば apt_generated の様に apt 専用置き場所を作成すべきですが、現状の android plugin では variant に対して新たな Source Root を追加する方法が見つかりませんでした。variant に追加しておかないと Android Studio で Source Root して扱ってくれません。そのため今回は仕方なく R.java と同居する方法をとりました(そのため暫定版です)。

3つ目は、 variant.javaCompile.doFirst に渡す Closure の中に追加した以下のクリーンアップ処理です。

        aptOutput.eachFileRecurse groovy.io.FileType.FILES, {
            if (it.name.equals('R.java')) {
                return
            }
            it.delete()
        }

これは、コンパイルの際のエラーを防ぐためのものです(一度 apt でファイルを生成していると、コンパイルの際にクラスの重複エラーが出ます)。

どうしても R.java との同居が許せない人は

R.java と同居するのがどうしても許せない人は

   def aptOutput = file("${project.buildDir}/source/apt_generated/${variant.dirName}")

として、その直後に以下の行を追加すると apt_generated フォルダを使うこともできます。

   tasks["compile${variant.name}Renderscript"].sourceOutputDir = aptOutput

これは、 Renderscript 用に用意されている Source Root (元のパスは "${project.buildDir}/source/rs/${variant.dirName}" ) を強引に書き換えることで実現しています。この設定をした状態で Renderscript を使用すると、 apt_generated の下に Renderscript のファイルも生成されてしまいます。

Android Studio で開いてみる

この設定を行ったプロジェクトを Android Studio で開くと以下のようになります。

use_r_folder.png

build/source/r/ の下にファイルが生成されています。また、build/source/r/release のフォルダのアイコンが水色になっていることから現在選択されている Build Variant に対応するフォルダが Source Root として使用されていることがわかります。

以下は Renderscript 用のフォルダを乗っ取る場合の例です。

take_over_renderscript.png

先ほど存在した build/source/rs フォルダがなくなった代わりに build/source/apt_generated が作成され、選択されている Build Variants に対応したフォルダが Source Root として使用されます。

今回の内容は https://github.com/zaki50/android_gradle_template で公開している Gradle + Android Studio 用の雛形プロジェクトにも反映させておきました。

6/21 追記

このエントリの(暫定版)を外す方法が何かないかと思って adt-dev で 質問してみた のですが、残念ながら Xavier さんの回答は

There's no easy way to do this now, it's all going to be ugly.

でした...

本日のツッコミ(全2件) [ツッコミを入れる]
_ kimukou_26 (2013-06-24 07:33)

初歩的な質問かもしれませんが、すみません。 <br> <br>上記と同じ構成にした時 (android annotaionを使用 <br> <br>AndroidStudioから 実機転送時に <br> <br>Launching application: com.example.abstest/com.example.abstest.MainActivity_. <br>DEVICE SHELL COMMAND: am start -n "com.example.abstest/com.example.abstest.MainActivity_" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER <br>Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example.abstest/.MainActivity_ } <br>Error type 3 <br>Error: Activity class {com.example.abstest/com.example.abstest.MainActivity_} does not exist. <br> <br>という形でエラーがが出てしまうのですが,これはどう回避すればよいのでしょうか? <br><コンソールからの gradlew clean build debug では転送可能なのは確認しています <br> <br> <br>

_ vvakame (2013-06-26 08:17)

apply plugin: 'android-library' の場合 Could not find property 'applicationVariants' って言われるんだけどLibraryProjectってvariant持てないの?ってzakiさんに聞いたら libraryVariants 使え って教えてもらったのでここにメモっておく


«前の日記(2012-12-19) 最新