読者です 読者をやめる 読者になる 読者になる

Androidアプリを新規リリースする際のあれこれ

こんにちは、投稿推進部の吉田です。 少し前に、お料理アルバムという「日々の料理を写真を記録する」ためのアプリのリリースしました。初めて会社のプロダクトのリリース作業を経験して、色々と学びがあったので共有したいと思います。

見出し

  • releaseビルドをCIで作成する
  • releaseビルドから不要なモノを排除する
  • Google Playストアのリファラを活用する
  • Google AnalyticsとGoogle Playストアの連携をする
  • リリース直前チェックリスト

releaseビルドをCIで作成する

リリース版apkはコマンドから生成できる環境を整え、CIで作成するのがお薦めです。
CI上でのビルドすることで、ビルド手順の共有が不要になり、開発チームの誰でもボタン一つでビルドが可能になります。
また、ビルドをCIに任せることで、keystoreとパスワードを開発者全員に共有する必要がなくなるので、セキュリティ的にも少し安心できます。

ここからは、コマンドでビルドするための手順を紹介します。
まずkeystoreを作りましょう。Android Studioからも作れますが、keytoolコマンドを利用することも可能です。

keytool -genkeypair -alias キーのエイリアス -keyalg RSA -keysize 2048 -sigalg SHA256withRSA -keystore ファイル名 -storetype jks -validity 365000

keystoreはデバッグ用とリリース用の2種類を用意しましょう。
keystoreの置き場ですが、デバッグ用はレポジトリに含めて問題ないので、appモジュール以下に配置しましょう。
リリース用のkeystoreはホームディレクトリ以下のandroidディレクトリ内に配置したと想定して、以後は説明します。

今作成したkeystoreを使って、コマンドからreleaseビルドを作成するために、次の設定をandroid以下に追加します。

android {
  // ここから追加
  signingConfigs {
      debug {
          storeFile = file('./debug.keystore')
          storePassword = 'debug_password'
          keyAlias = 'app_alias'
          keyPassword = 'debug_password'
      }
      release {
          storeFile = file('./release.keystore')
          storePassword "release_store_pass"
          keyAlias "app_alias"
          keyPassword "release_key_pass"
      }
  }
}

signingConfigsにkeystoreの情報を追加したことで、コマンドからreleaseビルドの生成が可能になりました。
しかし、これではbuild.gradleにrelease用のkeystoreやパスワードが直書きされていて、微妙です。
そこでreleaseの設定をgitなどでバージョン管理を行わない別ファイルに切り出しましょう。

先ほどのbuild.gradleからreleaseの設定のみを切り出したものが以下になります。
このファイルをrelease.gradleとし、プロジェクトのルートディレクトリに配置します。
この際に.gitignoreの設定などでバージョン管理の対象から外す操作も同時に行いましょう。

signingConfigs {
    release {
        def HOME = System.getenv()["HOME"]
        storeFile file("${HOME}/android/release.keystore")
        storePassword "release_store_pass"
        keyAlias "your_app_alias"
        keyPassword "release_key_pass"
    }
}

次にbuild.gradleからrelease.gradleを呼べるようにしましょう。
signingConfigsの中でrelease.gradleの設定を読み込み、手元にファイルがない場合は、 debugビルドの情報をreleaseビルドにも適応するように書き換えたものが、以下になります。

signingConfigs {
    debug {
        storeFile = file('./debug.keystore')
        storePassword = 'your_store_password'
        keyAlias = 'your_app_alias'
        keyPassword = 'your_store_key_password'
    }
    def releaseSettingGradleFile = new File("${project.rootDir}/release.gradle")
    if (releaseSettingGradleFile.exists()) {
        apply from: releaseSettingGradleFile, to: android
    } else {
        release {
            storeFile = debug.storeFile
            storePassword = debug.storePassword
            keyAlias = debug.keyAlias
            keyPassword = debug.keyPassword
        }
    }
}

これで、./gradlew assembleRelease を叩くとリリースビルドが作成されるようになります。

releaseビルドから不要なモノを排除する

開発の際にはデバッグに便利なライブラリを入れて開発することがあると思いますが、 debugCompileとして依存に入れておくと、それらの実装がreleaseビルドのapkに含まれなくなります。
今回はFacebook社が提供しているStethoというツールを例にdebugCompileの使い方を説明します。
まずbuild.gradleのdependenciesにStethoを追加します。

dependencies {
    debugCompile 'com.facebook.stetho:stetho:1.1.1'
}

Stethoの初期化は通常Applicationに書きますが、main以下のApplicationを継承したクラス(CusotomApplicationとします)には、依存関係上Stethoに関するコードは書けません。
そこで、app/src/debug/java以下にCusotomApplicationを継承したDebugApplicationを作成します。(debug以下のpackage構造はmainと合わせています)

public class DebugApplication extends CustomApplication {

    @Override
    public void onCreate() {
        super.onCreate();
        setUpStetho(this);
    }

    private void setUpStetho(Context context) {
        Stetho.initialize(
                Stetho.newInitializerBuilder(context)
                        .enableDumpapp(Stetho.defaultDumperPluginsProvider(context))
                        .enableWebKitInspector(Stetho.defaultInspectorModulesProvider(context))
                        .build());
    }
}

デバッグ時はDebugApplicationが参照されるように、app/debug以下にAndroidManifest.xmlを作成しApplicationにDebugApplicationを指定することで、 debugビルド時はManifestを上書きすることが出来ます。

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.cookpad.android.cookingphotoalbum">

    <application
        tools:replace="android:name"
        android:name=".application.DebugCookingStoryApplication">
    </application>
</manifest>

Google Playストアのリファラを活用する

累計/日毎のインストール数はplay developer consoleで確認することができますが、 どの導線からのインストールなのかは知ることができません。 実はGoogle Playストアのリンクにクエリパラメーターにreferrer属性を追加すると、アプリ側でリファラを受け取ることが可能です。

https://play.google.com/store/apps/details?id=com.cookpad.android.activities&hl=ja&referrer=xxxx

受け取り側はアプリのReceiverとして定義します。 GoogleAnalyticsやMixpanelを使っていると、リファラを受け取るためのReceiverが定義されているのでそちらを利用しましょう。 自分でReceiverを定義した場合このような実装になります。

public class ReferrerReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Bundle extras = intent.getExtras();
        if (extras != null) {
            String referrer = extras.getString("referrer");
            if (referrer != null) {
              // post event
            }
        }
    }

}

AndroidManifestの定義はこのようにします。

<receiver
        android:name="com.your.package.name.ReferrerReceiver"
        android:exported="true"
        >
    <intent-filter>
        <action android:name="com.android.vending.INSTALL_REFERRER"></action>
    </intent-filter>
</receiver>

Receiverの検証をするためには、com.android.vending.INSTALL_REFERRERイベントを発火させることが可能です。

adb shell am broadcast -a com.android.vending.INSTALL_REFERRER --es referrer debug_test_event

Google AnalyticsとGoogle Playストアの連携をする

Google AnalyticsとGoogle Playストアを連携させると、Google Analyticsの集客タブで、下の画像のようにGoogle Playストア内の行動まで把握できるようになります。 ストア内の離脱率がわかるようになるので、Play Developer Condoleのストア掲載文言のA/Bテスト機能とあわせると、掲載文言の改善が行えそうです。

f:id:kazy1991:20150904153155p:plain (引用元: How Google Analytics helps you make better decisions for your apps | Android Developers Blog)

Google AnalyticsとGoogle Playストアの連携の条件は、

  • 双方のアカウントが同じメールアドレスであること

なので、Google Playストアに登録しているアカウントがGoogleAnalyticsの編集権限を保持した状態で連携の操作をする必要があります。 なお設定は、アナリティクス設定>すべての商品から可能です。

リリース直前チェックリスト

最後にリリース日が近づいてきたら確認したいリストを、まとめました。
項目は少ないですが、よろしければご活用ください。

* [ ] PlayStoreの文言の準備(タイトル[30文字], 簡単な説明[80文字], 詳細な説明[4000文字])
* [ ] PlayStoreのストア用画像の準備(スクショ2枚, 高解像度アイコン[512x512], 宣伝用画像 [1024x500])
* [ ] Bugレポート系ツール(Crashlyticsなど..)の動作確認
* [ ] ログ収集系ツール(Mixpanel, GoogleAnalyticsなど..)の動作確認
* [ ] 通信するAPIが社外ネットワークからアクセス可能か確認
* [ ] デバッグ用のActivityがmainのAndroidManifestに含まれていないか確認
* [ ] release用のkeystoreでビルドされているか確認
* [ ] 利用しているOSSのオープンソースライセンスが明記されているか確認
* [ ] applicationIdの確認

applicationIdについて補足すると、applicationIdはPlayストアのURLにそのまま利用されます。 プロジェクト名をリネームした際に、build.gradle以下のdefaultConfigのapplicationidを変更し忘れると、 想定していたURLと違うURLでGoogle Playストアにリリースされてしまうのでお気をつけ下さい。

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/