build.gradleの設定でパッケージ名のみ違うdebug.apkを作る上での注意点メモ

はじめに

のお話なのですが、

  • app/build.gradle
    buildTypes {
        debug {
            debuggable true
            applicationIdSuffix ".debug"
            versionNameSuffix "-DEBUG"
        }
    }

このまま適応してしまうと、実行時にクラッシュエラーになる場合があるので、

どこを修正しないと駄目なのか辺りをちょっと整理してみました。*1

書き換えると、違うパッケージになるので楽だぜー な話しか見ないんですよね・・(汗

動作環境


applicationIdSuffix を適応したときはどういう状態なのか?

  • AndroidManifest.xml のみ書き換わる
  • class.jar 等はそのまま

書き換えられる箇所(ベース)

  • 変更前
<manifest 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="hoge.fuga.maiu"> <!--★-->
  • 変更後
<manifest 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="hoge.fuga.maiu.debug"> <!--★★-->

★ => ★★

物理package名 が補われるもの

android:name の場所は基本置き換えてくれるみたい

  • 変更前
<activity
    android:name=".activity.MainActivity"
    android:screenOrientation = "portrait">
</activity>


<service
     android:name=".service.MainService"
     android:exported="false">
</service>
  • 変更後
<activity
    android:name="hoge.fuga.maiu.activity.MainActivity"
    android:screenOrientation = "portrait">
</activity>


<service
     android:name="hoge.fuga.maiu.service.MainService"
     android:exported="false">
</service>

物理package名 が補われないもの

  • meta-data
<-- meta-dataはフルパスにしておかないと駄目 (class.jarは変更されないため) -->
    <meta-data
        android:name="hoge.fuga.maiu.application.LimitCacheSizeGlideModule"
        android:value="GlideModule" />

 桜さんのブログの記載箇所

GCMの箇所

ただGCMは現在新規登録できないので、古いアプリメンテ用の記載になるかも

  • app/AndroidManifest.xml
<uses-permission android:name="android.permission.WAKE_LOCK" />
<permission
    android:name="${applicationId}.permission.C2D_MESSAGE"
    android:protectionLevel="signature" />
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />

<receiver
    android:name="com.google.android.gms.gcm.GcmReceiver"
    android:exported="true"
    android:permission="com.google.android.c2dm.permission.SEND" >
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <category android:name="${applicationId}" />
    </intent-filter>
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
        <category android:name="${applicationId}" />
    </intent-filter>
</receiver>

FCMだと

上記あたりの記載は一切いらなくなる。移行は下記のページ参照

自動生成系

下記は、ManifestMarger時にマージされて付与される

<receiver android:exported="true" 
    android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" 
    android:permission="com.google.android.c2dm.permission.SEND">
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE"/>
        <category android:name="${applicationId}"/>
    </intent-filter>
</receiver>

<provider 
    android:authorities="${applicationId}.firebaseinitprovider" 
    android:exported="false" 
    android:initOrder="100" 
    android:name="com.google.firebase.provider.FirebaseInitProvider"/>

packageごとに、独自登録が必要なもの

google-play-service.json

MainP -- src/main
      -- google-services.json          hoge.fuga.maiu 用
       |        
       -- src/debug
              |        
              -- google-services.json  hoge.fuga.maiu.debug 用

登録時のFinglerPrint

keytool -exportcert -list -v -alias androiddebugkey -keystore ./app/irof_history_debug.keystore

な感じで算出

facebook provider

MainP -- src/main/res
      -- config.xml          hoge.fuga.maiu 用 [FacebookアプリID]
       |        
       -- src/debug/res
              |        
              -- config.xml  hoge.fuga.maiu.debug 用 [FacebookアプリID]
  • app/AndroidManifest.xml
<provider android:authorities="com.facebook.app.FacebookContentProvider[FacebookアプリID]"
          android:name="com.facebook.FacebookContentProvider"
          android:exported="true" /> 

登録時のキーハッシュ

keytool -exportcert -alias androiddebugkey -keystore ./app/irof_history_debug.keystore | openssl sha1 -binary | openssl base64

な感じで作成

自動生成系

下記は、ManifestMarger時にマージされて付与される

<provider 
    android:authorities="${applicationId}.FacebookInitProvider" 
    android:exported="false" 
    android:name="com.facebook.internal.FacebookInitProvider"/>

TwitterKit(febric)

これは AndroidManifest.xmlべた書きしか駄目*2

  • app/src/main/AndroidManifest.xml
<meta-data
            android:name="io.fabric.ApiKey"
            android:value="YOUR_API_KEY" />
  • app/src/debug/AndroidManifest.xml
<meta-data
            tools:replace="android:value"
            android:name="io.fabric.ApiKey"
            android:value="YOUR_DEBUG_API_KEY" />

applicationとかの差し替えの場合は、tools:replace="android:name" となる

いつものお話ですね。。


独自ContentProvider の話

  • app/AndroidManifest.xml
<provider
    android:name=".provider.DatabaseProvider"
    android:authorities="${applicationId}.DatabaseProvider" <!--◎-->
    android:exported="false" />

◎が端末ユニークである必要があるため、別々にしないと駄目

  • libraryProject側に記載しても、MainP側で
    • ManifestMargerで全 AndroidManifest.xmlがマージ
    • ${applicationId}を置き換え
    • placeHolderが適応

の流れなので、library-projectのパッケージが違う場合は期待通り動かない

  • app/DatabaseContract.java
public class DatabaseContract {
    /** The authority for the database provider */
    public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".DatabaseProvider";
    // ...
}

かプログラムで動的に取るか・・・*3

searchable

下記みたいな書き方が可能

<searchable
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:searchSuggestAuthority="${applicationId}.hoge.fuga" //★
    android:searchSuggestSelection=" ? "
    android:searchSuggestIntentAction="android.intent.action.SEARCH"
    android:label="@string/search_label"
    android:hint="@string/search_hint"
    android:searchMode="showSearchLabelAsBadge"
    android:imeOptions="actionSearch"
    android:inputType="textPersonName"/>

を参照

FileProvider

Uriを作成する仕組み(共有機能)

  • files-path
    • Context.getFilesDir()
  • cache-path
    • Context.getCacheDir()
  • external-path
    • Environment.getExternalStorageDirectory()
    • SD権限は別途必要

  • AndroidManifest.xml
<provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="${applicationId}.fileprovider" //★
        android:grantUriPermissions="true"
        android:exported="false">
    <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/filepaths" />
</provider>


類似の話

<sync-adapter 
    android:accountType="${applicationId}"
    android:allowParallelSyncs="false"
    android:contentAuthority="${applicationId}.DatabaseProvider"
    android:isAlwaysSyncable="true"
    android:supportsUploading="true"
    android:userVisible="true" />

<account-authenticator 
    android:accountType="${applicationId}"
    android:icon="@drawable/ic_launcher"
    android:label="@string/account_authenticator_label"
    android:smallIcon="@drawable/ic_launcher" />

adobe creative sdk(2018/03/01追記)

qiita.com

な話で話題になってる奴ですが、このFWの場合はaar内のAndroidManifest.xml

{appPackageName} というmanifestPlaceholdersを定義しましょう

という形式ですね。

stackoverflow.com

で、Android 3.0 DSLベースで 試しててハマったのが

  • app/build.gralde (NG)
android {
    defaultConfig {
            manifestPlaceholders = [appPackageName: "${applicationId}"]
            manifestPlaceholders = [appName: "@string/app_name"]  //★この状態だとmanifestPlaceholdersのMap自体を再初期化する
    }
}
  • app/build.gralde (OK)
apply plugin: 'com.github.gfx.ribbonizer' 

android {
    defaultConfig {
            manifestPlaceholders = ['appPackageName': "${applicationId}"]
            manifestPlaceholders += ['appName': "@string/app_name"] // += である必要があり
    }

    flavorDimensions "debug" //◎
    buildTypes{
          release{

          }
          debug{

          }
          debug_hoge{
                initWith(debug)
                matchingFallbacks["debug"] //◎
                defaultConfig.manifestPlaceholders ['appPackageName'] ="${applicationId}.debug"
                defaultConfig.manifestPlaceholders ['appName']= "あいうえお" //固定文字列でもOK。ただ結合は無理
          }
    }
}

ribbonizer {
    builder { variant, iconFile ->
        ant.echo "variant="+variant.buildType.name
        if (variant.buildType.name.indexOf("debug")!=-1) {
            if (variant.buildType.name  == "debug_hoge") {
                    // TODO: debug_hogeのときだけリボンをつける
                    return greenRibbonFilter(variant, iconFile)
            }
            else{
                   return null //no effect ribonnizer
            }
        }
    }
}
  • ◎・・library projectを使っている場合指定しないとビルドが中止される
  • キーの箇所をシングルで囲まないと代入上書きできない*4
  • buildTypesの状態だとdefaultConfigの値はまだ初期化されてないのでベタ値を入れるしかない*5
  • ribonnizer は return nullだとリボンつけない

  • AS2.3自体は探してくれてたんですけどね・・。
    • 高速化のために冗長な検索処理は削除されましたorz
  • AS3.1ベースだと、compleの記述が残ってると高頻度でGradleSync失敗。
    • これもチェックやめたんだろうなー*6

呼び出し側で気をつけること(2017/7/18追記)

  • 従来通りで気にしないでいい書き方
Intent intent = new Intent(this,MainActivity.class);
startActivity(intent);
  • この書き方の場合は注意
Intent intent = new Intent();
intent.setClassName(getPackageName(), "hoge.fuga.maiu.MainActivity"); //★
startActivity(intent);

//動かない書き方(1)
intent.setClassName(getPackageName(), getPackageName() + ".MainActivity");

//動かない書き方(2)
intent.setClassName("hoge.fuga.maiu","hoge.fuga.maiu.MainActivity");
  • 理由は
    • hoge.fuga.maiu.debug というパッケージクラスローダー上の
    • hoge.fuga.maiu.MainActivity のクラスを探す*7

という形になるから


で問題となることは・・・

正にこの現象。

  • debugSuffix とかつけたり
  • productFlavorつくったり

すると Rの生成がたまにおかしくなって Fragmentのレイアウト参照箇所のソースとかが定期的に真っ赤になる

cleanすれば治るんだけど、ビルドが不安定orz

だた、

とかあると、debugサフィックス を除去して、上記のファイルを除くとか正直面倒くさい><


併用すると便利なライブラリ

gradle-android-ribbonizer-plugin

debug=true

のbuildTypeのアイコンに、buildType名でリボンを付けてくれるgradle plugin*8

ribbonizerが使いづらい*9という場合は、manifestPlaceholders で頑張るって話は確かにあるなー。下記参照

muumuutech.hatenablog.com

Android-Iconics

下記に記載情報を移動、整理

*1:なんか検索すると sakura_bird1 さんと stackoverFlowぐらいしか情報が無いでござる!

*2:meta-data のため・・

*3:でも個体値でfinal宣言しているのが多いんですよね・・。直上のjavaソースと同じく

*4:ネットサンプルだとないから内部でtoStringとかしている処理動いてるのか?

*5:これがわかるまですごく苦戦した

*6:plugin側だと思うけど。preCheck辺りで引っかかってる所感

*7:class自体はそのままのパッケージ階層で保存されているため

*8:ただカスタムは難しいんだよな><

*9:閉鎖環境とかで