Realmを使ってみたくて、CRUDを実装したシンプルなメモ帳アプリを作ってみた。
学んだことやハマったことをメモしておく。
https://realm.io/
作ったもの
ソースコードはこちらから↓
https://github.com/orimomo/realm-sample-app
- Create : fabボタン押下→ダイアログ入力で新規作成
- Read : 起動時・データ変更時に読み込み
- Update : Itemの長押し→ダイアログ入力で更新
- Delete : Itemの削除ボタン押下で削除
Realmの導入
Gradleの設定
以下を追加。
dependencies {
classpath "io.realm:realm-gradle-plugin:6.0.2"
}
apply plugin: 'kotlin-kapt' // なければこれも追加
apply plugin: 'realm-android'
Realmの初期化
カスタムアプリケーションクラスの中で以下を追加。
override fun onCreate() {
super.onCreate()
Realm.init(this)
val config = RealmConfiguration.Builder()
// .deleteRealmIfMigrationNeeded()
.build()
Realm.setDefaultConfiguration(config)
}
これでRealmのデフォルト設定が完了し、default.realm
というファイルにデータが保存されるようになる。
.deleteRealmIfMigrationNeeded()
はモデルを変更した後など、必要に応じてDBを削除・再構築してくれるものなのだが(変更後にこのコードがないとアプリがクラッシュする)、
入っていると後述するstetho-realm
が使えなかったので、一旦コメントアウトしておき、必要なときに コメントを外して有効にするようにする。
Modelの作成
こんな感じ。
open class ListObject: RealmObject() {
@PrimaryKey
var id : Int? = null
@Required
var title = ""
}
RealmObject()
を継承する点と、プライマリーキーが必要な点に注意。
@PrimaryKey
は主キーで、 @Required
はnullを許容しない必須項目のアノテーション。
Fragmentから呼び出す
Fragment内でメンバ変数を作ってインスタンス化しておく。
また、Realmオブジェクトの削除処理も入れておく。
class ListFragment : Fragment() {
// メンバ変数として持っておく
private lateinit var realm: Realm
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// インスタンス化しておく
realm = Realm.getDefaultInstance()
return binding?.root
}
override fun onDestroy() {
super.onDestroy()
// Realmオブジェクトの削除
realm.close()
}
}
RealmでCRUD
Create
入力画面で「保存」ボタンを押したら、Realmにデータを書き込むようにする。
private fun create(title: String) {
// プライマリーキーを取得
val savedId = sharedPreferences.getInt(ViewModel.KEY.REALM_ID.name, id)
val id = savedId + 1
// トランザクションして書き込む
realm.executeTransaction { realm ->
val obj = realm.createObject(ListObject::class.java, id)
obj.title = title
}
// プライマリーキーを保存
sharedPreferences.edit().putInt(ViewModel.KEY.REALM_ID.name, id).apply()
}
プライマリーキーのインクリメントはとりあえず一番簡単な方法ですることとし、sharedPreferencesに前回書き込んだ番号を保存しておき、今回書き込むときに一つ番号を上げるようにした。
Read
起動時と、データ変更時に、Realmからデータを読み込んで画面に表示するようにする。
private fun read() {
// 全件取ってきて、降順にソートする
val all = realm.where(ListObject::class.java).findAll()
val sortedAll = all.sort("id", Sort.DESCENDING)
// RecyclerViewに表示する
sortedAll.forEach { obj ->
groupAdapter.add(ListItem(obj, viewModel))
}
}
データの変更は発生しないので、トランザクションは不要。
Update
RecyclerViewのItemの長押し→入力画面で「保存」ボタンを押したら、データが更新されるようにする。
private fun updateRealm(id: Int, newTitle: String) {
// プライマリーキーをもとに該当のデータを取得
val target = realm.where(ListObject::class.java)
.equalTo("id",id)
.findFirst()
// トランザクションして更新をかける
realm.executeTransaction {
target?.title = newTitle
}
}
Delete
RecyclerViewのItemにある「削除」ボタンを押したら、データが削除されるようにする。
private fun deleteRealm(id: Int) {
// プライマリーキーをもとに該当のデータを取得
val target = realm.where(ListObject::class.java)
.equalTo("id",id)
.findAll()
// トランザクションして削除
realm.executeTransaction {
target.deleteFromRealm(0)
}
}
stetho-realmを使ったデータの確認
ちゃんとデータが保存されたのかを確認するために、stetho-realm を使ってみた。
stetho-realm についての概要は下の記事にまとめている。
Realmのデータを確認するツール・ライブラリまとめ - Qiita
stetho-realmの導入
下記が本家なのだが、READMEに書いてある通りに設定してもうまく行かなかった。
https://github.com/uPhyca/stetho-realm
理由はRealm3.7.1以上のバージョンに対応していないため。
そこでコミュニティが対応している下記リポジトリを使う必要があった。
https://github.com/wickedev/stetho-realm
まずはREADMEの通り、下記をGradleに追加する。
repositories {
maven {
url 'https://github.com/uPhyca/stetho-realm/raw/master/maven-repo'
}
}
dependencies {
compile 'com.facebook.stetho:stetho:1.5.0'
compile 'com.uphyca:stetho_realm:2.1.0'
}
カスタムアプリケーションのRealmの初期化の下に、stetho-realmを初期化するコードを追加。
override fun onCreate() {
super.onCreate()
// Realmの初期化(省略)
// stetho-realmの初期化
Stetho.initialize(
Stetho.newInitializerBuilder(this)
.enableDumpapp(Stetho.defaultDumperPluginsProvider(this))
.enableWebKitInspector(RealmInspectorModulesProvider.builder(this).build())
.build())
}
}
Chrome上でデータを確認する
アプリを起動して(実機でもエミュレータでも可)、chrome://inspect/#devices
をChromeで検索する。
Developer Toolsが開くのでinspect
をクリック。
別ウィンドウが立ち上がって、Resources > Web SQL > defauld.realm からデータが確認できる。
おわりに
Realmの操作は簡単で、特にハマったりもせず、サクサクと作ることができた。良い。
今回はRealmでCRUDをしてみることを目的にしていたので、それ以外の部分を雑に書いてしまっているのが反省点(ViewModelに持っていくべきコードを持っていってなかったり、ボタンを非活性にすべきところをしてなかったりする)。
RealmはSwiftでも同じようなコードで使えるとのことなので、今度はiOSでも使ってみたいと思う。