2016-10-31 7 views
12

Ich habe eine Anwendung mit einem LoginActivity, dass, wenn der Benutzer richtig anmelden, ich mich registrieren, um Nachrichten zu empfangen. Und die LoginActivity springt auf MainActivity. Die ankommenden Nachrichten sollen in der Datenbank (Realm) gespeichert werden, um sich von einer Realm-Instanz in Main zu erholen.Realm-Zugriff von falschem Thread

Aber wenn die Nachricht eintrifft Es Crash Bereich dieses errror Start:

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) 

Ich bin ein wenig verloren, wie Realm funktioniert, und ich weiß nicht, wie Reich ohne eine über die Anwendung zugänglich zu machen Absturz und speichern Sie diese empfangenen Nachrichten von LoginActivity. Einige Hilfe oder Ansätze, um dies zu erreichen?

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(); 
    } 

Bild von dem, was benötigt wird:

enter image description here

Antwort

14

Realm Zugriff von falschem Thread. Auf Realmobjekte kann nur in dem Thread zugegriffen werden, in dem sie erstellt wurden.

Diese Fehlermeldung ist ziemlich selbsterklärend.

Wie ich sehe, initialisieren Sie realm durch Aufruf von Realm.getDefaultInstance() auf dem UI-Thread.

Der Fehler kommt von newMessageReceived(), also denke ich, dass diese Methode von einem Hintergrundthread aufgerufen wird.

Entweder erhalten eine Realm Instanz auf dem Hintergrund-Thread und dass statt der globalen Instanz verwenden:

@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()); 
     } 
    }); 
} 

Oder, wenn Sie aus irgendeinem Grund auf die globale Realm Beispiel bleiben möchten, dann machen sicher, dass Ihr Code wird durch den Aufruf runOnUiThread() auf dem UI-Thread ausgeführt (oder direkt ein Runnable an die Nachrichtenwarteschlange des Haupt-Threads durch ein Handler Posting):

@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()); 
       } 
      }); 
     } 
    }); 
} 
1

Instanz zuweisen vor der Transaktion und lassen Sie ihn direkt nach der Transaktion abgeschlossen ist, so dass Sie nicht Linegring-Verbindung haben und damit Einsparungen im Thread-Scheduler erzielen. Ich benutze 'Data Access Object' Schnittstelle für Manipulationen mit Daten und diese Schnittstelle wird mit Realm implementiert. Verwenden Sie nicht 'Transaktion async', verwenden Sie alle Aufrufe synchron, führen Sie jedoch Aufrufe des Datenzugriffsobjekts aus dem Hintergrundthread aus. Gute Lösung dafür - rxJava-Bibliothek. Einfach die Realm-Instanz holen und freigeben - die Bibliothek macht es billig.

+0

Es gibt nichts Falsches an der Verwendung der asynchronen Transaktions-API. Und der Typ kennt Realm kaum, warum würdest du ihm auch sagen, dass er mit Rx voll-FRP gehen soll? – EpicPandaForce

+0

Stellen Sie sich vor, Sie müssen eine Transaktion ausführen und nach Abschluss dieser Transaktion eine Schreibtransaktion oder eine Lesetransaktion durchführen. Sie müssen den zweiten Vorgang erst starten, wenn Sie ihn abgeschlossen haben. Wenn Sie async() verwenden, können Sie nicht nachverfolgen, wann die vorherige Transaktion abgeschlossen wurde. Oder wenn Sie ein Objekt in einem Thread erstellen und diese Instanz versehentlich auf einem anderen Thread verwenden (mit Thread-Pool), erhalten Sie einen Fehler. Wenn Sie jedoch die Instanz vor der Transaktion zuweisen und sie nach der Transaktion freigeben, ist dies nicht der Fall –

0

Ich habe Realm noch nicht verwendet. Aber was ich von der Fehlermeldung verstanden habe, ist, dass ConnectionConnectResponse am Leben sein kann, wenn Loginactivity stirbt. So sollten Sie Realm-Instanz als Parameter innerhalb

newMessageReceived(Realm realm, final ChatMessage message)

und setzen alle Realm Init-Code in der Klasse übergeben, wo Sie diese Methode ausgelöst.

Verwandte Themen