目次
この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。
概要
RealmというDBがやたら早いとかでモバイルでは流行りみたいです。
ここ数年はSQLiteからRealmに移行してる人が多いとか。
ということでRealm触ってみました。
サンプルコードの概要
冒頭のgif画像が動いている様子です。
右のボタンを押すと現在時刻をDBに突っ込んで表示します。
左のボタンを押すとDBのレコードを全て削除します。
fab使ってみたかったのでこんな感じにしました。
dataListを直接触らなくてもRealmを使うと簡単にRecyclerViewに反映されます。
以下コード
Activity
class MainActivity : AppCompatActivity() {
lateinit var mRealm: Realm
lateinit var mRecyclerView: RecyclerView
lateinit var mAdapter: RecyclerViewAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.layout_recycler_view)
// Realmのセットアップ
val realmConfig = RealmConfiguration.Builder(baseContext)
.deleteRealmIfMigrationNeeded()
.build()
mRealm = Realm.getInstance(realmConfig)
// Realmを読み込み
val dateList: RealmResults<CurrentTimeModel> = mRealm.where(CurrentTimeModel::class.java).findAll()
// RecyclerViewのセットアップ
mRecyclerView = findViewById(R.id.recycler_view) as RecyclerView
mAdapter = RecyclerViewAdapter(dateList)
val layoutManager = LinearLayoutManager(applicationContext)
mRecyclerView.layoutManager = layoutManager
mRecyclerView.itemAnimator = DefaultItemAnimator()
mRecyclerView.adapter = mAdapter
mRecyclerView.addItemDecoration(DividerItemDecoration(this))
// ボタンのセットアップ
val fabAddCurrentDateTime = findViewById(R.id.fab_add_current_date_time)
fabAddCurrentDateTime.setOnClickListener { addCurrentDateTime() }
val fabDeleteAllRecords = findViewById(R.id.fab_delete_all_records)
fabDeleteAllRecords.setOnClickListener { deleteAllRecords() }
}
override fun onDestroy() {
super.onDestroy()
mRealm.close()
}
fun addCurrentDateTime() {
mRealm.executeTransaction {
val currentDateTime = mRealm.createObject(CurrentTimeModel::class.java)
// LocalDateTime.now()がMIN_APIで使えないので、KotlinMomentを使用
currentDateTime.currentTime = Moment().toString()
mRealm.copyToRealm(currentDateTime)
}
mAdapter.notifyDataSetChanged()
}
fun deleteAllRecords() {
mRealm.executeTransaction {
mRealm.where(CurrentTimeModel::class.java)
.findAll()
.deleteAllFromRealm()
}
mAdapter.notifyDataSetChanged()
}
}
Model
open class CurrentTimeModel(
open var currentTime: String = ""
): RealmObject() {}
エラーが出る
Realmを使っていて、エラーが出て詰まる場面が2つありました。
1. 新しくモデルクラスを追加したときに、class com.list_sample.realmkotlinsample.FooModel is not part of the schema for this Realm.
というエラーが出る
2. 既に使っているモデルクラスをリネームしたり、パッケージ移動したりすると `Error:Execution failed for task ‘:app:transformClassesWithRealmTransformerForDebug’.
javassist.NotFoundException: com.list_sample.realmkotlinsample.Model.` というエラーが出る
エラー対策
JavaとKotlinで書いてみて、言語によって起こるエラーとそれ以外を切り分けます。
1. 新しくモデルクラスを作成した場合のエラー
まずはJavaで簡単なサンプルを動かす
#MainActivity
package com.list_sample.realmjavasample;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import io.realm.Realm;
import io.realm.RealmConfiguration;
import io.realm.RealmResults;
public class MainActivity extends AppCompatActivity {
private Realm mrealm;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Realmのセットアップ
RealmConfiguration realmConfig = new RealmConfiguration.Builder(this)
.deleteRealmIfMigrationNeeded()
.build();
mrealm = Realm.getInstance(realmConfig);
writeRealm();
RealmResults<HogeModel> mrealmResult = mrealm.where(HogeModel.class).findAll();
Log.d("Hoge", "Realm result is " + mrealmResult.toString());
}
private void writeRealm() {
mrealm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
HogeModel hoge = mrealm.createObject(HogeModel.class);
hoge.setHoge("hoge");
}
});
}
}
#HogeModel
public class HogeModel extends RealmObject{
private String hoge;
public String getHoge() {
return hoge;
}
public void setHoge(String hoge) {
this.hoge = hoge;
}
}
当然正常に動きます。
これに新しくモデルクラスとして、FooModelを追加します。
# FooModel
public class FooModel extends RealmObject {
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
private String foo;
}
# MainActivity
public class MainActivity extends AppCompatActivity {
private Realm mrealm;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Realmのセットアップ
RealmConfiguration realmConfig = new RealmConfiguration.Builder(this)
.deleteRealmIfMigrationNeeded()
.build();
mrealm = Realm.getInstance(realmConfig);
writeRealm();
RealmResults<FooModel> mrealmResult = mrealm.where(FooModel.class).findAll();
Log.d("Foo", "Realm result is " + mrealmResult.toString());
}
private void writeRealm() {
mrealm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
FooModel foo = mrealm.createObject(FooModel.class);
foo.setFoo("foo");
}
});
}
}
Javaではエラーが出ません。
これをKotlinで書くと、
# FooModel
open class FooModel(
open var foo: String = ""
): RealmObject(){}
# MainActivity
class MainActivity : AppCompatActivity() {
lateinit var mrealm: Realm
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Realmのセットアップ
val realmConfig = RealmConfiguration.Builder(this)
.deleteRealmIfMigrationNeeded()
.build()
mrealm = Realm.getInstance(realmConfig)
writeRealm()
val mRealmResults: RealmResults<FooModel> = mrealm.where(FooModel::class.java).findAll()
Log.d("Foo", "Realm results is $mRealmResults")
}
fun writeRealm() {
mrealm.executeTransaction {
val foo = mrealm.createObject(FooModel::class.java)
foo.foo = "foo"
}
}
}
落ちます。class com.list_sample.realmkotlinsample.FooModel is not part of the schema for this Realm.
原因と対策
これの原因はRealmのライブラリのバージョンが古かったことです。
今のRealmライブラリの最新バージョンが3.7.1で使っていたライブラリが1.1.0でした。
2. 既に使っているモデルクラスをリネームしたり、パッケージ移動したりすると `Error:Execution failed for task ‘:app:transformClassesWithRealmTransformerForDebug’.
javassist.NotFoundException: com.list_sample.realmkotlinsample.Model` というエラーが出る
ここでもJavaとKotlinを使って切り分けます。
不要になったHogeModelというクラスを削除してみます。
Javaの場合
削除しても正常に動きます。
Kotlinの場合
Error:Execution failed for task ':app:transformClassesWithRealmTransformerForDebug'.
> javassist.NotFoundException: com.list_sample.realmkotlinsample.HogeModel
落ちます。
原因
Realmはモデルクラスを作ってビルドするときに、Proxyクラスを生成します。
HogeModelの場合はHogeModelRealmProxyというファイルが生成されます。
これはJavaの場合もKotlinの場合も同じなんですが、Kotlinの場合RealmProxy クラスとモデルの名前やディレクトリが異なるとエラーが出ます。
対策
Rebuildすると直ります。一番手っ取り早いです。
あとはRealmProxyクラスを削除しても直ります。 -> android studio でcleanするとbuild以下のファイルは全て消えるため、手動で消すのは無駄だと指摘をうけました。
Javaでモデルクラスを作成した場合はそもそもエラーが出ないです。
Rebuildが一番良さそうな解決方法です。
感想
速度が求められるモバイル開発では今後もRealmを使う場面が増えてくると思います。
今回はざっくりした実装とエラー対策ではありましたが、これからも使い方を勉強してうまく使えるようになりたいですね。
あと、今回の記事が間違ってるよ!って方は教えていただけると助かります。
fabだったりRealmだったり、adb使った動画キャプチャのとり方だったり、画像の圧縮方式だったり、個人的には学びが多かったのでよかったです。
あとkotlinはやっぱりいいです。楽だし書きやすい。