首页 > 解决方案 > 如何通过 mvvm 架构使 Room 数据库插入方法返回 int?

问题描述

我想在 Room 数据库上执行插入操作时自动生成 id。我正在实现 MVVM (Model-View-ViewModel) 架构,它使用 DAO 来触发对 Room 数据库的查询。我在 viewmodel 和 DAO 之间添加了一个存储库层来创建一个 AsyncTask 来执行数据库操作。如何将插入操作的输出(即插入行的自动生成的 id)获取到使用视图模型的片段。层如下:Fragment -> ViewModel -> Repository -> DAO

ListFragment.java

public class ListFragment extends Fragment {
    private ReminderViewModel viewModel;
    private int id;
    ...
        viewModel = ViewModelProviders.of(this).get(ReminderViewModel.class);
    ...
        id = viewModel.insert(new TodoReminder(0, description, date, time));
    ...
}

ReminderViewModel.java

public class ReminderViewModel extends AndroidViewModel {
    private ReminderRepository repository;

    public ReminderViewModel(@NonNull Application application) {
        super(application);
        repository = new ReminderRepository(application);
    }

    public int insert(TodoReminder reminder) {
        repository.insert(reminder);
    }
}

ReminderRepository.java

public class ReminderRepository {
    private ReminderDAO reminderDAO;

    public ReminderRepository(Application application) {
        AppDatabase db = AppDatabase.getDatabase(application);
        reminderDAO = db.getReminderDAO();
    }

    public int insert(TodoReminder reminder) {
        new insertAsyncTask(reminderDAO).execute(reminder);
    }

    private static class InsertAsyncTask extends AsyncTask<TodoReminder, Void, Integer> {
        private ReminderDAO asyncTaskDAO;

        insertAsyncTask(ReminderDAO dao) {
            asyncTaskDAO = dao;
        }

        @Override
        protected Integer doInBackground(final TodoReminder... reminders) {
            return asyncTaskDAO.insert(reminders[0]);
        }
    }
}

ReminderDAO.java

@Dao
public interface ReminderDAO {
    @Insert
    public int insert(TodoReminder... reminders);
}

ToDoReminder.java

public class TodoReminder implements Serializable {
    @PrimaryKey(autoGenerate = true)
    @NonNull
    private int id;
    ...
}

我应该如何获取从 ReminderDAO 的 insert 方法返回的 int 并从 ReminderRepository 的 insert 方法返回?

标签: javaandroidandroid-asynctaskrepository-patternandroid-mvvm

解决方案


我有确切的问题。这就是我所做的。

我创建了一个界面

public interface Task
 {
    void processInsert(long id) 
}

在存储库中提供插入 fcn 的接口

public class Repository {

private Dao myDao;


    public void insertMyObject(MyOBject object,Task myInterface ) {

         new insertAysncTask(myDao,myInterface).execute(object);
      }

    private static class insertAsyncTask extends AysncTask<MyObject,Void,Long>{
        private Dao mDao;
        private Task mTask;

        public insertAysncTask(Dao dao, Task task) {

            this.mDao = dao;
            this.mTask=task;
        }


        @Override
        protected Long doInBackground(MyObject... myObjects) {
            return mDao.insertMyObject(myObjects[0]);

        }

        @Override
        protected void onPostExecute(Long aLong) {
            super.onPostExecute(aLong);
            mTask.processInsert(aLong);
        }
    }


}

在 DAO 类 fcn 中的返回类型应该是 Long

public interface MyDao {
    @Insert
    Long insertMyObject(MyObject object);

让 ViewModel 实现接口

 public class MyObjectViewModel extends AndroidViewModel implements Task{

    private Repository mRepository;

    public MyObjectViewModel(@NonNull Application application) {
        super(application);
        mRepository = new Repository(application);
    }

    @Override
    public void processInsert(Long id) {

      // code for processing the id returned from the db insert

    }

    public void insertMyObject(MyObject object){

        mRepository.insertMyObject(object,this);}


}

在活动/片段中调用 ViewModel 插入

mViewModel.insertMyObject(object);

更新

如果您想将 id 返回给活动或片段,则让片段/活动实现接口而不是视图模型

ListFragment extends Fragment implements Task{
.....

@Override
public void processInsert(Long id){
   //process id here
  }

//call the insert fcn passing in the reference to the fragment

mViewModel.insertMyObject(object,this)
}

修改 View Model 中的 insert fcn 以接受对接口的引用

public void insertMyObject(MyObject object, Task myInterface){

        mRepository.insertMyObject(object,myInterface);
}

使用这种方法,存储库 insert fcn 中的异步任务将保存对活动/片段的引用,如果活动/片段在异步任务完成之前被销毁,这将是一个问题。我认为如果可能的话,最好在视图模型中进行处理。片段/活动应该只处理 UI 问题而不是数据处理。

替代方法 另一种方法是将 LiveData 与观察者一起使用。

public class Repository{

private Dao myDao;
private MutableLiveData<Long> dbInsertId = new MutableLiveData<>();

public void insertMyObject(MyObject object){

    insertAysnc(object)
  }

private void insertAysnc(final MyObject object){

   new Thread(new Runnable() {
            @Override
            public void run() {
                Long id=myDao.insertMyObject(object); //call Dao insert fcn
              dbInsertId.postValue(id); //set the value of the livedata to the valued returned from the DB insert fcn
            }
        }).start();
  }

public LiveData<Long> getDbInsertedId(){
        return dbInsertId;
    }

}

在 ViewModel 中定义这个 fcn

public LiveData<Long> getDbInsertedId(){
        return mRepository.getDbInsertedId();// call the repository getId fcn
    }

在 Activity/Fragment 的 onCreate 中设置 LiveData 上的观察者

mViewModel= ViewModelProviders.of(this).get(MyViewModel.class);
mViewModel.getDbInsertedId().observe(this, new Observer<Long>() {
            @Override
            public void onChanged(Long aLong) {
               // do something with the long value returned from the database
            }
        });


推荐阅读