Android Room库报错:主线程无法访问数据库,求Java解决方案
解决Android Room主线程访问数据库的Java方案
问题
使用Android Room实现SQLite本地数据库,编译阶段创建数据库,运行项目时触发主线程访问数据库的异常,目标是创建空数据库后通过GUI填充数据,需要Java环境下的解决方案。
错误日志
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.dblearning.dbroom_test/com.dblearning.dbroom_test.RecyclerActivity}: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time. at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3698) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3855) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:105) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:136) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:96) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2267) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loopOnce(Looper.java:201) at android.os.Looper.loop(Looper.java:288) at android.app.ActivityThread.main(ActivityThread.java:7964) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:553) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003) Caused by: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time. at androidx.room.RoomDatabase.assertNotMainThread(RoomDatabase.kt:439) at androidx.room.RoomDatabase.query(RoomDatabase.kt:479) at androidx.room.util.DBUtil.query(DBUtil.kt:75) at com.dblearning.dbroom_test.DAO_Impl.getAll(DAO_Impl.java:165) at com.dblearning.dbroom_test.RecyclerActivity.onCreate(RecyclerActivity.java:29) at android.app.Activity.performCreate(Activity.java:8069) at android.app.Activity.performCreate(Activity.java:8049) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1329) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3668)
相关代码
RecyclerActivity代码
public class RecyclerActivity extends AppCompatActivity { private Button add,del,update; private EditText dataField; List<DataEntity> dataEntityList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_recycler); RoomDB db = Room.databaseBuilder(this, RoomDB.class, "tab").build(); DAO dao = db.Dao(); dataEntityList =dao.getAll(); RecyclerView recyclerView = findViewById(R.id.recyclerView); Adapter entityAdapter = new Adapter(dataEntityList); recyclerView.setLayoutManager(new GridLayoutManager(this, 3)); recyclerView.setAdapter(entityAdapter); ... } }
DAO接口代码
@Dao public interface DAO { @Query("SELECT * FROM tab") List<DataEntity> getAll(); @Query("SELECT * FROM tab WHERE id IN (:userIds)") List<DataEntity> loadAllByIds(int[] userIds); @Query("DELETE FROM tab") void deleteAll(); @Query("SELECT * FROM tab WHERE first_item LIKE :first AND " + "second_item LIKE :last LIMIT 1") DataEntity findByName(String first, String last); @Update void update(DataEntity entity); @Insert void insertAll(DataEntity... Entities); @Delete void delete(DataEntity Entity); }
DataEntity实体类代码
@Entity(tableName = "tab" ) public class DataEntity { @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") public int uid; public int getUid() { return uid; } public void setUid(int uid) { this.uid = uid; } @ColumnInfo(name = "first_item") public String firstItem; public String getFirstItem() { return firstItem; } public void setFirstItem(String firstItem) { this.firstItem = firstItem; } @ColumnInfo(name = "second_item") public String secondItem; public String getSecondItem() { return secondItem; } public void setSecondItem(String secondItem) { this.secondItem = secondItem; } public DataEntity(){ } }
解决方案
Room默认禁止主线程访问数据库,避免UI阻塞,以下是三种Java环境下的解决方式:
1. 使用AsyncTask执行后台查询(传统Java方案)
修改RecyclerActivity,将数据库查询放在AsyncTask中执行,查询完成后更新RecyclerView的Adapter:
public class RecyclerActivity extends AppCompatActivity { private Button add,del,update; private EditText dataField; List<DataEntity> dataEntityList; private Adapter entityAdapter; private RecyclerView recyclerView; private DAO dao; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_recycler); RoomDB db = Room.databaseBuilder(this, RoomDB.class, "tab").build(); dao = db.Dao(); recyclerView = findViewById(R.id.recyclerView); recyclerView.setLayoutManager(new GridLayoutManager(this, 3)); // 启动AsyncTask执行查询 new LoadDataTask().execute(); ... } private class LoadDataTask extends AsyncTask<Void, Void, List<DataEntity>> { @Override protected List<DataEntity> doInBackground(Void... voids) { // 后台线程执行数据库查询 return dao.getAll(); } @Override protected void onPostExecute(List<DataEntity> result) { super.onPostExecute(result); dataEntityList = result; entityAdapter = new Adapter(dataEntityList); recyclerView.setAdapter(entityAdapter); } } }
2. 改造DAO返回LiveData(推荐,生命周期感知)
这种方式更符合Android架构组件的设计,Room会自动在后台线程执行查询,并在数据变化时通知UI:
- 首先修改DAO接口,将
getAll()方法返回LiveData<List<DataEntity>>:
@Dao public interface DAO { @Query("SELECT * FROM tab") LiveData<List<DataEntity>> getAll(); // 修改返回类型 // 其他方法保持不变 @Query("SELECT * FROM tab WHERE id IN (:userIds)") List<DataEntity> loadAllByIds(int[] userIds); @Query("DELETE FROM tab") void deleteAll(); @Query("SELECT * FROM tab WHERE first_item LIKE :first AND " + "second_item LIKE :last LIMIT 1") DataEntity findByName(String first, String last); @Update void update(DataEntity entity); @Insert void insertAll(DataEntity... Entities); @Delete void delete(DataEntity Entity); }
- 然后修改
RecyclerActivity,观察LiveData的变化:
public class RecyclerActivity extends AppCompatActivity { private Button add,del,update; private EditText dataField; private Adapter entityAdapter; private RecyclerView recyclerView; private DAO dao; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_recycler); RoomDB db = Room.databaseBuilder(this, RoomDB.class, "tab").build(); dao = db.Dao(); recyclerView = findViewById(R.id.recyclerView); recyclerView.setLayoutManager(new GridLayoutManager(this, 3)); // 观察LiveData,数据变化时更新Adapter dao.getAll().observe(this, new Observer<List<DataEntity>>() { @Override public void onChanged(List<DataEntity> dataEntities) { entityAdapter = new Adapter(dataEntities); recyclerView.setAdapter(entityAdapter); } }); ... } }
3. 临时允许主线程访问(仅调试用,不推荐上线)
如果只是临时调试,可以在构建数据库时添加allowMainThreadQueries()方法,但这种方式会导致UI卡顿,上线版本绝对不能用:
RoomDB db = Room.databaseBuilder(this, RoomDB.class, "tab") .allowMainThreadQueries() // 添加此行 .build();
内容的提问来源于stack exchange,提问作者Drained
相关产品推荐
相关产品推荐

