13
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

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

最近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にあります。フィードバックや問題やプルリクエストは誠にありがたいです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
13
Help us understand the problem. What are the problem?