LoginSignup
1
5

More than 5 years have passed since last update.

RoomでRxJavaを使うときはバージョンに注意せよ

Last updated at Posted at 2018-11-07

この記事で言いたいこと

RoomとRxJavaを使うとき、AndroidXじゃない方のバージョンを使うと、SELECTだけしかRxで返してもらえないので注意。

Roomの操作はRxJavaで扱える

RoomとRxJava2を連携するライブラリが標準で提供されていて、Googleの公式ドキュメントに導入方法から使い方まで解説されています。
https://developer.android.com/training/data-storage/room/accessing-data#query-rxjava

で、こんな風にDaoの各メソッドの返り値を指定するだけで、勝手にRxと連携してくれます。
(公式ドキュメントよりコピペ)

@Dao
interface MyDao {
    @Query("SELECT * from user where id = :id LIMIT 1")
    fun loadUserById(id: Int): Flowable<User>

    // Emits the number of users added to the database.
    @Insert
    fun insertLargeNumberOfUsers(users: List<User>): Maybe<Int>

    // Makes sure that the operation finishes successfully.
    @Insert
    fun insertLargeNumberOfUsers(varargs users: User): Completable

    /* Emits the number of users removed from the database. Always emits at
       least one user. */
    @Delete
    fun deleteAllUsers(users: List<User>): Single<Int>
}

こんな感じでクエリ結果やInsertの結果を様々な型で扱うことができます。

でもAndroidXはまだalphaとかバージョンについてるし、安定版を使いたいなあ、と思ったとき、Xじゃないプレーンなプロジェクトの方を使うこともできます。
https://developer.android.com/topic/libraries/architecture/adding-components#room
これをみると、バージョン1.1.1がandroid.arch.persistence.roomに用意されているようです。

という感じでPre-AndroidXのバージョンを使ってもりもりコードを書いていったところ、次のようなエラーで怒られました。

Entities and Pojos must have a usable public constructor. You can have an empty constructor or a constructor whose parameters match the fields (by name and type)

謎すぎます。
KotlinならEntityはData Classで定義するので、ここで注意されているようなコンストラクタが無いわきゃないです。
試しに空のコンストラクタをわざわざ用意してみましたが、解決しませんでした。

いろいろ試行錯誤した結果、バージョン1.1.1ではInsert, Delete, Updateの返り値にRxを指定できないことがわかりました。
(実はさっきの公式ドキュメントにもそう書いてあるのですが…)
https://medium.com/androiddevelopers/room-rxjava-acb0cd4f3757
https://issuetracker.google.com/issues/63317956
バージョン1.1.1で InsertDeleteCompletableSingle は使えません。
それらを指定したとき、さっきのようなエラーが出たりするようです。(たぶん)

1.1.1で使うには

なので1.1.1ではInsertやDeleteの返り値はLongやUnitを指定することになります。
でも、できればRxで統一的に扱いたいわけです。
AndroidXを素直に使えというのはそのとおりですが、まあそうしないでもやっていくことはできます。
たとえば、DatabaseクラスにRxオブジェクトを返すメソッドを用意するなどが便利そうです。

@Database(entities = [Img::class], version = 1)
abstract class ImogeDatabase : RoomDatabase() {
    abstract fun imgDao(): ImgDao

    // たとえばDaoにdelete()が用意されているとして
    fun deleteAsCompletable(imoge: Img) = Completable.fromCallable {
        imgDao().delete(imoge)
    }
}

こういうメソッドを用意しておいて、Daoのdelete()は使わずに、このdeleteAsCompletable()を使うようにするとか。

でも本当はDatabaseクラスじゃなくて、Daoインターフェース自体にデフォルト実装をもつメソッドを用意したいんですよね…
SelectはDaoから呼ぶけど、InsertやDeleteはDatabaseから呼ぶ、とか使いづらいの極みでしかない。
けど、それはうまくいきません。

Roomが自動生成するコードがKotlinじゃなくてJavaっぽくて、Interfaceのデフォルト実装をうまく扱ってくれないんですよね…(参考)
誰かいい解決法を授けてください。
おたのみもうします。

1
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
5