スタッフブログ

STAFF BLOG

アプリ開発日誌

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」を押して同期しましょう。

まとめ

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

BACK

お問合せ

イーディーエーに興味をお持ちいただいて
ありがとうございます!
スマホアプリに関するご相談、
お見積りや弊社へのご質問など、
お気軽にお問い合わせください。
担当者より折り返しご連絡させていただきます。

    お名前必須
    会社名
    メールアドレス必須
    電話番号必須
    お問合わせ種別必須
    お問合わせ内容必須

    アンケートにご協力ください。
    弊社サイトへはどのようにしてアクセスされましたか?

    個人情報のお取扱いに関する同意事項

    1.事業者の氏名又は名称

    株式会社イーディーエー

    2.個人情報保護管理者の氏名又は職名、所属及び連絡先

    個人情報保護管理者 小宮 保人
    Mail:[email protected]

    3.取得した個人情報の利用目的

    当フォームで取得した個人情報は、お問い合わせに関する回答のために利用し、目的外利用はいたしません。

    4.弊社が取得した個人情報の第三者への委託、提供について

    弊社は、ご本人に関する情報をご本人の同意なしに第三者に委託または提供することはありません。

    5.個人情報保護のための安全管理

    弊社は、ご本人の個人情報を保護するための規程類を定め、従業者全員に周知・徹底と啓発・教育を図るとともに、その遵守状況の監査を定期的に実施いたします。
    また、ご本人の個人情報を保護するために必要な安全管理措置の維持・向上に努めてまいります。

    6.個人情報の開示・訂正・利用停止等の手続

    ご本人が、弊社が保有するご自身の個人情報の、利用目的の通知、開示、内容の訂正、追加又は削除、利用の停止、消去及び第三者への提供の停止を求める場合には、下記に連絡を頂くことで、対応致します。

    株式会社イーディーエー 個人情報お問合せ窓口
    〒106-0032 東京都港区六本木7丁目14番23 ラウンドクロス六本木4F
    TEL:03-5422-7524 FAX:03-5422-7534
    Mail:[email protected]

    7.ご提供いただく情報の任意性

    個人情報のご提供は任意ですが、同意を頂けない場合には、第3項にあります利用目的が達成できない事をご了承いただくこととなります。

    8.弊社Webサイトの運営について

    弊社サイトでは、ご本人が弊社Webサイトを再度訪問されたときなどに、より便利に閲覧して頂けるよう「クッキー(Cookie)」という技術を使用することがあります。これは、ご本人のコンピュータが弊社Webサイトのどのページに訪れたかを記録しますが、ご本人が弊社Webサイトにおいてご自身の個人情報を入力されない限りご本人ご自身を特定、識別することはできません。
    クッキーの使用を希望されない場合は、ご本人のブラウザの設定を変更することにより、クッキーの使用を拒否することができます。その場合、一部または全部のサービスがご利用できなくなることがあります。