【问题标题】:Realm access from incorrect thread来自错误线程的领域访问
【发布时间】:2017-03-13 16:34:53
【问题描述】:

我有一个带有LoginActivity 的应用程序,当用户正确登录时,我注册接收消息。 LoginActivity 跳转到 MainActivity。 到达的消息应该存储在数据库(Realm)中,以便从 Main 中的 Realm 实例中恢复。

但是当消息到达时它会崩溃领域启动此错误:

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 的工作原理有点迷茫,而且我不知道如何让整个应用程序可以访问 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();
    }

需要的图片:

【问题讨论】:

    标签: android realm


    【解决方案1】:

    来自错误线程的领域访问。 Realm 对象只能被访问 在创建它们的线程上

    这个错误信息是不言自明的。

    我看到您正在通过在 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());
                    }
                });
            }
        });
    }
    

    【讨论】:

    • 这里应该如何关闭领域实例?
    【解决方案2】:

    每次要访问数据库时只需创建Realm backgroundRealm = Realm.getDefaultInstance(),不要忘记使用realm.close()关闭它

    【讨论】:

      【解决方案3】:

      在事务之前分配实例并在事务完成后立即释放它,因此您不会有线性连接,这样做可以从线程调度程序中执行节省。 我使用“数据访问对象”接口来操作数据,该接口是使用 Realm 实现的。不要使用“事务异步”,同步使用所有调用,而是从后台线程对“数据访问对象”执行调用。很好的解决方案 - rxJava 库。 只需始终获取和释放 Realm 实例 - 库使其变得便宜。

      【讨论】:

      • 使用异步事务API没有任何问题。而且这家伙几乎不知道 Realm,你为什么还要告诉他用 Rx 去全 FRP?
      • 想象一下,您必须执行一个事务,并且在该事务完成后,执行写事务或一些读事务。只有在第一次完成时,您才必须开始第二次操作。如果您使用 async(),您将无法跟踪上一个事务何时完成。或者,如果您在一个线程上创建对象并意外在另一个线程上使用该实例(使用线程池),您将收到错误消息。另一方面,当你在事务之前分配实例并在事务之后释放它时,情况并非如此
      【解决方案4】:

      我还没有使用 Realm。但是我从错误消息中了解到,当 Loginactivity 死亡时 ConnectionConnectResponse 可能还活着。所以你应该在里面传递 Realm 实例作为参数

      newMessageReceived(Realm realm, final ChatMessage message)

      并将所有 Realm 初始化代码放在您触发此方法的类中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-03-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多