Android房间。哪种插入方法会更快?

Android Room. Which method of insertion will be faster?

我使用 Room 作为 SQLite 的抽象层。看完这个 page 我发现我们可以同时插入多个对象。目前我使用 For 循环来插入对象,即在每个 For 循环迭代中插入一个对象。我目前知道的两种插入方式是:

  1. 使用 For 循环并一次插入一个对象

    @Insert(onConflict = OnConflictStrategy.REPLACE) public void addActivity(Person person);

  2. 正在插入数组或对象列表。

    @Insert(onConflict = OnConflictStrategy.REPLACE) public void insertUsers(Person ... people);

我在写对象插入的时候,并不知道第二种插入方式。现在我想知道这两种方式之间的速度是否有明显差异,以便我可以更改我的代码以提高我的应用程序的性能。

幕后生成的 类 房间正在使用 EntityInsertionAdapter 来应对这种特殊情况。还有两个方法,我们需要检查:

  1. 这个用于插入单个实体

     public final long insertAndReturnId(T entity) {
        final SupportSQLiteStatement stmt = acquire();
        try {
            bind(stmt, entity);
            return stmt.executeInsert();
        } finally {
            release(stmt);
        }
    }
    
  2. 而这个是用来插入实体数组的

    public final void insert(Iterable<T> entities) {
        final SupportSQLiteStatement stmt = acquire();
        try {
            for (T entity : entities) {
                bind(stmt, entity);
                stmt.executeInsert();
            }
        } finally {
            release(stmt);
        }
    }
    

如您所见,内部结构与您的几乎相同 - stmt.executeInsert(); 被调用一次或在循环中调用。我能想到的使用 insertUsers 方法的唯一性能变化是更改通知,当所有用户都被插入时,它只会发生一次。但是,如果您已经在用 @Transaction 包裹的循环中插入,那么就不会有任何变化。

按照 OP 在对他们问题的评论中的要求,这里是(为了清楚起见,作为答案)我为检查性能所做的工作:

之前,一个一个插入对象:

@Dao
abstract class MyDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    abstract fun insert(items: MyObject): Long
    // ...
}

正在同步代码:

val response = backend.downloadItems() // download from server
val items = response.getData() // this is a List<MyObject>
if (items != null) {
    for (i in items) {
        myDao.persist(s)
    }
}

这在华为 P10+ 上花了一分钟。

我将其更改为:

@Dao
abstract class MyDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    abstract fun insert(items: Iterable<MyObject>)
    // ...
}

正在同步代码:

val response = backend.downloadItems() // download from server
val items = response.getData() // this is a List<MyObject>
response.getData()?.let { myDao.insert(it) }

这用了不到一秒钟。

这里的重点是专门使用 Iterable<> 版本的 DAO @Insert 方法,正如@iDemigod 所说,使用 Iterable<> 版本的 EntityInsertionAdapter.

所述函数的主体在@iDemigod 的回答中,它使用一个准备好的语句进行所有插入。

将 SQL 解析成一个语句是昂贵的,并且使用一个语句为整个插入批处理创建一个事务,这可以帮助解决其他问题(我在数据库上有一个可观察的 LiveData<> ,在插入期间被通知了 12k 次...性能是 糟糕)。