【问题标题】:Android Room database @Insert method returns row id starting at 0Android Room 数据库 @Insert 方法返回从 0 开始的行 id
【发布时间】:2020-11-06 21:31:46
【问题描述】:

我已经在我的应用中实现了 MVVM 架构。我有我的活动、视图模型、存储库、DAO 和数据库类。数据库包含包含不同列表的对象,我想在这些列表之间切换,并让我的 RecyclerView 显示当前选择的列表。我的理解是 SQLite 中的行 ID 从 1 开始,但是我的存储库中的 insert 方法(它在我的 DAO 中调用 @Insert 方法)总是以行 ID 0 开始。出于测试目的,我使用 LiveData 来获取数据库中的所有对象,当我记录它们的 ID 时,它们正确地从 1 开始,一直到 n。我不想在内存中维护数据库中所有对象的列表,只维护包含当前选定列表的单个对象。 当用户创建新列表(或选择现有列表)时,我想让我的 RecyclerView 显示其内容并观察该列表的任何更改。如果没有正确的对应 ID,我无法开始观察对象。

如何将正确的 ID 传播到 MainActivity?如果需要我的 ViewModel 和 Activity 代码,请告诉我,我会编辑帖子。

当我从存储库中记录 ID 时

当我在 MainActivity 中记录 LiveData 时,会显示正确的 ID

我的数据库类

@TypeConverters({ArrayListConverter.class}) // List converter for database
@Database(entities = ListContainer.class, version = 1, exportSchema = false)
public abstract class ListContainerDatabase extends RoomDatabase {

  private static ListContainerDatabase dbInstance;
  private static final String DB_NAME = "list_container_db";
  private static final int NUM_THREADS = 4;

  final static ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);

  public abstract ListContainerDao getDao();

  public static synchronized ListContainerDatabase getInstance(Context context) {
    if(dbInstance == null) {
      dbInstance = Room.databaseBuilder(
              context.getApplicationContext(),
              ListContainerDatabase.class, DB_NAME)
              .fallbackToDestructiveMigration()
              .build();
    }
    return dbInstance;
  }

  public static Executor getExecutor() {
    return executor;
}

我的 DAO

@Dao
public interface ListContainerDao {

  @Insert
  long insertListContainer(ListContainer container);

... // other database queries

}

我的仓库

public class Repository {

  private static final String TAG = "Repository";

  private ListContainerDao listContainerDao;
  private long id;

  private Executor executor = ListContainerDatabase.getExecutor();

  public Repository(Application application) {
    ListContainerDatabase containerDb = ListContainerDatabase.getInstance(application);
    listContainerDao = containerDb.getDao();
  }

  public long insertListContainer(ListContainer container) {
    executor.execute(new Runnable() {
      @Override
      public void run() {
        id = listContainerDao.insertListContainer(container);  // this returns ID starting at 0
      }
    });
    Log.i(TAG, "insertListContainer: from Repository id is :" + id);
    return id;
  }
}

【问题讨论】:

    标签: java android sqlite mvvm android-room


    【解决方案1】:

    您应该在这里考虑代码的异步性:

    private long id;
    
    public long insertListContainer(ListContainer container) {
        // Timestamp #1. This started, first time id is not initialised, equals to 0
        executor.execute(new Runnable() { 
        @Override
          public void run() {
            id = listContainerDao.insertListContainer(container);  
            // Timestamp #3. This returns you id = 1, but with some delay, so this value changed id and will be returned only next time you call the method 
          }
        });
        // Timestamp #2. Main thread isn't blocked, so it doesn't wait for runnable to be executed as well and returns first time id = 0, next time - changed value - 1, and so on
        Log.i(TAG, "insertListContainer: from Repository id is :" + id); 
        return id;
      }
    

    如果你想在 Java 中获得异步操作的结果,你可以使用:

    【讨论】:

    • 链接中的回调方法很清楚。但是,在该示例中,类 A 直接实例化类 B,将自身作为侦听器传递给 B 的构造函数。在我的例子中,我的 MainActivity 实例化 ViewModel,而 ViewModel 实例化 Repository(这是回调所在的位置)。我的问题是,我是否应该将 MainActivity 作为监听器传递给 ViewModel,并且当 ViewModel 实例化 Repository 时将其再次传递给 Repository 的构造函数?这有可能导致内存泄漏吗?
    • "这有可能导致内存泄漏吗?" - 是的,它确实。 “我应该将 MainActivity 作为 ViewModel 的侦听器传递” - 不,你不应该,ViewModel 不应该持有对活动的引用,MVVM 中有一种推荐的通信方式 - LiveData。 “当 ViewModel 实例化 Repository 时,将其再次传递给 Repository 的构造函数” - 类似的东西,但使用 ViewModel 作为侦听器。老实说,我不能说我是回调方法的忠实拥护者,但在 Java 中,如果您不想处理 RxJava,它仍然是一种选择。我更喜欢 Kotlin 中的协程)
    • 感谢您的建议。我将探索 RxJava 作为一个选项。正如我在原始帖子中提到的,我将 LiveData 与其他方法一起使用。您认为将我的 ----- public long insertListContainer(param) 转换为 -------- public LiveData insertListContainer(param) 会解决我的问题吗?很抱歉这些菜鸟问题,但我对使用 LiveData 很陌生。
    • 我的意思是提到 LiveData - LiveData(或其他观察者模式)是当您想要将数据从 ViewModel 获取到 Activity(而不是在 ViewModel 中直接引用 Activity)时推荐的方式。所以使用回调策略——ViewModel通过回调来监听Repository,获取值后更新LiveData的值,Activity对其进行观察。
    • 我最终使用了 Java 内置的 Future 类。 get() 方法停止代码执行并等待结果,这正是我想要的。您的回答促使我进行研究并最终找到解决方案。非常感谢。
    猜你喜欢
    • 2019-05-01
    • 2020-01-22
    • 2021-11-24
    • 1970-01-01
    • 1970-01-01
    • 2018-12-14
    • 2019-06-07
    • 1970-01-01
    • 2023-03-04
    相关资源
    最近更新 更多