Android Realm — доступ к объекту Realm из службы

У меня есть объект области, который создается в моей деятельности. Мне нужно иметь доступ к этому объекту в службе, которую я создал. Однако я получаю сообщение об ошибке при создании объекта Realm в службе

        mRealm = Realm.getInstance(getApplicationContext());

java.lang.IllegalStateException: доступ к области из неправильного потока. Доступ к объектам Realm возможен только в том потоке, в котором они были созданы.

Теперь я понимаю, что это означает, что, поскольку объект области был создан в моей деятельности, я не могу получить к нему доступ из фонового потока. Тем не менее, я не нахожу простого способа обойти это, кроме создания собственного пользовательского потока обработчика, но это просто кажется неуклюжим способом сделать это.

Я что-то упустил или действительно нет лучшего способа получить доступ к объекту Realm из разных потоков?

Обновление:

Я копнул немного глубже, чтобы понять, что в IntentService метод onHandleIntent выполняется в отдельном потоке, чем другие методы в классе. Поэтому я не могу создать экземпляр Realm на уровне класса и иметь возможность взаимодействовать с ним внутри и снаружи метода onHandleIntent. Это то, что вызывало исключение потока. Помимо создания отдельного экземпляра Realm в каждом методе, который мне нужен для доступа к объекту и извлечения его снова и снова, я думаю, что ответ Ильи Третьякова будет лучшим. Я могу скопировать объект из области в свой конструктор, а затем работать с ним на протяжении всего срока службы. Любые методы, которые должны выполнять обратную запись в объект Realm, должны будут создавать свой собственный экземпляр Realm в этом методе.


person Landen    schedule 18.02.2016    source источник
comment
Неудобно, когда они ограничивают экземпляр одним потоком. Вы можете опубликовать исполняемый файл в действии, и оно будет запущено в потоке действия (если у вас все еще есть доступ к действию).   -  person Andrew Williamson    schedule 18.02.2016
comment
Предлагаемый способ - сделать запрос в этой службе, чтобы получить тот же объект и использовать этот объект в службе. Все будет просто, если у вашего объекта есть первичный ключ. Вы можете сделать MyObject obj = mRealm.where(MyObject.class).equalTo("id", 123).findFirst(). Когда вы меняете obj в сервисе, тот, что в действии, будет автоматически изменен в следующем цикле пользовательского интерфейса. Вы можете использовать намерение для передачи первичного ключа между ними. См. realm.io/docs/java/latest/#threading и realm.io/docs/java/latest/#intents   -  person beeender    schedule 19.02.2016


Ответы (2)


Вы можете попробовать использовать realm.copyFromRealm(youRealmObject);. Эти методы копируют данные Realm в обычные объекты Java и отделяют их от Realm.

Вот пример использования:

youRealmObject = realm.copyFromRealm(youRealmObject);

Вот информация об этом из документов:

Создает автономную копию в памяти уже сохраненного объекта RealmObject. Это глубокая копия, которая копирует все объекты, на которые есть ссылки. Все скопированные объекты отсоединяются от Realm, поэтому они больше не будут автоматически обновляться. Это означает, что скопированные объекты могут содержать данные, которые больше не согласуются с другими управляемыми объектами Realm. ВНИМАНИЕ. Любые изменения в скопированных объектах могут быть объединены обратно в Realm с помощью copyToRealmOrUpdate(RealmObject), но все поля будут переопределены, а не только те, которые были изменены. Это включает ссылки на другие объекты и потенциально может переопределить изменения, сделанные другими потоками.

https://realm.io/docs/java/latest/api/io/realm/Realm.html#copyFromRealm-E-

person Ilya Tretyakov    schedule 19.02.2016

Технически вы должны открыть экземпляр Realm в начале фонового потока, закрыть его в конце выполнения в этом фоновом режиме и передать его промежуточным методам.

public void handleIntent() { // or doInBackground etc
    Realm realm = null;
    try {
         realm = Realm.getDefaultInstance();
         .... 
         MyObj obj = realm.where(MyObj.class)
                          .equalTo(MyObjFields.ID, myObjId)
                          .findFirst(); // get by id
         .... 
    } finally {
         if(realm != null) {
              realm.close(); // important 
         } 
    } 
} 

Использование realm.copyFromRealm() — это обходной путь, а не решение.


С AS 3.0 вы можете использовать try-with-resources независимо от того, какой у вас minSDK (так же, как если бы вы использовали Retrolambda):

public void handleIntent() { // or doInBackground etc
    try(Realm realm = Realm.getDefaultInstance()) {
         .... 
         MyObj obj = realm.where(MyObj.class)
                          .equalTo(MyObjFields.ID, myObjId)
                          .findFirst(); // get by id
         .... 
    } // auto-close
} 
person EpicPandaForce    schedule 27.10.2016
comment
Спасибо, что упомянули совет try-with-resources. Я не знал об этой функции на AS 3 - person Nicolás Carrasco-Stevenson; 20.06.2018