「RoomでDBの値更新時に、更新通知がこない!」こんなことがもし起こったら、DI周りをしっかり見直しましょう、という話になります。
TL;DR
RoomDatabaseやDaoクラスがアプリ内の複数箇所で扱われている場合に、同一インスタンスが利用されているかを確認しましょう。
(特にDatabaseBuilderで作成するDatabaseをDIしている際などには、それがアプリ内でSingletonで扱われるようになっているかどうかを確認しましょう。)
たとえば、以下のような同一クラスのDaoなのだけど、以下の処理をそれぞれ別インスタンスで行ってしまうと、更新通知が即座に発火されなかったりします。
- (Insert / Updateなど)更新処理を行う
- LiveDataなどを返す関数を持つような、変更通知を行う
DIしている場合
特に、一つのDaoを複数箇所で利用する場合 and/or DIする場合などには気をつけましょう。
RoomDatabaseをセットアップしアプリをビルドすると、XXXDatabase_Implクラスのコードが生成されます。
このImplクラスは、daoを作成する関数を持ちます。たとえばLocalArticleDaoというDaoを扱うとして、コードをビルドすると以下のような関数が生成されます。
@Override
public LocalArticleDao articleDao() {
if (_localArticleDao != null) {
return _localArticleDao;
} else {
synchronized(this) {
if(_localArticleDao == null) {
_localArticleDao = new LocalArticleDao_Impl(this);
}
return _localArticleDao;
}
}
}
このように、XXXDatabase_Implクラスのコードを読むと、一度生成されたDaoはフィールドに保持され、以後、生成されたDaoを返すような実装になっていることがわかります。
なので、Databaseクラスがアプリ内でSingletonであれば、基本的には問題ないと考えます。
DI(ここではDagger)の話になりますが、以下のようにAppDatabaseをSingletonではない形でProvideしていると、
Injectされる箇所ごとに、新しいAppDatabaseが初期化され渡されます。DaoはAppDatabaseに紐づくに形で作成されるため、新しいAppDatabaseが都度渡されるということは、
複数箇所でInjectされているDaoも、すべて別インスタンスとなります。
@Provides
fun provideAppDatabase(application: Context): AppDatabase {
return Room.databaseBuilder(
application.applicationContext,
AppDatabase::class.java,
"app-db"
)
.build()
}
@Provides
fun provideArticleDao(db: AppDatabase): LocalArticleDao = db.articleDao()
まとめ
なんか当たり前のことしか書いてない気がしますが、生成されたコードだったりを深く読み解くのも大事ですが、基本的なところもしっかり確認していきましょう。。ということで。
自戒しかないメモでした。