アプリ開発日誌
2017.02.14
Android Studioのmoduleのbuild.gradleを分割してみる
おはようございます。プログラマーの小林です。
さて、本日のテーマは「Android Studioのbuild.gradleを分割する」です。
アプリケーション開発をする上で、ビルドファイルをいじるというのは避けて通れません。
今回はそのビルドファイルを分割して、処理をモジュール化してみましょう。
build.gradleは分割できる
最近、ビルドファイルがやたら長くなってしまい、「可読性低いなぁ」と思うことがありました。
そこで、Gradleのリファレンスを参考に、プロジェクトのビルドファイルを分割してみました。
簡単かつ手軽にできるので、皆さんも是非やってみましょう。
プロジェクトの構成
今回のプロジェクトの構成は以下の通りです。
-project
–app
—src
—-debug
——AndroidManifest.xml
—-develop
——AndroidManifest.xml
—-main
—-release
——AndroidManifest.xml
—-build.gradle
ビルドファイル
今回の分割前のビルドファイルの中身です。
apply plugin: 'com.android.application'
apply plugin: 'ライブラリのプラグイン'
import java.text.SimpleDateFormat
import java.util.regex.Pattern
def VERSION_NAME = "1.0.0"
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
signingConfigs {
develop {
storeFile file(devKeystore)
keyAlias devKeyAlias
storePassword devKeyPass
keyPassword devKeyAliasPass
}
}
defaultConfig {
applicationId "パッケージ名"
minSdkVersion 19
targetSdkVersion 25
versionName VERSION_NAME
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
// サポートする言語のリソースのみ保持
resConfigs "ja"
}
buildTypes {
debug {
shrinkResources true
minifyEnabled true
applicationIdSuffix ".debug"
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
testProguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-test-rules.pro'
zipAlignEnabled true
}
develop {
debuggable true
shrinkResources true
minifyEnabled true
signingConfig signingConfigs.develop
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
zipAlignEnabled true
}
release {
debuggable false
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
zipAlignEnabled true
}
}
// Releaseでビルドバージョンを自動インクリメント
task('incrementVersionCode') << {
// AndroidManifest取得
def manifestFile = file('src/release/AndroidManifest.xml')
def manifestText = manifestFile.getText();
// versionCodeの記述を検索
def pattern = Pattern.compile("versionCode=\"(\\d+)\"")
def matcher = pattern.matcher(manifestText);
matcher.find()
def versionCode = Integer.parseInt(matcher.group(1));
// versionCodeの値を更新
versionCode++;
def resultManifest = matcher.replaceAll("versionCode=\"" + versionCode + "\"")
manifestFile.write(resultManifest);
}
// Debugビルドでエラーが出ないようにReleaseビルドの時のみ上記のタスクを実行
tasks.whenTaskAdded { task ->
if(task.name == 'generateReleaseBuildConfig') {
task.dependsOn 'incrementVersionCode'
}
}
// release版のapkファイル名を変更
applicationVariants.all { variant ->
if (variant.buildType.name.equals('release')) {
variant.outputs.each { output ->
if (output.outputFile != null && output.outputFile.name.endsWith('.apk')) {
def date = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date())
def filename = "app_${date}.apk"
output.outputFile = new File(output.outputFile.parent, filename);
}
}
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
testCompile 'junit:junit:4.12'
// support libraries
compile 'com.android.support:appcompat-v7:25.1.1'
compile 'com.android.support:recyclerview-v7:25.1.1'
compile 'com.android.support:support-fragment:25.1.1'
// google play services
compile 'com.google.android.gms:play-services-ads:10.0.1'
// firebase-messaging
compile 'com.google.firebase:firebase-messaging:10.0.1'
// 40行ほどライブラリの記述
}
apply plugin: 'com.google.gms.google-services'
なかなかの長さですね。使用するプラグイン(DeployGateなど)によってはもっと長くなります。
ファイルを編集するときに何度もスクロールしなければならないので、結構面倒です。
それでは、ファイルを分割していきましょう。
1.releaseビルドの処理を分割
android内に定義されているreleaseビルドに関する処理を分割してみましょう。
まずbuild.gradleと同じディレクトリに新しいビルドファイルを作成します。
buildReleaseConfig.gradleとでもしましょうか。
中身は以下のようになります。
import java.text.SimpleDateFormat
import java.util.regex.Pattern
android {
// Releaseでビルドバージョンを自動インクリメント
task('incrementVersionCode') << {
// AndroidManifest取得
def manifestFile = file('src/release/AndroidManifest.xml')
def manifestText = manifestFile.getText();
// versionCodeの記述を検索
def pattern = Pattern.compile("versionCode=\"(\\d+)\"")
def matcher = pattern.matcher(manifestText);
matcher.find()
def versionCode = Integer.parseInt(matcher.group(1));
// versionCodeの値を更新
versionCode++;
def resultManifest = matcher.replaceAll("versionCode=\"" + versionCode + "\"")
manifestFile.write(resultManifest);
}
// Debugビルドでエラーが出ないようにReleaseビルドの時のみ上記のタスクを実行
tasks.whenTaskAdded { task ->
if(task.name == 'generateReleaseBuildConfig') {
task.dependsOn 'incrementVersionCode'
}
}
// release版のapkファイル名を変更
applicationVariants.all { variant ->
if (variant.buildType.name.equals('release')) {
variant.outputs.each { output ->
if (output.outputFile != null && output.outputFile.name.endsWith('.apk')) {
def date = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date())
def filename = "app_${date}.apk"
output.outputFile = new File(output.outputFile.parent, filename);
}
}
}
}
}
リリースビルド時の処理を分割しました。込み入った処理が多いので分割できると嬉しいですね。
2.ライブラリ定義を分割
dependenciesを別ファイルに定義してみましょう。
この部分は込み入った処理はないのですが、単純に長くなりがちなので、
分割できるとbuild.gradleがすっきりします。
build.gradleと同じディレクトリに新しいビルドファイルを作成します。
buildDependencies.gradleとでもしましょうか。
apply plugin: 'ライブラリのプラグイン'
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
testCompile 'junit:junit:4.12'
// support libraries
compile 'com.android.support:appcompat-v7:25.1.1'
compile 'com.android.support:recyclerview-v7:25.1.1'
compile 'com.android.support:support-fragment:25.1.1'
// google play services
compile 'com.google.android.gms:play-services-ads:10.0.1'
// firebase-messaging
compile 'com.google.firebase:firebase-messaging:10.0.1'
// 40行ほどライブラリの記述
}
apply plugin: 'com.google.gms.google-services'
この部分も分割できるんですね。これで必要な部分は全て分割し終わりました。
3.分割された後のbuild.gradle
さて、ファイルを分割しましたが、このままではビルドの定義に加わっていません。
gradleのモジュールを追加するには、以下の宣言をする必要があります。
apply from: 'yourFileName.gradle'
それでは早速使ってみましょう。
apply plugin: 'com.android.application'
def VERSION_NAME = "1.0.0"
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
signingConfigs {
develop {
storeFile file(devKeystore)
keyAlias devKeyAlias
storePassword devKeyPass
keyPassword devKeyAliasPass
}
}
defaultConfig {
applicationId "パッケージ名"
minSdkVersion 19
targetSdkVersion 25
versionName VERSION_NAME
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
// サポートする言語のリソースのみ保持
resConfigs "ja"
}
buildTypes {
debug {
shrinkResources true
minifyEnabled true
applicationIdSuffix ".debug"
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
testProguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-test-rules.pro'
zipAlignEnabled true
}
develop {
debuggable true
shrinkResources true
minifyEnabled true
signingConfig signingConfigs.develop
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
zipAlignEnabled true
}
release {
debuggable false
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
zipAlignEnabled true
}
}
}
apply from: 'buildReleaseConfig.gradle'
apply from: 'buildDependencies.gradle'
「android」の部分がbuild.gradleと他ファイルで存在しますが、ビルド時に処理はマージされます。
これでビルドファイルの分割は完了です。
注意
新しく作成したファイルですが、build.gradleと異なり、
編集した時にプロジェクトと同期する通知が出ません、
なので編集した時はAndroid StudioのToolbarの
「Sync Project With Gradle Files」を押して同期しましょう。
まとめ
いかがでしたでしょうか、いつものようになかなか地味な内容だったかと思います。
こういった作業はプロダクトの出来に直接関係があるわけではありませんが、
開発をする上で、こういった作業は後々の効率の面でとても重要になります。
例えば、「この処理、簡単に別プロジェクトで使いまわしたいなぁ」と思った時などですね。
皆さんもビルドファイルをいじり倒して、開発を効率化しましょう!