J'ai une application avec un LoginActivity
, que lorsque l'utilisateur se connecte correctement, je m'inscris pour recevoir des messages. Et LoginActivity
passe à MainActivity
. Les messages entrants sont supposés être stockés dans une base de données (Realm), afin de récupérer une instance de Realm dans Main.
Mais quand le message arrive, le crash provoque le lancement de cette erreur:
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)
Je suis un peu perdu sur le fonctionnement de Realm, et je ne sais pas comment rendre le royaume accessible dans l’application sans incident et continuer à stocker les messages reçus de LoginActivity
. Une aide, ou des approches pour y parvenir?
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();
}
Image de ce qui est nécessaire:
Accès au domaine par un thread incorrect. Seuls les objets de domaine sont accessibles sur le fil ils ont été créés .
Ce message d'erreur est assez explicite.
Comme je vois, vous initialisez realm
en appelant Realm.getDefaultInstance()
sur le thread d'interface utilisateur.
L'erreur provient de newMessageReceived()
, donc je suppose que cette méthode est appelée depuis un thread en arrière-plan.
Vous pouvez soit obtenir une instance Realm
sur le thread d'arrière-plan et l'utiliser à la place de l'instance globale:
@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());
}
});
}
Ou , si vous souhaitez vous en tenir à l'instance globale Realm
pour une raison quelconque, assurez-vous que votre code est exécuté sur le thread d'interface utilisateur en appelant runOnUiThread()
(ou en publiant directement une Runnable
à la file de messages du thread principal à travers une 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());
}
});
}
});
}
Allouez l'instance avant la transaction et relâchez-la juste après la fin de la transaction pour ne pas avoir de connexion linéaire, ce qui vous permet d'économiser depuis le planificateur de threads . J'utilise l'interface 'Data Access Object' pour les manipulations de données. L'interface est implémentée à l'aide de Realm. N'utilisez pas «transaction async», utilisez tous les appels de manière synchrone, mais effectuez des appels sur un «objet d'accès aux données» à partir du fil d'arrière-plan. Bonne solution pour cela - librairie rxJava. Il suffit d’obtenir et de libérer l’instance de Realm tout le temps - la bibliothèque le rend peu coûteux.
Il suffit de créer Realm backgroundRealm = Realm.getDefaultInstance()
chaque fois que vous souhaitez accéder à la base de données et n'oubliez pas de la fermer avec realm.close()