Доступ к царству из неправильной нити
У меня есть приложение с LoginActivity
, которое при правильном входе в систему регистрируется для приема сообщений. И LoginActivity
переходит на MainActivity
.
Ожидаемые сообщения должны храниться в базе данных (Realm) для восстановления из экземпляра Realm в Main.
Но когда приходит сообщение. Это аварийное царство запускает это сообщение:
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();
}
Изображение того, что необходимо:
![введите описание изображения здесь]()
Ответы
Ответ 1
Доступ в Realm из неправильного потока. Объекты Realm могут быть доступны только в потоке они были созданы.
Это сообщение об ошибке довольно понятно.
Как я вижу, вы инициализируете realm
, вызывая Realm.getDefaultInstance()
в потоке пользовательского интерфейса.
Ошибка возникает из 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
Выделите экземпляр перед транзакцией и выпустите его сразу после завершения транзакции, поэтому у вас не будет подключения к линии, и при этом вы будете экономить на планировщике потоков.
Я использую интерфейс Data Access Object для манипуляций с данными, и этот интерфейс реализуется с использованием Realm. Не используйте транзакцию async, используйте все вызовы синхронно, но выполняйте вызовы "объекта доступа к данным" из фонового потока. Хорошее решение для этого - библиотека rxJava.
Просто получите и освободите экземпляр Realm все время - библиотека делает его недорогим.
Ответ 3
Я еще не использовал Realm. Но то, что я понял из сообщения об ошибке, заключается в том, что ConnectionConnectResponse может быть живым, когда Logactivity умирает. Поэтому вы должны передать экземпляр Realm в качестве параметра внутри
newMessageReceived(Realm realm, final ChatMessage message)
и поместите весь код инициализации Realm в класс, в котором вы запускаете этот метод.