Realmメモ
全くのRealm素人ですが、
Realmハンズオン (Android) #2に参加したので、覚えたことを書いていきます。
http://realm.connpass.com/event/28919/
このドキュメントがめちゃくちゃ詳しいです。
https://realm.io/jp/docs/java/latest/
サンプルとステップはこちらにあるようです。
https://github.com/zaki50/Realm-Hands-On2_android
準備
/build.gradle
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0-alpha4'
classpath 'io.realm:realm-gradle-plugin:0.88.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
app/build.gradle
apply plugin: 'com.android.application'
// androidの後である必要がある
apply plugin: 'realm-android'
Applicationクラスで利用準備する
初期化
カスタムのApplication#onCreate内
@Override
public void onCreate() {
Realm.setDefaultConfiguration(new RealmConfiguration.Builder(this).build());
}
マイグレーション(フィールドの追加などのバージョンアップ)
DynamicRealmという特殊なオブジェクトを利用して、フィールドを追加する
ここに詳しいものがあるみたい
https://realm.io/docs/java/latest/#migrations
@Override
public void onCreate() {
super.onCreate();
Realm.setDefaultConfiguration(buildRealmConfiguration());
}
private RealmConfiguration buildRealmConfiguration() {
return new RealmConfiguration.Builder(this)
.schemaVersion(1L)
.migration(new RealmMigration() {
@Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
if (oldVersion == 0L) {
final RealmObjectSchema tweetSchema = realm.getSchema().get("Tweet");
tweetSchema.addField("favorited", boolean.class);
//noinspection UnusedAssignment
oldVersion++;
}
}
})
.build();
}
エンティティの用意
RealmObjectを継承する。
プライマリーキーがある場合は@PrimaryKeyをつける。
StatusはTwitter4jのオブジェクトなので、各自入れたいものをコンストラクタで渡せば良い。
package io.realm.handson2.twitter.entity;
import java.util.Date;
import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;
import twitter4j.Status;
public class Tweet extends RealmObject {
@PrimaryKey
private long id;
private Date createdAt;
private String screenName;
private String text;
private String iconUrl;
private boolean favorited;
public Tweet() {
}
public Tweet(Status status) {
//https://dev.twitter.com/overview/api/twitter-ids-json-and-snowflake
setId(status.getId());
setCreatedAt(status.getCreatedAt());
setScreenName(status.getUser().getScreenName());
setText(status.getText());
setIconUrl(status.getUser().getProfileImageURLHttps());
setFavorited(status.isFavorited());
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public String getScreenName() {
return screenName;
}
public void setScreenName(String screenName) {
this.screenName = screenName;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getIconUrl() {
return iconUrl;
}
public void setIconUrl(String iconUrl) {
this.iconUrl = iconUrl;
}
public boolean isFavorited() {
return favorited;
}
public void setFavorited(boolean favorited) {
this.favorited = favorited;
}
}
データを入れる
Realm.getDefaultInstance()
した分だけ、realm.close();
する必要がある。
executeTransaction()
を使うことで楽にトランザクションできる。
copyToRealmOrUpdate()
を使うと追加するかprimaryKeyによってアップデートするかうまくしてくれる。
copyToRealm()
も存在し、これの場合は同じprimaryKeyを入れようとするとExceptionが投げられます。
final Realm realm = Realm.getDefaultInstance();
try {
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
for (Status status : homeTimeline) {
final Tweet tweet = new Tweet(status);
// プライマリーキーが同じならアップデート
realm.copyToRealmOrUpdate(tweet);
}
}
});
} finally {
// getしたらcloseする
realm.close();
}
ちなみにハンズオンではデータ取得とデータを入れるのにIntentServiceを利用していました。
データを取り出して表示する
realm.allObjectsSorted(Tweet.class, "createdAt", Sort.DESCENDING);
というように使うことでソートしたリストが取得できる。
realm = Realm.getDefaultInstance();
final RealmResults<Tweet> tweets = realm.allObjectsSorted(Tweet.class, "createdAt", Sort.DESCENDING);
データ変更時に自動的に更新する
RealmBaseAdapter
を使うとRealmのデータが変更された時に自動的にnotifyDataSetChanged()
が呼ばれて、いい感じにしてくれる。(RecyclerView用のものはこれから追加されるらしい。今はプルリクの状態:https://github.com/realm/realm-java/pull/2548 )
終了時にはrealm.close();
する。(Androidのライフサイクルに合わせる)
public class TimelineFragment extends ListFragment {
private Realm realm;
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
realm = Realm.getDefaultInstance();
final RealmResults<Tweet> tweets = buildTweetList(realm);
final RealmBaseAdapter<Tweet> adapter = new RealmBaseAdapter<Tweet>(getContext(), tweets, true) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final Tweet tweet = getItem(position);
if (convertView == null) {
convertView = inflater.inflate(R.layout.listitem_tweet, parent, false);
}
// TODO ViewHolderパターンを適用
((TextView) convertView.findViewById(R.id.screen_name)).setText(tweet.getScreenName());
((TextView) convertView.findViewById(R.id.text)).setText(tweet.getText());
Glide.with(TimelineFragment.this).load(tweet.getIconUrl()).into((ImageView) convertView.findViewById(R.id.image));
return convertView;
}
};
setListAdapter(adapter);
}
@NonNull
protected RealmResults<Tweet> buildTweetList(Realm realm) {
return realm.allObjectsSorted(Tweet.class, "createdAt", Sort.DESCENDING);
}
// 閉じる必要あり
@Override
public void onDestroyView() {
super.onDestroyView();
// Fragmentのバグ?でDestroyされた後もViewがrealmをいじってしまう場合があるので、realmの結果を使わないように変更
((RealmBaseAdapter<?>) getListAdapter()).updateRealmResults(null);
realm.close();
realm = null;
}
データを絞り込む
equalTo()を使うことで絞込ができるみたい
@NonNull
protected RealmResults<Tweet> buildTweetList(Realm realm) {
return realm.where(Tweet.class)
.equalTo("favorited", true)
.findAllSorted("createdAt", Sort.DESCENDING);
}
RxJavaと使う
(ハンズオンの内容にはありませんでしたが、、)
依存関係の追加
dependencies {
compile 'io.reactivex:rxjava:1.1.3'
}
こんな感じでObservableが使えるみたい
realm.allObjectsSorted(Tweet.class, "createdAt", Sort.DESCENDING).asObservable();
まとめ
普通に使いやすそうなので、これに速さなどの利点があって、Rxに対応していたりして、とても良いのではないかと思いました。