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

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

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

Android Studio 0.4.0 への移行

Android Studio 0.4.0 がリリースされていますね! わー、ぱちぱち。

Android Studio 0.4.0 への移行は、単にバージョンを上げるだけという感じではなかったので、苦労した箇所などを簡単にメモしておきます。

Gradle と Android Gradle プラグインのバージョンアップ

Android 0.4.0 では、Gradle 1.9、Android Gradle プラグイン 0.7 が必要となっています。 それぞれバージョンアップを行いましょう。 Gradle は Gradle ラッパーとしてプロジェクト内に持っていることが多いと思うので、gradle/wrapper/gradle-wrapper.properties を次のように書きかえればいいはずです。 *1

-distributionUrl=http\://services.gradle.org/distributions/gradle-1.8-bin.zip
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.9-bin.zip

Android Gradle プラグインのバージョンアップは、build.gradle の記述を次のような感じで変更すればよいですね。 (0.7.1 がリリースされており、後述する packagingOptions の exclude は 0.7.1 から使えるようになったものなので、バージョンは 0.7.1 以降を指定しています。)

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

build.gradle の記述変更

Android Gradle プラグイン 0.7 では、大きめの変更が入っていますので、そのままビルドしようとしても失敗する可能性があります。 とりあえずビルドしてみて、エラーが出たら必要に応じて変更を加えればよいでしょう。

以下に、私が行った変更を書いておきます。

buildConfig から buildConfigField に変更

Android Gradle プラグイン 0.6.x のときには、BuildConfig クラスのフィールドを定義するために、次のように buildConfig メソッドを使用することができました。

android {
    buildTypes {
        release {
            buildConfig "public final static boolean XXX_TEST_MODE = false;"
        }

0.7.0 以降ではこれは使用できなくなっており、代わりに buildConfigField メソッドを使用します。 buildConfigField は 3 引数をとるメソッドで、前から順にフィールドの型、フィールドの識別子、フィールドの値を設定します。

android {
    buildTypes {
        release {
            buildConfigField "boolean", "XXX_TEST_MODE", "false"
        }
私がはまったところ

私はもともと次のようなビルドスクリプトを書いていました。

android {
    buildTypes {
        debug {
            buildConfig "public final static boolean XXX_TEST_MODE = false;"
        }
        testtarget.initWith(buildTypes.debug)
        testtarget {
            buildConfig "public final static boolean XXX_TEST_MODE = true;"
        }

で、Android Gradle プラグイン 0.6.x の頃までは問題なくビルドできていたのですが、0.7.1 に移行して次のように書きかえるとビルドできなくなりました。

android {
    buildTypes {
        debug {
            buildConfigField "boolean", "XXX_TEST_MODE", "false"
        }
        testtarget.initWith(buildTypes.debug)
        testtarget {
            buildConfigField "boolean", "XXX_TEST_MODE", "true"
        }

ビルドに失敗する際のエラー出力は次のようなもの。

.../BuildConfig.java:14: エラー: 変数 XXX_TEST_MODEはすでにクラス BuildConfigで定義されています
  public static final boolean XXX_TEST_MODE = true;
                              ^

debug ビルドタイプでの buildConfigField の設定がそのまま testtarget に受け継がれて、testtarget ビルドタイプでも同名のフィールドを定義しているので問題が起こる、という感じでした。 で、どうすればいいのか結構悩んだのですが、単に debug ビルドタイプで buildConfigField メソッドを呼び出す前に initWith して testtarget ビルドタイプの初期化をしてやれば良いのでした。

android {
    buildTypes {
        // `initWith` した後に `debug` ビルドタイプで `buildConfigField` 呼び出しをすればよい
        testtarget.initWith(buildTypes.debug)
        debug {
            buildConfigField "boolean", "XXX_TEST_MODE", "false"
        }
        testtarget {
            buildConfigField "boolean", "XXX_TEST_MODE", "true"
        }

jar ファイルに含まれるリソースファイルがだぶる問題

ビルドを実行すると次のようなエラーが発生するかもしれません。 これは、libs ディレクトリの JAR ファイルを展開したときに、同じパスのリソースファイルが複数の JAR ファイルに存在すると発生するエラーのようです。

Error: duplicate files during packaging of APK ...\build\apk\App-testtarget-unaligned.apk
        Path in archive: META-INF/NOTICE.txt
        Origin 1: ...\App\libs\xxxx-4.0.0.jar
        Origin 2: ...\App\libs\yyyy-4.0.0.jar
You can ignore those files in your build.gradle:
        android {
          packagingOptions {
            exclude 'META-INF/NOTICE.txt'
          }
        }

どちらの JAR に含まれるリソースファイルも必要でない場合 (例えばパッケージに含めなくて良い NOTICE.txt など) は、そのリソースファイルをパッケージング時に除外するようにすることでこのエラーを回避できます。 エラーメッセージにも書かれていますが、次のようなコードを書けばよいです。

android {
    packagingOptions {
        exclude 'META-INF/NOTICE.txt'
    }
}

これは Android Gradle プラグインの 0.7.1 で導入された機能です。

lint でエラーになってもビルドに失敗しないようにする

おそらく Android Gradle プラグインの 0.7.0 からだと思うのですが、build タスクの一部として lint タスクも実行されるようになりました。 で、デフォルトでは lint タスクでエラーが発生するとビルドが止まるようになっています。 理想的には lint でエラーが発生しないようにするのが良いと思いますが、実際には問題ない箇所でも lint のエラーになることがあった *2 ので、とりあえず lint でエラーが出てもビルドを継続するようにしました。

android {
    lintOptions {
        abortOnError false
    }

参考資料

Android Studio 0.4.0 移行とは関係ないけど Windows で Gradle を使うときの文字コードではまった

Android Studio 0.4 移行とは関係ないのですが、Windows で Gradle を使っていると、行コメントの次の行までコメント扱いされる、ということがあってはまってしまいました。

Groovy 処理系が期待するファイルの文字エンコーディング (Windows なので CP932) と、実際のファイルの文字エンコーディング (UTF-8) が違っているために起こる問題のようです。 この問題については下記の記事が参考になりました。

結局のところ、Gradle ラッパーを使っているのであれば、gradlew.bat を次のように変更したら良さそうですね。 *3

diff --git a/gradlew.bat b/gradlew.bat
index 8a0b282..c56f988 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -10,6 +10,7 @@ if "%OS%"=="Windows_NT" setlocal

 @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
 set DEFAULT_JVM_OPTS=
+set JAVA_OPTS="-Dgroovy.source.encoding=UTF-8" "-Dfile.encoding=UTF-8"

 set DIRNAME=%~dp0
 if "%DIRNAME%" == "" set DIRNAME=.

コンソール出力のエンコーディングも変化してしまうことに気付いた (追記)

-Dfile.encoding によるデフォルトファイルエンコーディングの変更を行うと、コンソール出力のエンコーディングも変化してしまうことに気付きました。 いまのところビルドスクリプトのエンコーディングを指定するいい方法はなさそうですね。

次の記事に、Gradle に変更を加えてビルドスクリプトのエンコーディングを指定できるようにすることについて書きました。

*1:私は Gradle ラッパーの更新時には毎回 jar なども生成し直してます。 念のため。

*2:具体的には、API level を確認したうえで特定 API level 以降でしか使用できないメソッドを呼び出しているような場合。

*3:"-Dgroovy.source.encoding=UTF-8" は必要かどうかわかんないですが。