Edited at
RealmDay 8

DynamicRealmを使って、Realm Browserを作ってみましょう!

More than 3 years have passed since last update.

最近realmはDynamic Reamsを含んだrealm0.86をリリースしました。Dynamic Realmsによって私たちはRealm Schemaにアクセスし、そしてオブジェクトを作り、文字列(モデルネームとフィールドネーム)を使ってフィールドに動的にアクセスできます。


Concept

Realm browserはテーブル、構造、コンテンツを見ることに使われます。このブラウザはAndroidデバイスによって提供されるローカルホストからアクセスすることができます。


NanoHTTP

NanoHTTPは他のアプリケーションに埋め込まれた軽量のHTTPサーバーで、修正BSDライセンスの下でリリースされました。


NanoHTTPD sample

public class HelloServer extends NanoHTTPD {

public static void main(String[] args) {
ServerRunner.run(HelloServer.class);
}

public HelloServer() {
super(8080);
}

@Override
public Response serve(IHTTPSession session) {
String msg = "<html><body><h1>Hello server</h1></body></html>";

return newFixedLengthResponse(msg);
}
}

このシンプルなコードはHello serverテキストでHTMLページを作りそれはローカルホストのポート8080からアクセスできます。


DynamicRealm

最初に言ったように、Dynamic Realmsは従来のRealmsの柔軟なバージョンと言えます。Dynamic Realmsは従来のRealmで使っていた物と同じconfigurationを使えるが、どんなschema定義、migration、schemaのバージョンも無視します。

Dynamic Realmsを使ったら、モデル名とフィールド名が分かればコンテンツをアックセスすることができます。

DynamicRealmObject user = realm.createObject("User");

String name = user.getString("name");
int age = user.getString("age");

DynamicRealmは柔軟性のために型安全とパフォーマンスを犠牲にしています。


RealmBrowser


実装に関して

重要なコードだけを説明するので、フルコードを見たい場合は私のgithubリポジトリを見てください。

最初に前回のセッションからパラメーターがあるかないかチェックします。

Map<String, String> params = session.getParms();

String className = params.get("class_name");
String selectedView = params.get("selected_view");

そして、そのクラスがあるかないかチェックし、そのクラスがRealmObjectのサブクラスかじゃないかチェックします。

Class clazz = Class.forName(className);

if (RealmObject.class.isAssignableFrom(clazz)) {
// Show structure or content
}

そのクラス名から普通のqueryを実行して、resultsを取ります。

RealmResults<DynamicRealmObject> realmResults = dynamicRealm.where(simpleTableName).findAll();

RealmSchemaからフィールド名を取れます。

RealmObjectSchema realmObjectSchema = dynamicRealm.getSchema().get(simpleTableName);

Set<String> fieldNames = realmObjectSchema.getFieldNames();

今フィールドタイプを直接取れるAPIはまだ実装されていませんが、私はgithub issueを登録して、realmチームが解決してくれたので次のバージョン(0.87)のリリースに入っているはずです。

そして、フィールドタイプを取るために私は他の方法を考えて、一つ一つのオブジェクトをタイプチェックを行います。

private RealmFieldType checkRealmFieldType(Object obj) {

int nativeValue = -1;
if (obj instanceof Long || obj instanceof Integer || obj instanceof Short || obj instanceof Byte) {
nativeValue = 0;
} else if (obj instanceof Boolean) {
nativeValue = 1;
} else if (obj instanceof String) {
nativeValue = 2;
} else if (obj instanceof byte[] || obj instanceof ByteBuffer) {
nativeValue = 4;
} else if (obj == null || obj instanceof Object[][]) {
nativeValue = 5;
} else if (obj instanceof Mixed) {
nativeValue = 6;
} else if (obj instanceof Date) {
nativeValue = 7;
} else if (obj instanceof Float) {
nativeValue = 9;
} else if (obj instanceof Double) {
nativeValue = 10;
}
return RealmFieldType.fromNativeValue(nativeValue);
}

そして、さっきのresultsから一番目のオブジェクトを取って、さっき定義した機能でタイプチェックを行います。

int index = 0;

for (String fieldName : fieldNameArray) {
Object object = realmResults.get(0).get(fieldName);
realmFieldTypes[index] = checkRealmFieldType(object);
index++;
}

最後にコンテンツを取るために、テーブルを反復して、フィールドタイプによってgetter機能でvalueを取ります。

for (int i = 0; i < tableSize; i++) {

DynamicRealmObject dynamicRealmObject = realmResults.get(i);
for (int j = 0; j < columnCount; j++) {
String columnName = fieldNameArray[j];
String value = "";
switch (realmFieldTypes[j]) {
case INTEGER:
value = String.valueOf(dynamicRealmObject.getLong(columnName));
break;
case BOOLEAN:
value = String.valueOf(dynamicRealmObject.getBoolean(columnName));
break;
case FLOAT:
value = String.valueOf(dynamicRealmObject.getFloat(columnName));
break;
case DOUBLE:
value = String.valueOf(dynamicRealmObject.getDouble(columnName));
break;
case DATE:
value = dynamicRealmObject.getDate(columnName).toString();
break;
case STRING:
value = dynamicRealmObject.getString(columnName);
break;
}
}
}


使い方

このライブラリーの使い方はRealmBrowserのインスタンスを作って、RealmBrowser.start();という機能を呼んだらRealmBrowserが始まります。ポートを設定したい場合に、パラメーターを入れてください。使い終わったら RealmBrowser.stop();を呼んでください。

ブラウザからアックセスしたい場合に、realmBrowser.showServerAddress(this); という機能を呼んで、logcatでRealmBrowserのaddressが表示されます。

注意:AndroidデバイスとPCやMacが同じWi-Fiに接続していないとアクセスすることができません。


最後に

このライブラリーは改善の余地がまだまだあると思います。今週中に検索機能を追加したいと今度HTML Pageの作り方も直したいと思います。

今フルコードはgithubにあります。フィードバックや問題やプルリクエストは誠にありがたいです。