Android Studioのmoduleのbuild.gradleを分割してみる

by

スクリーンショット 2017-02-10 17.24.04

おはようございます。プログラマーの小林です。

さて、本日のテーマは「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」を押して同期しましょう。

 

まとめ

いかがでしたでしょうか、いつものようになかなか地味な内容だったかと思います。
こういった作業はプロダクトの出来に直接関係があるわけではありませんが、
開発をする上で、こういった作業は後々の効率の面でとても重要になります。
例えば、「この処理、簡単に別プロジェクトで使いまわしたいなぁ」と思った時などですね。
皆さんもビルドファイルをいじり倒して、開発を効率化しましょう!

小林良
2014年10月にEDAで1ヶ月間インターン研修を受けた縁で
2016年4月入社。入社前は専門学校でプログラムを専攻。
好きな言語はC++、グラフィックスAPIはOpenGL。
最近はマルチスレッドのパターンを勉強中。

Egg Device Application

東京品川のスマホアプリ開発会社です。
一般アプリ、業務用アプリからVRまで開発可能。

ライター一覧

求人情報

スマホアプリ開発の
相談を受け付けています

メールでのご相談

お電話でのご相談
TEL 03-5422-7524
平日10:00~18:00