Androidのローカルデータベースの選択肢の整理
この記事はコネヒトアドベントカレンダー2022の13日目の記事です。
Androidでローカルデータベースの選択肢を整理してみた。
最初に結論
- Realmは更新の速度は優秀だが、アプリサイズや参照メソッド数の考慮が必要
- Androidだけの環境ならRoomを選択する方が無難に思う
- iOSとAndroidのマルチプラットフォームの構想があるならRealmもあり
※他にもいい選択肢があれば教えてください!
デベロッパーガイドはRoomの利用を強く推奨している
Androidでローカルデータベースを選択する場合、Room or Realmが選択肢に上がることが多いと思う。
もちろんSQLiteを生で実装することも、可能ではあるが学習コストの高さを軽減したり、安定的な開発をするために、デベロッパーガイドではRoomを利用することが推奨されている。
数年前では、Realmを利用することが多かったイメージだが、Roomが実装されたことにより、トレンドが大きく動いたと感じている。この2つについて改めて整理してみた。
RealmとRoomの比較は下の記事を参考にさせてもらった。
参照メソッド数とアプリサイズ
RoomとRealmの参照されるメソッドは、Roomがはるかに少なかった。Realmの場合はMultiDexやアプリサイズへの影響を考慮する必要がありそうだった。
Room
Realm
アプリサイズ
加えて、Realmはネイティブライブラリが含まれるため、アプリサイズへの影響もありそうだった
複数スレッドでの操作
RoomはRoomDatabase.Builder.setTransactionExecutor(Executor)
を指定することで、複数スレッドからのトランザクションを抑制できるとのことで、それ以降のバージョンを使うほうが余計な考慮はせず済みそうだった。
対してRealmはKotlinのSDKのみスレッドセーフな実装になっている様子。
速度面
触ってみるついでに、1万件のクラスを作りそれぞれ全件取得するコードを書いて速度を比較してみた。
Room
@Entity data class User( @PrimaryKey val uid: Int, @ColumnInfo(name = "first_name") val firstName: String?, @ColumnInfo(name = "last_name") val lastName: String? ) @Dao interface UserDao { @Query("SELECT * FROM user") fun getAll(): List<User> } val db = Room.databaseBuilder( applicationContext, AppDatabase::class.java, "database-name" ).build() val userDao = db.userDao() userDao.getAll()
Realm
class Person : RealmObject { var name: String = "Foo" } val configuration = RealmConfiguration.create(schema = setOf(Person::class)) val realm = Realm.open(configuration) realm.query<Person>().find().forEach { // nop }
結果
このような使い方をすることはないので、あくまでも参考情報だが、10万件の全件取得だとRoomの方が速度が速かった。RoomはgetAllで全件を一度に取得する設計に対して、RealmはfindではRealmResultで参照を持ち、アクセス時に実体を取得する設計のようだった。
試しに1件だけ更新したところ以下のようにRomの方が高速で処理がなされていた。
じゃあ、どっちを使う?
性能面の比較をしてみたが、Realmではもう一つ重要な観点があった。
RealmはKMMでのSDKが提供されているため、iOSとAndroidでのマルチプラットフォームを狙う場合は、選択肢としては強い動機になりうると思った。
結論
- Realmは更新の速度は優秀だが、アプリサイズやMultiDexの考慮が必要
- Androidだけの環境ならRoomを選択する方が無難に思う
- iOSとAndroidのマルチプラットフォームの構想があるならRealmもあり
- Android開発楽しい!