web-dev-qa-db-ja.com

不正なスレッドからのレルムアクセス

ユーザーが正しくログインしたときに、メッセージを受信するように登録するLoginActivityを持つアプリケーションがあります。そして、LoginActivityMainActivityにジャンプします。到着したメッセージは、メインのレルムインスタンスから回復するために、データベース(レルム)に格納されることになっています。

しかし、メッセージが到着すると、このエラーを起動してレルムがクラッシュします。

Exception in packet listener
    Java.lang.IllegalStateException: Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.
    at io.realm.BaseRealm.checkIfValid(BaseRealm.Java:383)
    at io.realm.Realm.executeTransactionAsync(Realm.Java:1324)
    at io.realm.Realm.executeTransactionAsync(Realm.Java:1276)
    at es.in2.in2tant.LoginActivity.newMessageReceived(LoginActivity.Java:124)
    at es.in2.in2tant.Connection.Connection$4$1.processMessage(Connection.Java:227)
    at org.jivesoftware.smack.chat.Chat.deliver(Chat.Java:180)
    at org.jivesoftware.smack.chat.ChatManager.deliverMessage(ChatManager.Java:351)
    at org.jivesoftware.smack.chat.ChatManager.access$300(ChatManager.Java:53)
    at org.jivesoftware.smack.chat.ChatManager$2.processPacket(ChatManager.Java:162)
    at org.jivesoftware.smack.AbstractXMPPConnection$4.run(AbstractXMPPConnection.Java:1126)
    at Java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.Java:1113)
    at Java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.Java:588)
    at Java.lang.Thread.run(Thread.Java:818)

Realmの仕組みに少し迷いました。アプリケーション全体でクラッシュせずにレルムにアクセスできるようにし、LoginActivityからこの受信メッセージを保存し続ける方法がわかりません。これを達成するためのいくつかの助け、またはアプローチ?

LoginActivity.Java

public class LoginActivity extends AppCompatActivity implements ConnectionConnectResponse {
.....
protected void onCreate(Bundle savedInstanceState) {
//Realm Init config:
        Realm.init(this);
        RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().build();
        Realm.deleteRealm(realmConfiguration); // Clean slate
        Realm.setDefaultConfiguration(realmConfiguration); // Make this Realm the default


@Override
    public void newMessageReceived(final ChatMessage message) {
        Logger.d("NEWMESSAGERECEIVED :" + message);


        realm.executeTransactionAsync(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {

                Message receivedMessage = realm.createObject(Message.class, message.id);
                receivedMessage.setBodyMessage(message.message);
                receivedMessage.setFrom(message.from);
                receivedMessage.setTo(message.to);
                receivedMessage.setDelivered(false);
                receivedMessage.setMine(false);
                receivedMessage.setDate(Calendar.getInstance().getTime());
            }
        });
        //Logger.d("NEWMESSRE: LAST MESSAGE:" + realm.where(Message.class).equalTo("chatID", message.id));
    }

@Override
    protected void onStart() {
        super.onStart();
        realm = Realm.getDefaultInstance();
    }

    @Override
    protected void onStop() {
        super.onStop();
        realm.close();
    }

必要なものの画像:

enter image description here

18
Shudy

誤ったスレッドからのレルムアクセス。 レルムオブジェクトは、作成されたスレッドでのみアクセスできます

このエラーメッセージは、一目瞭然です。

ご覧のとおり、UIスレッドでRealm.getDefaultInstance()を呼び出してrealmを初期化しています。

エラーはnewMessageReceived()から発生しているため、このメソッドはバックグラウンドスレッドから呼び出されていると思います。

バックグラウンドスレッドでRealmインスタンスを取得し、グローバルインスタンスの代わりにそれを使用します。

_@Override
public void run () {
    Realm backgroundRealm = Realm.getDefaultInstance();
    backgroundRealm.executeTransactionAsync(new Realm.Transaction() {
        @Override
        public void execute(Realm realm) {
            Message receivedMessage = realm.createObject(Message.class, message.id);
            receivedMessage.setBodyMessage(message.message);
            receivedMessage.setFrom(message.from);
            receivedMessage.setTo(message.to);
            receivedMessage.setDelivered(false);
            receivedMessage.setMine(false);
            receivedMessage.setDate(Calendar.getInstance().getTime());
        }
    });
}
_

または、何らかの理由でグローバルRealmインスタンスに固執したい場合は、runOnUiThread()(または、Runnableを介してHandlerをメインスレッドのメッセージキューに直接投稿します。

_@Override
public void newMessageReceived(final ChatMessage message) {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            realm.executeTransactionAsync(new Realm.Transaction() {
                @Override
                public void execute(Realm realm) {
                    Message receivedMessage = realm.createObject(Message.class,
                        message.id);
                    receivedMessage.setBodyMessage(message.message);
                    receivedMessage.setFrom(message.from);
                    receivedMessage.setTo(message.to);
                    receivedMessage.setDelivered(false);
                    receivedMessage.setMine(false);
                    receivedMessage.setDate(Calendar.getInstance().getTime());
                }
            });
        }
    });
}
_
19
earthw0rmjim

データベースにアクセスするたびにRealm backgroundRealm = Realm.getDefaultInstance()を作成し、realm.close()を使用して閉じることを忘れないでください。

1
latifalbr

トランザクションの前にインスタンスを割り当て、トランザクションが完了した直後にインスタンスを解放します。そのため、ライングリング接続ができなくなり、そうすることで、スレッドスケジューラによる節約を実行できます。データの操作には「データアクセスオブジェクト」インターフェイスを使用し、そのインターフェイスはレルムを使用して実装されます。 「トランザクション非同期」を使用せず、すべての呼び出しを同期的に使用しますが、バックグラウンドスレッドから「データアクセスオブジェクト」に対して呼び出しを実行します。そのための良い解決策-rxJavaライブラリ。常にRealmインスタンスを取得してリリースするだけです。ライブラリにより安価になります。

1
Alex Shutov

レルムはまだ使用していません。しかし、エラーメッセージから理解したのは、Loginactivityが終了したときにConnectionConnectResponseが生きている可能性があるということです。そのため、Realmインスタンスをパラメーターとして内部に渡す必要があります

newMessageReceived(Realm realm, final ChatMessage message)

このメソッドを起動するクラスにすべてのレルム初期化コードを配置します。

0
ugur