📜  Android 内部的 Room 是如何工作的?

📅  最后修改于: 2022-05-13 01:58:44.814000             🧑  作者: Mango

Android 内部的 Room 是如何工作的?

在创建 Room 数据库后第一次编译代码时,Room 会自动生成 @Database 和 @Dao 注释类的实现。前面示例中的 UserDatabase 和 UserDao 的实现是由 Room 注释处理器自动生成的。自动生成的代码可以在 build/generated/source/kapt/ 文件夹中找到。在我们的示例中,UserDatabase 实现称为 UserDatabase Impl,而 UserDao 实现称为 UserDao Impl。这些是进行实际处理的类。让我们分别看一下每个实现。

当你使用 Room 创建数据库实例时,会调用createOpenHelper ()。数据库生成器()。建造()。它创建并返回一个 SupportSQLiteOpenHelper 实例,这是一个用于管理数据库创建和版本管理的帮助类。

  1. createInvalidationTracker() 创建一个失效跟踪器,它保存由查询修改的表的列表,并在这些表被修改时通知其回调。
  2. clearAllTables() 是一个从指定数据库中的所有表中删除数据的函数。
  3. userDao() 创建(如果尚未存在)并返回一个 UserDao Impl 实例,用于与用户的表进行交互。
Kotlin
public final class GfgCoursesDatabase_Impl extends GfgCoursesDatabase {
    
  private volatile GfgCoursesDao _gfgCoursesDao;
  
  @Override
  protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
    // Implementation
  }
  
  @Override
  protected InvalidationTracker createInvalidationTracker() {
    // Implementation
  }
  
  @Override
  public void clearAllTables() {
    // Implementation
  }
  
  @Override
  public GfgCoursesDao gfgCoursesDao() {
    // Implementation
  }
}


Kotlin
public final class GfgCoursesDao_Impl implements GfgCoursesDao {
  private final RoomDatabase __db;
  
  private final EntityInsertionAdapter __insertionAdapterOfGfgCourses;
  
  private final EntityDeletionOrUpdateAdapter __deletionAdapterOfGfgCourses;
  
  public GfgCoursesDao_Impl(RoomDatabase __db) {
    this.__db = __db;
    this.__insertionAdapterOfGfgCourses = new EntityInsertionAdapter(__db) {
      // Implementation
    };
    this.__deletionAdapterOfGfgCourses = new EntityDeletionOrUpdateAdapter(__db) {
      // Implementation
    };
  }
  
  @Override
  public void insertAll(final GfgCourses... gfgCoursess) {
    // Implementation
  }
  
  @Override
  public void delete(final GfgCourses gfgCourses) {
    // Implementation
  }
  
  @Override
  public List getAll() {
    // Implementation
  }
  
  @Override
  public List loadAllByIds(final int[] gfgCoursesIds) {
    // Implementation
  }
  
  @Override
  public GfgCourses findByName(final String first, final String last) {
    // Implementation
  }
}


Kotlin
val coursesDatabase = Room.databaseBuilder(
    applicationContext,
    UserDatabase::class.java,
    "gfg_courses_db"
).build()


Kotlin
@Override
public void delete(final GfgCourses gfgCourses) {
  __db.assertNotSuspendingTransaction();
  __db.beginTransaction();
  try {
    __deletionAdapterOfGfgCourses.handle(gfgCourses);
    __db.setTransactionSuccessful();
  } finally {
    __db.endTransaction();
  }
}


Kotlin
@Override
public void insertAll(final GfgCourses... gfgCoursess) {
  __db.assertNotSuspendingTransaction();
  __db.beginTransaction();
  try {
    __insertionAdapterOfGfgCourses.insert(gfgCoursess);
    __db.setTransactionSuccessful();
  } finally {
    __db.endTransaction();
  }
}


Kotlin
@Override
public List getAll() {
  final String _sql = "SELECT * FROM gfgCoursess";
  final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
  __db.assertNotSuspendingTransaction();
  final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
  try {
    final int _cursorIndexOfUid = CursorUtil.getColumnIndexOrThrow(_cursor, "courseID");
    final int _cursorIndexOfCourseNameName = CursorUtil.getColumnIndexOrThrow(_cursor, "course_name");
    final int _cursorIndexOfCoursePriceName = CursorUtil.getColumnIndexOrThrow(_cursor, "course_price");
    final List _result = new ArrayList(_cursor.getCount());
    while(_cursor.moveToNext()) {
      final GfgCourses _item;
      final int _tmpUid;
      _tmpUid = _cursor.getInt(_cursorIndexOfUid);
      final String _tmpCourseNameName;
      _tmpCourseNameName = _cursor.getString(_cursorIndexOfCourseNameName);
      final String _tmpCoursePriceName;
      _tmpCoursePriceName = _cursor.getString(_cursorIndexOfCoursePriceName);
      _item = new GfgCourses(_tmpUid,_tmpCourseNameName,_tmpCoursePriceName);
      _result.add(_item);
    }
    return _result;
  } finally {
    _cursor.close();
    _statement.release();
  }
}


上例中的 CourseDao Impl 具有三个字段:

  1. __db是一个 RoomDatabase 实例,用于多种用途,包括数据库事务和查询。
  2. _ _insertionAdapterOfCourse是一个 EntityInsertionAdapter 实例,用于将实体插入到表中。 insertAll() 方法利用了这一点。
  3. __deletionAdapterOfCourse是一个 EntityDeletionOrUpdateAdapter 实例,用于从表中更新/删除实体。这是由 delete() 方法使用的。

科特林

public final class GfgCoursesDao_Impl implements GfgCoursesDao {
  private final RoomDatabase __db;
  
  private final EntityInsertionAdapter __insertionAdapterOfGfgCourses;
  
  private final EntityDeletionOrUpdateAdapter __deletionAdapterOfGfgCourses;
  
  public GfgCoursesDao_Impl(RoomDatabase __db) {
    this.__db = __db;
    this.__insertionAdapterOfGfgCourses = new EntityInsertionAdapter(__db) {
      // Implementation
    };
    this.__deletionAdapterOfGfgCourses = new EntityDeletionOrUpdateAdapter(__db) {
      // Implementation
    };
  }
  
  @Override
  public void insertAll(final GfgCourses... gfgCoursess) {
    // Implementation
  }
  
  @Override
  public void delete(final GfgCourses gfgCourses) {
    // Implementation
  }
  
  @Override
  public List getAll() {
    // Implementation
  }
  
  @Override
  public List loadAllByIds(final int[] gfgCoursesIds) {
    // Implementation
  }
  
  @Override
  public GfgCourses findByName(final String first, final String last) {
    // Implementation
  }
}

建立房间数据库

我们已经弄清楚了我们的项目成功编译后会发生什么。为了执行任何与数据库相关的操作,我们还知道我们需要一个 UserDatabase 的实例,它为我们提供了一个 UserDao 的实例。 Room 提供了一个名为 Room.databaseBuilder 的构建器方法,该方法返回 RoomDatabase.Builder 的实例,以获取 UserDatabase 的实例。通过在这个实例上调用 build() 方法,我们可以获得 UserDatabase。

科特林

val coursesDatabase = Room.databaseBuilder(
    applicationContext,
    UserDatabase::class.java,
    "gfg_courses_db"
).build()

我们可以使用此构建器以下列方式配置我们的数据库:

  1. 要从资产(位于应用程序'assets/' 文件夹中)或预打包的数据库文件创建和打开数据库,请使用 createFromAsset()/createFromFile()。
  2. 要将数据库从一个版本迁移到另一个版本,请使用 addMigrations()。即使两个版本的模式都没有变化,每当我们更改数据库的版本时,我们都必须执行迁移。
  3. allowMainThreadQueries () 允许从主线程进行数据库查询。 Room 默认不允许这样做。
  4. 如果没有找到迁移,Room 可以使用 fallbackToDestructiveMigration() 以破坏性方式重新创建数据库表。

RoomDatabase.Builder 还包括大量用于数据库配置的其他方法。

在创建 UserDatabase Impl 之后,会绕过数据库配置调用数据库的 init() 方法,然后调用 UserDatabase Impl 的 createOpenHelper() 方法。现在我们来看看UserDatabase Impl和UserDao Impl中的一些重要方法是如何实现的。

CoursesDao 中的 delete()

科特林

@Override
public void delete(final GfgCourses gfgCourses) {
  __db.assertNotSuspendingTransaction();
  __db.beginTransaction();
  try {
    __deletionAdapterOfGfgCourses.handle(gfgCourses);
    __db.setTransactionSuccessful();
  } finally {
    __db.endTransaction();
  }
}

CoursesDao 中的 insertAll()

科特林

@Override
public void insertAll(final GfgCourses... gfgCoursess) {
  __db.assertNotSuspendingTransaction();
  __db.beginTransaction();
  try {
    __insertionAdapterOfGfgCourses.insert(gfgCoursess);
    __db.setTransactionSuccessful();
  } finally {
    __db.endTransaction();
  }
}

CoursesDao 中的 getAll()

科特林

@Override
public List getAll() {
  final String _sql = "SELECT * FROM gfgCoursess";
  final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
  __db.assertNotSuspendingTransaction();
  final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
  try {
    final int _cursorIndexOfUid = CursorUtil.getColumnIndexOrThrow(_cursor, "courseID");
    final int _cursorIndexOfCourseNameName = CursorUtil.getColumnIndexOrThrow(_cursor, "course_name");
    final int _cursorIndexOfCoursePriceName = CursorUtil.getColumnIndexOrThrow(_cursor, "course_price");
    final List _result = new ArrayList(_cursor.getCount());
    while(_cursor.moveToNext()) {
      final GfgCourses _item;
      final int _tmpUid;
      _tmpUid = _cursor.getInt(_cursorIndexOfUid);
      final String _tmpCourseNameName;
      _tmpCourseNameName = _cursor.getString(_cursorIndexOfCourseNameName);
      final String _tmpCoursePriceName;
      _tmpCoursePriceName = _cursor.getString(_cursorIndexOfCoursePriceName);
      _item = new GfgCourses(_tmpUid,_tmpCourseNameName,_tmpCoursePriceName);
      _result.add(_item);
    }
    return _result;
  } finally {
    _cursor.close();
    _statement.release();
  }
}

结论

可以看出,它根据@Query 注解中指定的查询生成了一个 RoomSQLiteQuery 对象。然后它创建一个游标来从数据库中检索数据。这足以基本了解 Room 内部的运作方式。