如何访问不同Realm实例创建的托管对象?Android Realm数据库关闭异常处理
嘿,我来帮你解决这两个Realm开发里的常见问题👇
1. 如何访问由不同Realm实例创建的托管对象?
首先得明确:Realm的托管对象是和创建它的Realm实例强绑定的,而且只能在创建它的线程里使用,直接跨实例/跨线程访问肯定会报错。要安全访问其他Realm实例创建的对象,有两种靠谱的方式:
- 通过主键重新查询:如果你的对象模型定义了主键(用
@PrimaryKey注解标记),那在另一个Realm实例里,直接通过主键查询就能拿到属于当前实例的托管对象。这种方式最推荐,因为新对象能正常进行读写操作,完全符合Realm的线程安全规则。 - 转成非托管对象:如果只是要读取数据、不需要再同步到数据库,可以用
realm.copyFromRealm(yourManagedObject)把托管对象转成普通Java对象。非托管对象不依赖任何Realm实例,随便在哪用都没问题,但修改它不会影响数据库里的数据。
举个代码示例,假设你有个带主键的User模型:
public class User extends RealmObject { @PrimaryKey private String userId; private String name; // 省略getter/setter }
跨实例访问的实现:
// 第一个Realm实例创建并拿到托管对象 Realm realm1 = Realm.getDefaultInstance(); User managedUser = realm1.where(User.class).equalTo("userId", "u_123").findFirst(); realm1.close(); // 在另一个Realm实例里访问目标对象 Realm realm2 = Realm.getDefaultInstance(); // 方式1:主键查询获取新的托管对象 User crossInstanceUser = realm2.where(User.class).equalTo("userId", managedUser.getUserId()).findFirst(); // 方式2:转成非托管对象 User unmanagedUser = realm2.copyFromRealm(managedUser); realm2.close();
2. 解决“Realm数据库已关闭”的错误
先说说你当前思路的问题:
- Android系统里
Application.onTerminate()几乎不会被触发,只有在模拟器或者手动杀死进程时才可能调用,所以你的Realm实例大概率不会被正常关闭,反而容易引发内存泄漏。 - Realm实例是线程绑定的!同一个实例不能在多个线程里使用,你把它存在Application的静态变量里,在其他Activity(可能属于不同线程)调用时,不仅会报错,还会有线程安全隐患。
正确的解决方法:
别把托管对象存静态变量:托管对象依赖它的Realm实例,一旦实例关闭,对象就彻底失效了,这就是你碰到错误的核心原因。如果需要缓存数据,要么存对象的主键,要么把托管对象转成非托管对象(用
copyFromRealm)再存。每个组件独立管理Realm实例:在Activity/Fragment的
onCreate()里调用Realm.getDefaultInstance()获取实例,在onDestroy()里调用realm.close()关闭。这样每个组件的Realm实例都和自身生命周期绑定,线程安全,不会出现提前关闭的问题。
Activity的示例代码:
public class HomeActivity extends AppCompatActivity { private Realm realm; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home); // 获取当前组件的Realm实例 realm = Realm.getDefaultInstance(); // 在这里正常操作数据库 User user = realm.where(User.class).equalTo("userId", "u_123").findFirst(); } @Override protected void onDestroy() { super.onDestroy(); // 确保Realm实例被关闭 if (realm != null && !realm.isClosed()) { realm.close(); } } }
- 闪屏页预加载数据的正确姿势:闪屏页加载数据时,在自己的Realm实例里完成操作,然后把数据转成非托管对象再存到Application的静态变量里。比如:
// 闪屏页代码 public class SplashActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_splash); Realm splashRealm = Realm.getDefaultInstance(); // 预加载所有用户数据 List<User> managedUsers = splashRealm.where(User.class).findAll(); // 转成非托管对象后缓存到Application MyApp.getInstance().cachedUsers = splashRealm.copyFromRealm(managedUsers); // 关闭闪屏页的Realm实例 splashRealm.close(); // 跳转到主页面 startActivity(new Intent(this, HomeActivity.class)); finish(); } }
这样其他Activity访问cachedUsers时,拿到的是普通Java对象,完全不依赖Realm实例,自然不会出现“数据库已关闭”的错误。
内容的提问来源于stack exchange,提问作者Cruces
相关产品推荐
相关产品推荐

