2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Realmの初期データをKotlinで作る

Posted at

プロジェクトでカタログアプリのようなものを作る機会がありました。
そこでは、商品の切り替えはあまりなく、あっても1年に1度程度、アプリのアップデートと絡めてで問題ないということでした。
そのため、Realmのデータベースを別途作成、それをiOS/Androidの両方のアプリに組み込む形としました。

単純なCSVからテーブルを作るだけであれば、Realm Studioの機能でできるのですが、ある程度正規化したりしながら作る場合、プログラムを書く必要があります。

Swiftでの初期データ作成は記事があったのですが、個人的にはAndroidの方が得意だったので、Kotlinでやってみました。

(Swiftでの記事: https://qiita.com/negibouze/items/0fe2928ea2f00d8d3cd3

完成形

Androidエミュレータを起動し、DB作成用のアプリを実行、adbコマンドを利用してローカルにコピーする形です。
(本当は、Androidを利用せずにやりたかったのですが、単独のJava用SDKなどが見当たらなかったので、この形にしました。)

コマンドとしては、下記のとおりです。

./gradlew :realmcreator:installDebug
adb shell am start -n hm.orz.chaos114.android.realmcreator/hm.orz.chaos114.android.realmcreator.MainActivity
adb shell run-as hm.orz.chaos114.android.realmcreator cat files/output.realm > default.realm

取得できた default.realm を、利用したいアプリの app/src/main/assets/ に配置し、下記のように読み込めます。

val config = RealmConfiguration.Builder()
        .name(Realm.DEFAULT_REALM_NAME)
        .assetFile("default.realm")
        .readOnly()
        .build()
realm = Realm.getInstance(config)

今回のサンプル

この前の世界バレーの結果を、下記のような形式のCSVにしたものを使います。

POOL A 日本 オランダ アルゼンチン ドイツ カメルーン メキシコ
日本 -
オランダ 3-2 -
アルゼンチン 0-3 0-3 -
ドイツ 0-3 1-3 3-0 -
カメルーン 0-3 0-3 0-3 0-3 -
メキシコ 0-3 0-3 3-0 0-3 1-3 -
POOL B 中国 イタリア トルコ ブルガリア カナダ キューバ
中国 -
イタリア 3-1 -
トルコ 0-3 0-3 -
ブルガリア 1-3 0-3 0-3 -
カナダ 0-3 0-3 0-3 1-3 -
キューバ 0-3 0-3 1-3 0-3 1-3 -

これを、「国テーブル」と「対戦結果テーブル」に分割して登録してみました。

image.png

image.png

データベース作成の実装

まず、 realmcreator/src/main/assets/ にCSVファイルを配置します。

そしてコードとしては、

  • ActivityのonCreateで Realm オブジェクトを作成
  • 古いデータを削除
  • CSVデータを読みながら登録
  • writeCopyToを利用してサイズの縮小

といった流れです。

CSVの読み込みには、 Apache Commons CSVを利用しました。

package hm.orz.chaos114.android.realmcreator

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.text.TextUtils
import hm.orz.chaos114.android.realmcreator.entity.Country
import hm.orz.chaos114.android.realmcreator.entity.Match
import io.realm.Realm
import org.apache.commons.csv.CSVFormat
import org.apache.commons.csv.CSVRecord
import java.io.BufferedReader
import java.io.File
import java.io.InputStreamReader


class MainActivity : AppCompatActivity() {

    lateinit var realm: Realm

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        Realm.init(this)
        realm = Realm.getDefaultInstance()

        val outputFile = File(filesDir, "output.realm")
        deleteOldData(outputFile)

        createCountries()
        createMatch()

        // reduce size
        realm.writeCopyTo(outputFile)
    }

    private fun deleteOldData(outputFile: File) {
        realm.beginTransaction()
        realm.deleteAll()
        realm.commitTransaction()
        outputFile.delete()
    }

    private fun createCountries() {
        val reader = BufferedReader(InputStreamReader(resources.assets.open("match.csv")))
        reader.use {
            val records = CSVFormat.EXCEL.parse(reader)

            var poolName: String? = null
            realm.beginTransaction()
            records.records.forEach loop@{ record ->
                if (record.get(0).isEmpty()) {
                    poolName = null
                    return@loop
                }
                if (poolName == null) {
                    poolName = record.get(0)
                    return@loop
                }

                val obj = Country(
                        name = record.get(0),
                        pool = poolName!!
                )
                realm.copyToRealm(obj)
            }
            realm.commitTransaction()
        }
    }

    private fun createMatch() {
        val reader = BufferedReader(InputStreamReader(resources.assets.open("match.csv")))
        reader.use {
            val records = CSVFormat.EXCEL.parse(reader)

            var header: CSVRecord? = null
            realm.beginTransaction()
            records.records.forEach loop@{ record ->
                if (record.get(0).isEmpty()) {
                    header = null
                    return@loop
                }
                if (header == null) {
                    header = record
                    return@loop
                }

                val country1: Country = getCountry(record.get(0))
                record.forEachIndexed colLoop@{ index, colmn ->
                    if (index == 0) {
                        return@colLoop
                    }
                    if (TextUtils.isEmpty(colmn) || colmn.equals("-")) {
                        return@colLoop
                    }
                    val obj = Match(
                            country1 = country1,
                            country2 = getCountry(header!!.get(index)),
                            result = colmn
                    )
                    realm.copyToRealm(obj)
                }
            }
            realm.commitTransaction()
        }
    }

    private fun getCountry(name: String): Country {
        return realm.where(Country::class.java).equalTo("name", name).findFirst()!!
    }
}

createCountriescreateMatch で同じようなループをしており、処理としては無駄が大きいのですが、それぞれのテーブル作成条件が変わったりといった仕様変更に耐えやすかと思い、冗長に書いています。

Realm#writeCopyTo については、これを行うことで出力されるファイルサイズが減るようです。
今回のサンプルでは、8kByteが4kByteといった差となりました。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?