GradleでAndroidを継続的インテグレーションするための雛形

旧ブログ(blog.ogaclejapan.com)から移行してきた古い記事です。 移行に伴い、一部のレイアウトが崩れている可能性もありますmm

Author's Avatar mini

Masaki Ogata

AuthorMasaki Ogata

Published

Updated

GradleでAndroidビルドを検証したので雛形をUPしておく。

参考にしたのは「Android Tools Project Site」のサイト

※ちなみにGradleはバージョン1.2じゃないと動かないらしいので注意!

build.gradleの雛形

以下のコードをAndroidプロジェクト直下にbuild.gradleという名前で保存する。

buildscript {
repositories {
mavenCentral()
}

dependencies {
classpath 'com.android.tools.build:gradle:0.2'
}
}

project.ext {
def projectProps = new Properties()
file("project.properties").withInputStream {
stream -> projectProps.load(stream)
}
props = new ConfigSlurper().parse(projectProps)
manifest = new XmlSlurper().parse(file("AndroidManifest.xml"))
}

apply plugin: 'android'

android {
target = project.props.target
defaultConfig {
//manifestファイルと異なるビルドをする場合のみコメント外すこと
//packageName = manifest.@package.text()
//versionCode = manifest.@versionCode.text()
//versionName = manifest.@versionName.text()
}
buildTypes {
debug {
//packageNameSuffix = ".dev"
debuggable = true
debugSigned = true
zipAlign = true
}
staging {
//packageNameSuffix = ".stg"
debugSigned = true
zipAlign = true
}
release {
debugSigned = false
zipAlign = true
}
}
sourceSets {
main {
manifest {
srcFile 'AndroidManifest.xml'
}
java {
srcDir 'src'
}
res {
srcDir 'res'
}
assets {
srcDir 'src'
}
resources {
srcDir 'src'
}
}
test {
java {
srcDir 'tests/src'
}
resources {
srcDir 'tests/src'
}
}
debug {
java {
srcDir 'profiles/dev/src'
}
resources {
srcDir 'profiles/dev/src'
}
}
staging {
java {
srcDir 'profiles/stg/src'
}
resources {
srcDir 'profiles/stg/src'
}
}
release {
java {
srcDir 'profiles/release/src'
}
resources {
srcDir 'profiles/release/src'
}
}
}
}

sourceCompatibility=1.6
targetCompatibility=1.6

repositories {
mavenCentral()
mavenLocal()
}

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

tasks.withType(Compile) {
options.encoding = 'UTF-8'
}
packageNameSuffixについて

生成するapkに指定した名前を付加してくれるものと思ってたら、全く違った。。

このパラメータの用途は環境ごとに異なるapkを1つの端末に同時にインストールすることができるようにするための設定だと思われる。 manifestファイルのpackage属性を環境ごとに一意にすることで別物のアプリとして認識させるためのパラメータのようだ。

生成されるmanifestファイルはこんな感じかな。

(debug版ビルド)

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ogaclejapan.dev"
android:versionCode="1"
android:versionName="1.0">

...
</manifest>

(staging版ビルド)

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ogaclejapan.stg"
android:versionCode="1"
android:versionName="1.0">

...
</manifest>

ただし、このパラメータを使用するにはmanifestファイルの書き方に注意する必要があるのでメモしておく。

  • manifestファイルのapplication要素以下すべてのパッケージ名をフルパスで定義すること

    ⇒ドット.からの相対パスで要素を定義するとpackage属性を基準にされてしまうので定義したクラスまでのパッケージパスがズレる

プロファイル環境毎に設定ファイルを切り替える

mavenのprofiles機能と同様にdevやstagingなど環境毎に変わるものを上書きすることができる。 この雛形ではprofiles/xx/srcがprofilesと同じ役目を果たしている。

現状、切り替え可能なことを確認できたのは、

  • resources配下のプロパティファイル(xx.properties)
  • res/values配下の設定ファイル

のみ。

現段階のバージョンが未実装なのか不明だが、 Javaコードや画像ファイル類は反映されなかった。

...
debug {
resources {
srcDir 'profiles/dev/src'
}
res {
srcDir 'res'
}
}
staging {
resources {
srcDir 'profiles/stg/src'
}
res {
srcDir 'res'
}
}
release {
resources {
srcDir 'profiles/release/src'
}
res {
srcDir 'res'
}
}
...

※sourceSetsより前でbuildTypesを定義しないとビルド時にエラーが発生するようなので注意!

フォルダ構成がMaven系の場合

既にフォルダ構成がsrc/main/java,src/main/resourcesなどmaven系プロジェクトの場合はsourceSetsを以下のように変更してやる必要がある。

...
sourceSets {
main {
manifest {
srcFile 'src/main/AndroidManifest.xml'
}
java {
srcDir 'src/main/java'
}
res {
srcDir 'src/main/res'
}
assets {
srcDir 'src/main/assets'
}
resources {
srcDir 'src/main/resouces'
}
}
test {
java {
srcDir 'src/test/java'
}
resources {
srcDir 'src/test/resources'
}
}
debug {
java {
srcDir 'src/dev/java'
}
resources {
srcDir 'src/dev/resources'
}
}
staging {
java {
srcDir 'src/staging/java'
}
resources {
srcDir 'src/staging/resources'
}
}
release {
java {
srcDir 'src/release/java'
}
resources {
srcDir 'src/release/resources'
}
}
}
...

ちなみにこのプラグインはデフォルトmaven形式を推奨しているので、sourceSetsのデフォルトが上記のような値になっていると思われる。従ってsourceSetsの定義はいらないかも。。

※あと現時点ではEclipseのADTがこのフォルダ構成を認識してくれないので、おすすめしない。

ネイティブ共有ライブラリ(libxx.so)をApkに含める方法

実際のところ、これが一番ハマった。。 EclipseのADTプラグインはlibs/armeabi配下に置いとけば自動で含めてくれてたし、 そもともADTプラグインがどうやってapkを生成しているかなんて全く知らん。

色々なサイトを探しまわった結果、adt-devのForumに同じ質問をしているスレを発見。 https://groups.google.com/forum/?fromgroups=#!topic/adt-dev/SOs6mxZGjMM

Support for .so libraries in Gradle builds? で、結論からいうと、現段階のバージョンでは未実装とのこと…orz

しかし、神現る!! このスレにHackしたという人の書き込みがあり、Hack方法をGistにアップしてくれている。

ただGistのコードだと要らない部分も含まれていたり、 libs配下にsoファイルを置いているケースではなかったりするので、実際に試してたHack定義をメモしておく。

task copyNativeLibs(type: Copy) {
def libsDir = "$projectDir/libs"
from(libsDir) { include '**/*.so' }
into new File(buildDir, 'native-libs')
}

tasks.withType(Compile) { compileTask -> compileTask.dependsOn copyNativeLibs }

clean.dependsOn 'cleanCopyNativeLibs'

//この定義があるとgradle tasksコマンドが例外で発生する
tasks.withType(com.android.build.gradle.PackageApplicationTask) { pkgTask ->
pkgTask.jniDir new File(buildDir, 'native-libs')
}

一応コード中にコメントで記載したが、 この定義をしてからGradleタスクのtasksコマンド使用できなくなった。。 解決方法があるのか今のところ不明なため、共有ライブラリが必要なければムリにこのHackを入れないほうが良さげ。

Author's Avatar

Masaki Ogata ( a.k.a. ogaclejapan )

5年間ほどAndroidアプリ開発者へ型変換していましたが、Designも含めてサービス開発に必要な技術をすべて吸収していきたいマン。WebとBackendの記憶を只今アップデート中 :P

もし気に入っていただけたら記事シェアのご協力をお願いします!!