Realm跨线程访问异常求助:Spinner监听器操作Realm对象报错
解决Realm跨线程访问的IllegalStateException问题
嗨,这个问题我太熟了——你踩了Realm最常见的线程安全坑之一!报错信息已经说得很清楚了:Realm对象是线程绑定的,你在UI线程拿到的loginDatas,绝对不能拿到后台线程去操作。
为什么你之前的尝试没用?
- 你创建新Realm实例的思路是对的,但你还是在后台事务里直接访问了UI线程的
loginDatas对象,这依然会触发线程检查 - 用
runOnUiThread把事务放UI线程虽然能解决线程问题,但会阻塞UI界面,绝对不推荐这么做
正确的解决方案:在后台线程重新查询对象
既然loginDatas是UI线程的,那我们就在后台事务里用当前线程的Realm实例重新查询一次,这样拿到的对象属于后台线程,就能安全修改了。
修改后的代码如下:
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { selectedLang = langList.get(position); realm.executeTransactionAsync(new Realm.Transaction() { @Override public void execute(Realm realm) { // 关键:在后台线程中重新查询LoginData对象 LoginData threadLocalLoginDatas = realm.where(LoginData.class).findFirst(); if(threadLocalLoginDatas != null && threadLocalLoginDatas.getUser() != null) { threadLocalLoginDatas.getUser().setLanguage(selectedLang); // 划重点:Realm托管对象修改后自动同步,不需要手动调用insertOrUpdate! // realm.insertOrUpdate(threadLocalLoginDatas); // 这行可以删掉 } } }, new Realm.Transaction.OnSuccess() { @Override public void onSuccess() { FragmentTransaction ft = getFragmentManager().beginTransaction(); ft.detach(UserFragment.this).attach(UserFragment.this).commit(); } }, new Realm.Transaction.OnError() { @Override public void onError(Throwable error) { error.printStackTrace(); } }); } @Override public void onNothingSelected(AdapterView<?> parent) { selectedLang = langList.get(0); } });
几个关键细节要注意:
- 后台事务必须用当前线程的Realm查询对象:每个Realm实例都是线程专属的,后台事务里的
realm参数就是当前后台线程的实例,用它查询的对象才可以安全操作 - 托管对象无需手动插入更新:从Realm查询出来的对象是“托管”状态,修改后会自动同步到数据库,你之前写的
realm.insertOrUpdate(loginDatas)完全是多余的 - 记得关闭Realm实例:Fragment销毁时一定要关闭Realm,避免内存泄漏:
@Override public void onDestroyView() { super.onDestroyView(); if(realm != null && !realm.isClosed()) { realm.close(); } }
备选方案:传递主键查询
如果你的LoginData有明确的主键(比如userId),也可以先在UI线程拿到主键值,再到后台线程用主键查询,效果是一样的:
// UI线程先获取主键 final String loginDataPrimaryKey = loginDatas.getUserId(); realm.executeTransactionAsync(new Realm.Transaction() { @Override public void execute(Realm realm) { LoginData threadLocalLoginDatas = realm.where(LoginData.class) .equalTo("userId", loginDataPrimaryKey) .findFirst(); // 后续修改操作和上面一致 } // ... 成功/失败回调 });
这样修改后,你应该就能解决这个线程异常问题了!
内容的提问来源于stack exchange,提问作者theanilpaudel
相关产品推荐
相关产品推荐

