Android中线程错误的Realm访问

8

我正在处理Android上的这个问题:

错误线程访问Realm。 Realm对象只能在创建它们的线程上访问。

我想在我的RemoteViewsFactory中使用Realm。

public class RemoteViewsX implements RemoteViewsFactory {

    public RemoteViews getViewAt(int paramInt) {
    if (this.results != null && this.results.size() != 0 && this.results.get(paramInt) != null) {
    //FAILED HERE
    }

这个调用失败了!为什么?

我在我的类中以以下方式获取数据:

public void onDataSetChanged() {
        Realm realm = Realm.getInstance(RemoteViewsX.this.ctx);
        this.results =  realm.where(Model.class).findAll();
}

我像这样调用了我的remoteFactory:
public class ScrollWidgetService extends RemoteViewsService {
    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        return new RemoteViewsX (getApplicationContext());
    }
}

有什么想法吗?

1
我不熟悉RemoteViewsService,但显然你的onDataSetChanged方法是从另一个线程调用的,而不是你的getViewAt方法。我不确定这是因为RemoteViewsService的工作方式还是你的代码。 - Christian Melchior
好的,谢谢。我该如何修复它? - manua27
为了突破该领域的限制,您可以参考此帖子https://dev59.com/14Tba4cB1Zd3GeqP9KdT#32607241。它展示了一种包装器实现,从而使您的应用程序免受来自错误线程异常的Realm访问束缚。 - Ripal Tamboli
2个回答

2

如果问题是由于从另一个线程调用onDataSetChangedgetViewAt引起的,您可以强制它们使用相同的线程,创建自己的HandlerThread,如下所示:

public class Lock {
    private boolean isLocked;

    public synchronized void lock() throws InterruptedException {
        isLocked = true;
        while (isLocked) {
            wait();
        }
    }

    public synchronized void unlock() {
        isLocked = false;
        notify();
    }
}

public class MyHandlerThread extends HandlerThread {
    private Handler mHandler;

    public MyHandlerThread() {
        super("MY_HANDLER_THREAD");
        start();
        mHandler = new Handler(getLooper());
    }

    public Handler getHandler() {
        return mHandler;
    }
}

public class RemoteViewsX implements RemoteViewsFactory {
    private MyHandlerThread mHandlerThread;
    ...
}

public void onDataSetChanged() {
    Lock lock = new Lock();
    mHandlerThread.getHandler().post(new Runnable() {
        @Override
        public void run() {
            Realm realm = Realm.getInstance(ctx);
            results = realm.where(Model.class).findAll();
            lock.unlock();
        }
    });
    lock.lock();
}

public RemoteViews getViewAt(int paramInt) {
    Lock lock = new Lock();
    final RemoteViews[] result = {null};
    mHandlerThread.getHandler().post(new Runnable() {
        @Override
        public void run() {
            // You can safely access results here.
            result[0] = new RemoteViews();
            lock.unlock();
        }
    });
    lock.lock();
    return result[0];
}

我从这个页面复制了Lock类:http://tutorials.jenkov.com/java-concurrency/locks.html
当你的任务完成后,不要忘记退出处理程序线程。


关于“退出”,您的意思是什么? - manua27
我指的是这个方法:http://developer.android.com/reference/android/os/HandlerThread.html#quit() 除非你调用这个方法,否则线程不会停止。 - usp

0

我基于 RxJava,所以我在 realm 中执行此操作。我克隆每个项,因为它们是主线程的一部分,在另一个线程(例如小部件主屏幕)中工作时会出现问题。

myRealm.where( Dog.class ).findAllAsync().subscribe( mainThreadDogs->{
       thisThreadDogs.clear();

       for( Dog dog: mainThreadDogs ){
            thisThreadDogs.add( ModelUtil.cloneDog( dog ) ); 
       }    
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接