使用Handler在独立线程处理Android Room时遇主线程数据库访问报错
问题分析与解决方案
嗨,问题出在你DbHandler的构造方法执行时机上!
你在AppConfig初始化时创建DbHandler,这个过程是在主线程完成的。而你在DbHandler的构造方法里直接调用了Room.databaseBuilder(...).build()并获取了Dao——Room在创建数据库实例和获取Dao的过程中,会触发一些底层的数据库初始化操作(比如检查版本、创建表结构等),这些操作默认是不允许在主线程执行的,所以直接抛出了那个IllegalStateException。
简单说:你以为数据库操作都在子线程,但其实连数据库的初始化步骤都跑到主线程去了,这才是报错的根源。
修复方案
把数据库的初始化和Dao的获取逻辑,移到DbHandler的handleMessage方法里(也就是子线程中执行),具体可以这么改:
修改DbHandler类
public class DbHandler extends Handler { private Context appContext; private String dbName; // 延迟初始化数据库和Dao private AppDatabase db; private CountOrderDao coDao; private ProductListDao plDao; private ProductDao pDao; DbHandler(Looper looper, Context context, String dbName) { super(looper); // 用ApplicationContext避免内存泄漏 this.appContext = context.getApplicationContext(); this.dbName = dbName; } @Override public void handleMessage(Message msg) { // 先检查数据库是否已初始化,未初始化则在子线程完成初始化 if (db == null) { db = Room.databaseBuilder(appContext, AppDatabase.class, dbName) .fallbackToDestructiveMigration() .build(); coDao = db.countOrderDao(); plDao = db.productListDao(); pDao = db.productDao(); } // 再处理具体的数据库操作 switch (msg.what) { case AppConfig.SAVE: // 在这里执行你的插入操作,比如 coDao.insert(xxx); break; // 其他操作类型的case } } }
额外的小建议
- 发送消息时建议用
AppConfig.serviceHandler.sendMessage(msg),而不是dispatchMessage(msg)——后者会直接同步调用handleMessage,前者才是将消息放到子线程的消息队列中异步执行,更符合HandlerThread的设计初衷。 - 一定要用
ApplicationContext来初始化Room,避免因为持有Activity的Context导致内存泄漏。
这样修改后,所有和数据库相关的操作(包括初始化)都会在你创建的dbHandlerThread子线程中执行,再也不会触发主线程访问的错误啦。
内容的提问来源于stack exchange,提问作者Yegie
相关产品推荐
相关产品推荐

