RoomのDatabase Instanceの初期化についてのメモ

概要

Roomのcodelabで疑問に思う箇所があったのでメモ

codelabで紹介されているコード

codelabではRoomDatabaseのインスタンス生成はsingletonパターンで実装され、以下のようなコードになっている。

@Database(entities = [Word::class], version = 1)
public abstract class WordRoomDatabase : RoomDatabase() {

   abstract fun wordDao(): WordDao

   companion object {
        @Volatile
        private var INSTANCE: WordRoomDatabase? = null

        fun getDatabase(context: Context): WordRoomDatabase {
            val tempInstance = INSTANCE
            if (tempInstance != null) return tempInstance
            synchronized(this) {
                val instance = Room.databaseBuilder(
                        context.applicationContext,
                        WordRoomDatabase::class.java, "Word_database").build()
                INSTANCE = instance
                return instance
            }
        }
   }
}

@VolatileアノテーションKotlinのドキュメントでは次のように解説されている。

Marks the JVM backing field of the annotated property as volatile, meaning that writes to this field are immediately made visible to other threads.

したがって、codelabのコードでは変数INSTANCEにVolatileアノテーションが付けることで複数のスレッドからアクセスをスレッドセーフにしている。

疑問

synchronizedでlockを取得する前に他のスレッドがlockを解放した場合、codelabのコードでは既にインスタンスが生成済みでもRoom.databaseBuilderが呼ばれてしまうでは?

どうするか?

以下の記事の対策3に今回のコードは近くて、synchronizedブロック内で変数instanceが初期化されているかを確認している。

シングルトンパターンの遅延初期化をスレッドセーフにするには - じゅんいち☆かとうの技術日誌

試しにPlaidのコードを見ると、synchronizedブロックの中でinstanceを再度チェックしていた。

// For Singleton instantiation
@Volatile private var instance: DesignerNewsDatabase? = null

fun getInstance(context: Context): DesignerNewsDatabase {
    return instance ?: synchronized(this) {
        instance ?: buildDatabase(context).also { instance = it }
    }
}

まとめ

synchronizedブロックの中でinstanceが初期化済みかを再度チェックした方がよさそう。

Gradle Play PublisherのAndroid App Bundle Supportがもうすぐリリースされそう

Gradle Play Publisher?

Gradle Play PublisherはAPKのGoogle Play Developerコンソールへのアップロードを自動化できるGradleプラグインです。

github.com

APKのアップロード以外にもPlay Storeに掲載する各種メタデータの管理も一括で行うことができます。 アプリに関する各言語向けデスクリプションやストア画像などをバージョン管理システムで管理できるようになりますし、とても便利です。 Gradleプラグイン化されていることで、CircleCIやBitriseのようなCIサービスから実行するのも簡単なのも嬉しいポイントですね。

Android App Bundle

Android App BundleGoogle I/O 2018で発表された新しいAndroidアプリのアップロードフォーマット(aabファイル)で、アプリのコンパイル済みのソースコードとリソースを全て含みます。APKと異なるのは、APKの生成と署名がGoogle Play側で行われる点です。その仕組上Google Play App Signingの有効化が必須となります。

Android App Bundleに対応することで、ユーザーがアプリをインストールする端末のコンフィグレーションに最適化されたAPKが生成されるようになり、不要なリソースやコードを含まないことでダウンロードするAPKサイズが小さくなります1。この新しいアプリの配布の仕組みをDynamic Deliveryと呼びます。

また、Dynamic feature modulesというモジュールをプロジェクトに追加することでアプリの機能とリソースを分離し、必要になった時に動的にダウンロードすることが可能になります。初回のダウンロードには使用頻度の高い機能のみを含めることでAPKのサイズをさらに削減できます。(2018/10現在でβ)

Android App Bundleの概要は次の動画がわかりやすくオススメです。

Gradle Play PublisherのAndroid App Bundle Support

aabファイルのアップロードにGradle Play Publisherが対応していませんでしたが2、バージョン2.0.0でついにサポートされます。

gradle-play-publisher/CHANGELOG.md at master · Triple-T/gradle-play-publisher · GitHub

READMEはすでに2.0.0のものに更新されています。./gradlew publishBundleでaabファイルのアップロードを実行できるようですね。 CHANGELOGではto be releasedとなっているので近日中にリリースされそうです。

参考


  1. 最適化されたAPKが生成されるのは5.0以上で4.4以下ではMultiple APKとしてもっとも適切なAPKが配信されます。この場合全ての言語別のリソースが含まれます。

  2. Fastlane supplyは先に対応していました。

kotlin-puzzlersのnaughtyWhen問題

Twitterで#KotlinConf18jpのハッシュタグを眺めてたら次のような問題が流れてきた。

package syntax.naughtyWhen
// by Ilya Gorbunov

fun f(x: Boolean) {
   when(x) {
       x == true -> println("$x TRUE")
       x == false -> println("$x FALSE")
   }
}

f(true)
f(false)

// What will it print?
// a) true TRUE; false FALSE
// b) true TRUE; false TRUE
// c) true FALSE; false FALSE
// d) none of the above

この問題の正解はbになる。Kotlin Playgroundで試してみた結果はこちら。

何故その結果になるのか疑問に思って調べるとGiHubで問題と解説が公開されていた。

kotlin-puzzlers/src/syntax/naughtyWhen at master · angryziber/kotlin-puzzlers · GitHub

when式でfalseを渡すと実際には次のように評価されるのが理由で正解がbになる。

when(x) {
    x == true -> println("$x TRUE") // 実際にはfalse == x == trueとなる。xにはfalseが入るためtrueと判定される。
}

なるほど。そして解説には

when(x)when { x == ... } はどちらか一方を使うこと。両方を同時に使わない。

とあった。

なるほど。

参考

DiffUtilのgetChangePayloadで返したオブジェクトをどう使うか

DiffUtilのgetChangePayloadでは、古いアイテムと新しいアイテムでどのフィールドが更新されたかの情報を詰めたオブジェクトを生成して返す。

どんな感じなのかは以下のサイトを参照。

このreturnするPayloadがどこで使われるかちゃんとわかって無かったのだけど、onBindViewHolder(ViewHolder holder, int position, List<Object> payloads)payloadsで参照できる。

RecyclerView.Adapter  |  Android Developers

areItemsTheSame(int, int)trueを返し、areContentsTheSame(int, int)falseを返すなら、新旧のitemエンティティ(一意に識別できるオブジェクト)自体は同じだが、そのオブジェクトの持つデータが異なる。

どのデータが新しいデータで更新されたかをgetChangePayloadでオブジェクトにして返してやり、onBindViewHolder(ViewHolder holder, int position, List<Object> payloads)でpayloadsから更新内容を取得してViewを部分的に更新してやればゼロからViewを構築するよりコストが下がる。

この時アニメーションを掛けて更新すると良い感じになる。なるほど。

LiveDataのユニットテストを書く際に参考になる記事

LiveDataのユニットテストを書く場合にどうするか調べたのでメモ。

LiveDataのUnitTest – Kenji Abe – Medium

How To Unit Test LiveData and Lifecycle Components – ProAndroidDev

  • KotlinでLiveDataとlifecycleのユニットテストについて解説している記事。lambdaMockという関数を用意しているのがポイント。
  • Lifecycleのユニットテストについても言及している。
  • ProAndroidDevは参考になる記事が多いですね。

LiveDataの基礎的な性質を整理する。 - Qiita

  • LiveDataの性質についてまとめられた記事です。LiveDataがどのような挙動をするかがテストコードを交えてわかりやすく解説されています。
  • LiveDataの性質についてだけで無く、テストコードの書き方についても参考になりました。

Mockito

Mockitoの使い方については、こちらのブログがとても参考になります。ありがたい🙏

Mockito カテゴリーの記事一覧 - R42日記

It’s time to upgrade GCM to Firebase Cloud Messagingの意訳

FirebaseからGCM終了のお知らせとFCMへのアップグレード告知メールが来たので意訳した。ついにGCMが終了するのかー

意訳文

2016年、Google Cloud Messaging(GCM)の後継としてFirebase Cloud Messaging(FCM)をローンチしました。FCMはGCMを進化させ、通知とデータメッセージをiOSAndroidの両方に確実に送信することを可能にしています。加えて、FCMはFirebase console上でプッシュ通知を簡単に送信できる機能を提供しています。それによって、ユーザーの再獲得のためのプッシュ通知を簡単に送信できます。

FCMの改善のために注意を払い多くの時間を掛けた事で、今日私達は開発者の皆さんに来年FCMへアップグレードする必要がある事を告知する事ができました。GCMサーバとクライントAPIは非推奨になり、2019年4月11日にすみやかに削除されます。私達は、出来る限り速くFCMにアップグレードし、FCMが提供する便利な機能のアドバンテージを得る事を推奨します。

もしあなたのプロジェクトでまだGCM APIを使用しているなら、2019年4月11日までにクライアントとサーバーのコードでFCMを使用するよう更新する必要があります。今使用しているGCMトークンはFCMで引き続き動作するため既存のユーザーへプッシュ通知が送信不能にはなりません。

何をする必要があるのか?

こちらからFCMへのアップグレードプロセスを確認できます。また、こちらの動画でもマイグレーションプロセスを確認できます。

もしドキュメントで回答が得られない質問があれば、サポートページのCloud Messaging FAQsをご覧頂くかその他のサポートチャンネルを通じてご連絡ください。

あなたをFCMへ迎える事を楽しみにしています!

Firebaseチームより。

原文

In 2016, we launched Firebase Cloud Messaging (FCM), the successor to Google Cloud Messaging (GCM). As the next evolution of GCM, Firebase Cloud Messaging allows you to send notifications and data messages reliably to iOS, Android, and the Web at no cost. In addition, FCM provides you with new features like the easy-to-use notifications interface in the Firebase console, so you can easily target and test notifications to re-engage your users.

In order to devote more time and attention to improving FCM, today we’re announcing that you must upgrade to FCM in the next year. The GCM server and client APIs are deprecated and will be removed as soon as April 11, 2019. We recommend you upgrade sooner rather than later so you can start taking advantage of the new features in FCM today.

If you have projects that are still using the GCM APIs, you will need to update your client and server code to use FCM before April 11, 2019. Your existing GCM tokens will continue to work with FCM so you won’t lose the ability to send to your existing users.

What do I need to do?

You’ll find a complete walkthrough of the FCM upgrade process here. You can also check out this video, which walks through the migration process.

If you have questions that aren’t answered in the deprecation documentation, take a look at the Cloud Messaging FAQs on our support page or reach out to us through one of our other support channels.

We look forward to welcoming you to FCM!

Thanks,

Firebase Team