

こんにちは。realmの例外についてです。  
Wordというモデルクラスのインスタンスにint 型のrandomIdというランダムな値をセットしたいのですが、

> java.lang.IllegalStateException: Cannot modify managed objects outside of a
> write transaction.

が起きてしまいます。なぜでしょうか。

試したこととしてはsetRandomId()にランダムではなく任意の数値を入れてみたり、他activityにて問題なく作動している値をsetするメソッド（setMeaningというメソッドを使いましたが内容は省略します。）を使うなどしましたが、その場合も同様のエラー文が出てきます。  
スタックオーバーフロー上ではandroidでのこの例外処理に関する情報がなく質問致しました。

[code]

        private void setRandom() {
        mEditor.putBoolean("random", true);
        mEditor.apply();
    
        mRealm = Realm.getDefaultInstance();
        RealmResults<Word> realmResults = mRealm.where(Word.class).findAll();
        int realmSize = realmResults.size();
        mRealm.close();
    
        boolean num[] = new boolean[realmSize]; //重複判定用
        Random rand = new Random(); //ランダムな数値
    
        // すべての重複判定用配列をfalseにしておく
        for(int i=0; i<realmSize; i++){
            num[i] = false;
        }
    
        for (int i = 0; i < realmSize; ) {
    
            int p = rand.nextInt(realmSize);
    
            if(!num[p]){
                Word word = mRealm.where(Word.class).equalTo("id", i).findFirst();
                word.setRandomId(p);  //ここでクラッシュがおきる
                num[p] =true;
                i++;
                mRealm.beginTransaction();
                mRealm.copyToRealmOrUpdate(word);
                mRealm.commitTransaction();
    
                mRealm.close();
            }
        }
    }
    
[/code]


CC BY-SA 4.0
2018-05-16T04:51:36.327
44016
2018-05-16T07:53:50.517
27793
question
android realm
android realmの例外処理。 Cannot modify managed objects outside of a write transaction

[code]

    Cannot modify managed objects outside of a write transaction.
    
[/code]

エラーメッセージの通りです。Realmに保存されたオブジェクト（＝managed
objects）のプロパティを変更する場合は必ずトランザクションの中で行う必要があります。  
（Realmに保存する前のオブジェクトはただのオブジェクトなのでその限りではありません）

下記のコードの`word`オブジェクトは`mRealm.where(Word.class).equalTo("id",
i).findFirst();`でRealmに保存されているオブジェクトを取得したものなので、このオブジェクトはManaged
objectです。変更はトランザクション内で行う必要があります。

[code]

    if(!num[p]){
        Word word = mRealm.where(Word.class).equalTo("id", i).findFirst();
        word.setRandomId(p);  //ここでクラッシュがおきる
        num[p] =true;
        i++;
        mRealm.beginTransaction();
        mRealm.copyToRealmOrUpdate(word);
        mRealm.commitTransaction();
    
        mRealm.close();
    }
    
[/code]

簡単に直すなら次のようにトランザクションの範囲を広げます。

[code]

    if(!num[p]){
        mRealm.beginTransaction();
    
        Word word = mRealm.where(Word.class).equalTo("id", i).findFirst();
        word.setRandomId(p);  //ここでクラッシュがおきる
        num[p] =true;
        i++;
    
        mRealm.copyToRealmOrUpdate(word);
        mRealm.commitTransaction();
    
        mRealm.close();
    }
    
[/code]

ただし、この処理はForループの中で行われているので、特に理由がなければトランザクションはループの外側まで広げる方がいいです。トランザクションのコミットはそれなりに高価な処理なので、ループ中で毎回トランザクションを開始してコミットしているとパフォーマンスが悪くなります。


CC BY-SA 4.0
2018-05-16T07:53:50.517
44017
2018-05-16T07:53:50.517
5519
44016
answer