ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

Gradle による Android アプリのビルド

週末は Gradle と Android アプリのビルドツールと戯れていたので、得られた知見などを書き残しておく。

更新記録

  • 2014-01-07: Android Gradle プラグイン 0.7.1 (Android Studio 0.4 対応版) に合わせて内容を更新しました。

Gradle について

Java 周辺のプロジェクト管理ツール (ビルドシステム?) といえば Ant や Maven があるけど、最近 (?) 注目を集めているのが Gradle。 XML で記述される Ant や Maven とは違い、Gradle のビルドファイルは Groovy による DSL で記述される。 Ruby におけるビルドツールである Rake と似たような雰囲気。 記述の容易さはもちろんのこと、Ant のタスクをそのまま使えたり依存関係解決に Maven リポジトリを使えたりする など、良さそうな雰囲気を醸し出している。

今のところの難点としては、Maven を使っている場合と比べて依存関係の解決が甘い (?) ことがあること。

ただ、これは Android アプリ開発時には問題にはならないと思う。

Gradle と Android アプリのビルド

Android アプリのビルドツールとして公式的には Ant が採用されているが、最近 Gradle を使った新しいビルドシステムを開発しているらしい (Android Studio に合わせて公開されたのかな)。

Gradle のプラグインとして android プラグインが提供されている。 このプラグインは Maven のセントラルリポジトリで公開されているので、build.gradle に次のように記述するだけで Android アプリのビルド用のあれこれが使えるようになる。

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.7.1+'
    }
}

apply plugin: 'android'

android {
    compileSdkVersion 19
}

buildscript の部分では、ビルドスクリプト自体の依存関係を解決している (参考: 第59章 ビルドロジックの体系化)。 android の部分が Android アプリのビルドのための設定を記述する場所。

サンプルプロジェクトを作ってみた

実際に試してみないとわからないのでサンプルプロジェクトを作ってみた。 ビルド方法などは README に書いてある。

Gradle ラッパーについて

サンプルプロジェクトに含まれている次のファイルは Gradle ラッパーのためのもの。

  • gradlew ファイル
  • gradlew.bat ファイル
  • gradle/ ディレクトリ

Gradle ラッパー (gradlew) というのは gradle コマンドの代わりに使用されるもので、(既にシステムにインストール済みで) gradle コマンドが使用できるならば *1 それに処理を委譲し、使用可能でなければインターネット経由で gradle を取得してローカルディレクトリにインストールしてそれを使用する、というものである。

Android Studio で新しくプロジェクトを作るとこれらのファイルが含まれているので、バージョン管理する必要があるのかどうか悩むところではあるが、あると便利といえば便利 (特にバージョンが固定されるのが良い) なので、特に理由がなければバージョン管理すれば良さそう。 (Gradle コミュニティの文化としては、Gralde ラッパーはバージョン管理するものであるようだ。)

マルチプロジェクトについて

上に書いたサンプルプロジェクトでは、アプリ本体と、ライブラリ Volley をそれぞれサブプロジェクトとしており、マルチプロジェクト構成になっている。 ライブラリは基本的に AAR や JAR で扱えるのが一番良い *2 と思うのだけど、Volley みたいに公式的な JAR がない場合や、自社で開発しているライブラリを使用しながらどんどん変更を加えていきたい場合などは、ライブラリをサブプロジェクトとしてプロジェクトに追加するのが良さそうだと思う。

マルチプロジェクト構成は次のような感じになるっぽい (間違ってるかも)。

  • 親プロジェクトに settings.gradle というファイルがあって、サブプロジェクトのディレクトリがどれかを指し示す
  • 各サブプロジェクトは親プロジェクト内の好きな場所 (?) に置ける
  • 親プロジェクトも各サブプロジェクトもそれぞれ独自の build.gradle を持つことができる
  • 親プロジェクトで Gradle のタスクを実行すると、すべてのサブプロジェクトの同じタスクが実行される

サブプロジェクトのビルドスクリプトを親プロジェクトの build.gradle に記述できる

結構面白いと思ったのが、サブプロジェクトのビルドスクリプトを、そのサブプロジェクトの build.gradle ではなく、別の build.gradle (例えば親プロジェクトの build.gradle) に記述できるということ。

例えば今回のサンプルプロジェクトだと、サブプロジェクトとして Volley を使っているわけだけど、リポジトリは本家のものを使用しているのでサブプロジェクト内の build.gradle を変更することができない。 なので親プロジェクトの build.gradle で Volley 用のビルド設定を変更する、なんてことをやってみた。

また、昔の Volley は build.gradle を持っていなかったので、それこそビルドスクリプトを全て親プロジェクトに書いたりしていた。

基本的にはサブプロジェクトのビルドスクリプトはそのリポジトリの中に入れておきたいところではある。

ディレクトリ構成

Gradle の Java プラグインでのデフォルトのディレクトリ構成は、Maven と同じで src/main/java 以下に Javaソースコードを配置する、というものらしい (参考: Chapter 23. The Java Plugin)。 Android アプリ用のプラグインでも同じようなディレクトリ構成が採用されている。

もちろんデフォルトのディレクトリ構成じゃないプロジェクトでも簡単に対応することができる。 サンプルプロジェクトでも、Volley のディレクトリ構成に合わせてビルドスクリプトを記述した。

ソースコードのエンコーディング指定

javac コマンドは、ソースコードのエンコーディング設定としてデフォルトではシステムのエンコーディングを使用する。 システムのエンコーディングが UTF-8 になっている環境 (多くの *iux 系環境ではそうだと思う) では問題になることは少ないが、Windows を使用していると痛い目に合うことが多い。 Gradle で Java のコンパイル時のエンコーディング指定をするのは次のように書くのが今のところ最良のような気がする。

tasks.withType(JavaCompile) {
    options.encoding = 'UTF-8'
}

コマンドで PC でビルドした Android アプリのパッケージを Android 端末にインストールする

Android Gradle プラグインは、Android 端末にアプリのパッケージをインストールするタスクも提供している。 installXxxx というタスクがそれである。 (./gradlew tasks で確認できる。) 例えば、Debug ビルドのパッケージをインストールする場合は、次のようにすればよい。

./gradlew installDebug

すると、接続している Android 端末に、debug ビルドのパッケージがインストールされる。

古い内容 (自分でタスクを起動する処理を書いてみた)

Gradle のタスクとしてアプリを起動するタスクを書いておけば便利。 android プラグインで提供されてるような気もするけど見つけられなかった。 → 上で書いたように、パッケージをインストールするタスクは提供されている。 起動までするタスクはないようだけど。

// Android SDK Tools に含まれる adb コマンドのパスを文字列で返す
def getAdbCmdPath = {
    def androidPlugin = project.plugins.getPlugin('android')
        // プラグインオブジェクトの取得方法: http://www.gradle.org/docs/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:plugins
    def sdkDir = androidPlugin.sdkDirectory
        // android プラグインのソースコード: https://android.googlesource.com/platform/tools/build/
    return new File(sdkDir, 'platform-tools/adb').toString()
}

task installToDevice(type:Exec) {
    // apk ファイル名 (TODO 自動で取得できるように)
    def apkPackageName = 'GradleBuildSystemSample-debug-unaligned.apk'

    // デバイスへのインストール
    def installCmd = getAdbCmdPath() + ' -d install -r ' + buildDir + '\\apk\\' + apkPackageName
    // コマンド実行  (Windows 用)
    commandLine 'cmd', '/c', installCmd
}

task runOnDevice(type:Exec) {
    // Main Activity のパッケージ名とアクティビティクラス名 (TODO 自動で取得できるように)
    def mainPackage = 'info.vividcode.android.app.gradlebuildsystemsample'
    def mainActivity = 'MainActivity'

    // コマンド実行 (Windows 用)
    commandLine 'cmd', '/c', getAdbCmdPath() + ' shell am start -a android.intent.action.MAIN -n ' + mainPackage + '/.' + mainActivity
}

起動する Activity のパッケージ名などを動的に取得できるようにしたいところ。

Eclipse 対応

ビルドシステムとして Gradle を使えば Android Studio にいい感じにインポートできるが、Eclipse へのインポートはそう簡単にはいかない。

Gradle のデフォルトのディレクトリ構成では、EclipseAndroid プラグイン (com.android.ide.eclipse.adt) が対応できないっぽい。 プラグインのソースコードを読んだ感じだと、res ディレクトリの位置などはプロジェクト直下にあることが想定されていて、外部から変更できない感じだった *3

Eclipse 上で Gradle のタスクを実行すること自体は難しくなくて、Gradle Integration for Eclipse というプラグインを使用すればよい。 今回のサンプルプロジェクトは主に Eclipse 上で開発したのだが、その際もこのプラグインを使用して Gradle のビルドを実行するという感じで開発していた。 *4

Gradle をビルドシステムとして使用するプロジェクトで、Eclipse でも開発できるようにするには、とりあえずは次のようにするのが良いのかなーと思った。

  • EclipseAndroid プラグインが認識できるディレクトリ構成にする
  • Android プロジェクトとして Eclipse にインポートする
  • 必要な依存関係は手で修正する

まとめ

Building and Testing With Gradle

Building and Testing With Gradle

  • Gradle 便利
  • Groovy 便利
  • 今後は Android アプリのビルドも Gradle で管理するのが良さそう
    • Eclipse を使わないとビルドできないようなプロジェクトは良くない
    • Android Studio を使うなら Gradle でプロジェクト管理して何も問題ないが、Gradle を使ったプロジェクトを Eclipse でも開発できるようにするならディレクトリ構成とかをちょっと考える必要がある
    • もしくは EclipseAndroid プラグインに手を加えるか (そのうち公式でサポートされる気はする)
  • マルチプロジェクト構成とかディレクトリ構成とかプラグイン周りのこととか、基本的に Gradle を勉強しなきゃなー、という感じ

参考

次のエントリも参考になります。

*1:より正確にいうと、指定されているバージョンの gradle コマンドが使用できるならば

*2:さらに言えば Maven のセントラルリポジトリにあると最高ですね

*3:ざっと見ただけなので、そもそも見るファイルが間違ってるかもしれないし、外部からディレクトリの位置を変更したりできるのかもしれない

*4:Android SDK Tools の Eclipse プラグインの恩恵を受けていたわけではないので Eclipse 上で開発して便利だったわけでもない