从其他线程访问非托管对象会抛出IllegalStateException

时间:2017-03-10 08:22:39

标签: android realm

我在项目中使用了RealmObjects

class A extends RealmObject {
   ...
   private B b;
   private RealmList<C> c;
   //Getter and Setters
}

class B extends RealmObject {
   ...
   private RealmList<D> d;
   //Getter and Setters
}

class C extends RealmObject {
   ...
   private String str1;
   private String str2;
   //Getter and Setter
}

class D extends RealmObject {
   ...
   private String str1;
   private String str2;
   //Getter and Setter
}

我从Realm获得了A的列表,如下所示:

Realm.where(A.class).findAllSortedAsync("sortField", Sort.DESCENDING)
              .asObservable().filter(items -> items.isLoaded())
              .map(new Func1<RealmResults<A>, List<A>>() {
                 @Override
                 public List<A> call(RealmResults<A> realmResults) {
                    return Realm.copyFromRealm(realmResults);
                 }
               })
               .observeOn(Schedulers.computation())
               .map(new Func1<List<A>, Object object>() {
                 @Override
                 public Object call(List<A> items) {
                   for (A item : items) {
                     item.get(...);
                     item.set(...);
                   }
                 }
               }).observeOn(AndroidSchedulers.mainThread()).subscribe(...);

当我运行上面的代码时,抛出以下异常:

FATAL EXCEPTION: RxComputationScheduler-2
java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.Worker thread.
   at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:59)
   at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:428)
   at java.util.concurrent.FutureTask.run(FutureTask.java:237)
   at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272)
   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
   at java.lang.Thread.run(Thread.java:761)
Caused by: 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:353)
   at io.realm.RealmResults.checkForAddRemoveListener(RealmResults.java:138)
   at io.realm.RealmResults.removeChangeListener(RealmResults.java:171)
   at io.realm.rx.RealmObservableFactory$6$2.call(RealmObservableFactory.java:156)
   at rx.subscriptions.BooleanSubscription.unsubscribe(BooleanSubscription.java:71)
   at rx.internal.util.SubscriptionList.unsubscribeFromAll(SubscriptionList.java:136)
   at rx.internal.util.SubscriptionList.unsubscribe(SubscriptionList.java:125)
   at rx.Subscriber.unsubscribe(Subscriber.java:98)
   at rx.internal.util.SubscriptionList.unsubscribeFromAll(SubscriptionList.java:136)
   at rx.internal.util.SubscriptionList.unsubscribe(SubscriptionList.java:125)
   at rx.Subscriber.unsubscribe(Subscriber.java:98)
   at rx.internal.util.SubscriptionList.unsubscribeFromAll(SubscriptionList.java:136)
   at rx.internal.util.SubscriptionList.unsubscribe(SubscriptionList.java:125)
   at rx.Subscriber.unsubscribe(Subscriber.java:98)
   at rx.internal.util.SubscriptionList.unsubscribeFromAll(SubscriptionList.java:136)
   at rx.internal.util.SubscriptionList.unsubscribe(SubscriptionList.java:125)
   at rx.Subscriber.unsubscribe(Subscriber.java:98)
   at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:72)
   at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.call(OperatorObserveOn.java:224)
   at rx.internal.schedulers.EventLoopsScheduler$EventLoopWorker$1.call(EventLoopsScheduler.java:172)
   at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
   at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:428) 
   at java.util.concurrent.FutureTask.run(FutureTask.java:237) 
   at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272) 
   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) 
   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)

看到如何在计算线程上抛出异常,我想当我尝试访问A类的对象时会发生这种情况。我不明白这是怎么发生的,因为我已经将托管的RealmResults转换为非托管List,因此在其他线程中访问它应该不是Realm wiki中提到的问题。

  

非托管对象就像普通的Java对象一样,它们不会被持久化,也不会自动更新。它们可以在线程之间自由移动。

我的问题是如何从RealmResults获取一个非托管的对象列表,这些对象可以从其他线程访问,以及我在上面的实现中做错了什么?

2 个答案:

答案 0 :(得分:1)

你错过了

}).observeOn(AndroidSchedulers.mainThread()) 
 .unsubscribeOn(AndroidSchedulers.mainThread()) // this
 .subscribe(...);

虽然考虑到你是在UI线程上复制你的结果,但是当我找到键盘时,我可能会更多地考虑这个问题。

编辑:好的,我认为应该这样工作:

    realm.where(A.class).findAllAsync()
          .asObservable()
          .subscribeOn(AndroidSchedulers.mainThread())
          .unsubscribeOn(AndroidSchedulers.mainThread())
          .observeOn(Schedulers.io())
          .map(new Func1<RealmResults<A>, List<A>>() {
             @Override
             public List<A> call(RealmResults<A> ignored) {
                try(Realm bgRealm = Realm.getDefaultInstance()) {
                    return bgRealm.copyFromRealm(
                        bgRealm.where(A.class).findAllSorted("sortField", Sort.DESCENDING)
                    );
                }
             }
           })
           .observeOn(Schedulers.computation())
           .map(new Func1<List<A>, Object>() {
             @Override
             public Object call(List<A> items) {
               for (A item : items) {
                 item.get(...);
                 item.set(...);
               }
             }
           })
           .observeOn(AndroidSchedulers.mainThread())
           .subscribe(...);

答案 1 :(得分:0)

使用方法Realm.where(A.class).findAllSortedAsync("sortField", Sort.DESCENDING),这将在主线程中发生。

使用方法map ... return Realm.copyFromRealm(realmResults);,Rx将在另一个线程中运行map方法,因此会发生错误:

  

Realm从错误的线程访问。只能访问领域对象   在他们创建的主题上。

如果你想在另一个线程中运行,你可以通过在该线程中创建另一个Realm实例来解决这个问题

map(new Func1<RealmResults<A>, List<A>>() {
                 @Override
                 public List<A> call(RealmResults<A> realmResults) {
                    Realm realm = Realm.getDefaultInstance();
                    try {
                        realm.beginTransaction();

                        // Your code here

                        realm.commitTransaction();
                    } finally {
                        realm.close();
                    }
                                     }
               })