如何在Room DB+MVVM架构下用Executors获取插入数据ID
Room+MVVM架构下获取插入数据自增ID及异步查询实现方案
一、修改StudentDao,支持返回自增ID与单条查询
首先调整Dao层的插入方法,使其返回自动生成的ID,并添加根据ID查询学生的方法:
@Dao public interface StudentDao { @Insert long insert(Student student); // 修改为返回long类型的自增ID @Update void update(Student student); @Delete void delete(Student student); @Query("DELETE FROM students_table") void deleteAll(); @Query("SELECT * FROM students_table ORDER BY name, father_name COLLATE NOCASE") LiveData<List<Student>> getAll(); // 添加根据ID查询单条学生数据的方法 @Query("SELECT * FROM students_table WHERE id = :studentId") Student getStudentById(long studentId); }
二、在Repository中实现异步回调机制
由于Executors.execute()是异步执行的,无法直接返回结果,因此需要通过回调接口来传递异步操作的结果:
- 在
StudentsRepo中定义回调接口并修改相关方法:
package ask2784.studentsdetails; import android.app.Application; import android.os.Handler; import android.os.Looper; import androidx.lifecycle.LiveData; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.Executors; public class StudentsRepo { private StudentDao studentDao; private LiveData<List<Student>> allStudents; private Executor service = Executors.newSingleThreadExecutor(); // 插入结果回调接口 public interface InsertCallback { void onInsertSuccess(long insertedId); void onInsertFailed(Exception e); } // 单条学生查询回调接口 public interface GetStudentCallback { void onStudentLoaded(Student student); void onLoadFailed(Exception e); } public StudentsRepo(Application application) { StudentsDatabase db = StudentsDatabase.getInstance(application.getApplicationContext()); studentDao = db.studentDao(); allStudents = studentDao.getAll(); } // 修改插入方法,通过回调返回自增ID public void insert(Student student, InsertCallback callback) { service.execute(() -> { try { long insertedId = studentDao.insert(student); // 切换回主线程回调结果(确保UI操作安全) new Handler(Looper.getMainLooper()).post(() -> callback.onInsertSuccess(insertedId)); } catch (Exception e) { new Handler(Looper.getMainLooper()).post(() -> callback.onInsertFailed(e)); } }); } // 实现根据ID查询学生的异步方法 public void getStudentById(long studentId, GetStudentCallback callback) { service.execute(() -> { try { Student student = studentDao.getStudentById(studentId); new Handler(Looper.getMainLooper()).post(() -> callback.onStudentLoaded(student)); } catch (Exception e) { new Handler(Looper.getMainLooper()).post(() -> callback.onLoadFailed(e)); } }); } // 保留原有其他方法 public void update(Student student) { service.execute(() -> studentDao.update(student)); } public void delete(Student student) { service.execute(() -> studentDao.delete(student)); } public void deleteAllStudents() { service.execute(() -> studentDao.deleteAll()); } public LiveData<List<Student>> getAllStudents() { return allStudents; } }
三、更新ViewModel层,传递回调接口
调整StudentViewModel,让其暴露带回调的插入和查询方法:
import android.app.Application; import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.LiveData; import java.util.List; public class StudentViewModel extends AndroidViewModel { private StudentsRepo repo; private LiveData<List<Student>> allStudents; public StudentViewModel(Application application) { super(application); repo = new StudentsRepo(application); allStudents = repo.getAllStudents(); } // 带回调的插入方法 public void insert(Student student, StudentsRepo.InsertCallback callback) { repo.insert(student, callback); } // 带回调的单条查询方法 public void getStudentById(long studentId, StudentsRepo.GetStudentCallback callback) { repo.getStudentById(studentId, callback); } // 保留原有其他方法 public void update(Student student) { repo.update(student); } public void delete(Student student) { repo.delete(student); } public void deleteAllStudents() { repo.deleteAllStudents(); } public LiveData<List<Student>> getAllStudents() { return allStudents; } }
四、在UI层(Activity/Fragment)中使用
在界面代码中调用ViewModel的方法,通过回调获取结果:
// 示例:插入新学生并获取自增ID Student newStudent = new Student("张三", "张建国", "三年级二班"); studentViewModel.insert(newStudent, new StudentsRepo.InsertCallback() { @Override public void onInsertSuccess(long insertedId) { // 成功获取自增ID,执行后续操作 Toast.makeText(MainActivity.this, "插入成功,学生ID:" + insertedId, Toast.LENGTH_SHORT).show(); // 可选:用刚获取的ID查询该学生 studentViewModel.getStudentById(insertedId, new StudentsRepo.GetStudentCallback() { @Override public void onStudentLoaded(Student student) { // 处理查询到的学生数据 } @Override public void onLoadFailed(Exception e) { // 处理查询失败逻辑 Toast.makeText(MainActivity.this, "查询失败:" + e.getMessage(), Toast.LENGTH_SHORT).show(); } }); } @Override public void onInsertFailed(Exception e) { // 处理插入失败逻辑 Toast.makeText(MainActivity.this, "插入失败:" + e.getMessage(), Toast.LENGTH_SHORT).show(); } });
关键说明
- 不能直接从
Executors.execute()中返回值的原因:该方法是在子线程异步执行的,主线程调用方法时,子线程的数据库操作尚未完成,直接返回会得到无效结果。 - 使用
Handler(Looper.getMainLooper())切换回主线程:确保回调中的UI操作(如Toast、更新界面)在主线程执行,避免线程异常。
内容的提问来源于stack exchange,提问作者Arvind Kaushik
相关产品推荐
相关产品推荐

