01-10-28 Room数据库框架:Android官方SQLite封装方案

01-10-28 Room数据库框架:Android官方SQLite封装方案

摘要

Room是Google官方推出的Android SQLite数据库封装库,通过编译时注解处理生成数据库访问代码,提供了类型安全、易于测试和维护的数据库操作API。本文深入剖析Room的核心实现原理,包括数据库架构设计、DAO代码生成机制、Migration迁移策略、LiveData集成以及多线程处理,揭示其如何简化Android数据库开发。

关键词:Room、SQLite、数据库、ORM、编译时注解、DAO、Migration、Android Jetpack


一、Room架构概览

1.1 核心架构设计

应用层

@Database注解类

@Dao接口

@Entity数据类

RoomProcessor

生成实现类

DatabaseImpl

DaoImpl

EntityCursorAdapter

SQLiteOpenHelper

SQLiteStatement

SQLite数据库

1.2 三大核心组件

@Database

数据库配置

@Entity

表结构定义

@Dao

数据访问接口

版本管理

DAO引用

Entity列表

字段映射

主键定义

索引配置

查询方法

插入方法

更新/删除方法


二、核心注解设计

2.1 @Database注解

// room-common/src/main/java/androidx/room/Database.java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Database {
    /**
     * 数据库中的Entity列表
     */
    Class<?>[] entities();

    /**
     * 数据库中的View列表
     */
    Class<?>[] views() default {};

    /**
     * 数据库版本号
     */
    int version();

    /**
     * 是否导出schema文件
     */
    boolean exportSchema() default true;
}

// 使用示例
@Database(
    entities = {User.class, Order.class},
    version = 1,
    exportSchema = false
)
public abstract class AppDatabase extends RoomDatabase {

    public abstract UserDao userDao();
    public abstract OrderDao orderDao();

    private static volatile AppDatabase INSTANCE;

    public static AppDatabase getInstance(Context context) {
        if (INSTANCE == null) {
            synchronized (AppDatabase.class) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(
                        context.getApplicationContext(),
                        AppDatabase.class,
                        "app_database"
                    ).build();
                }
            }
        }
        return INSTANCE;
    }
}

2.2 @Entity注解

// room-common/src/main/java/androidx/room/Entity.java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Entity {
    /**
     * 表名,默认使用类名
     */
    String tableName() default "";

    /**
     * 索引定义
     */
    Index[] indices() default {};

    /**
     * 是否继承父类字段
     */
    boolean inheritSuperIndices() default false;

    /**
     * 主键定义
     */
    String[] primaryKeys() default {};

    /**
     * 外键定义
     */
    ForeignKey[] foreignKeys() default {};
}

// 使用示例
@Entity(
    tableName = "users",
    indices = {
        @Index(value = {"email"}, unique = true),
        @Index(value = {"name", "age"})
    }
)
public class User {

    @PrimaryKey(autoGenerate = true)
    private long id;

    @ColumnInfo(name = "user_name")
    private String name;

    @ColumnInfo(name = "user_age")
    private int age;

    private String email;

    @Ignore
    private String tempData;

    // Getters and setters
}

2.3 @Dao注解

// room-common/src/main/java/androidx/room/Dao.java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Dao {
}

// 使用示例
@Dao
public interface UserDao {

    @Query("SELECT * FROM users")
    List<User> getAllUsers();

    @Query("SELECT * FROM users WHERE id = :userId")
    User getUserById(long userId);

    @Query("SELECT * FROM users WHERE name LIKE :searchQuery")
    LiveData<List<User>> searchUsers(String searchQuery);

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    long insertUser(User user);

    @Insert
    void insertUsers(List<User> users);

    @Update
    int updateUser(User user);

    @Delete
    void deleteUser(User user);

    @Query("DELETE FROM users WHERE id = :userId")
    void deleteUserById(long userId);

    @Transaction
    @Query("SELECT * FROM users WHERE id = :userId")
    UserWithOrders getUserWithOrders(long userId);
}

三、编译时注解处理

3.1 RoomProcessor核心逻辑

// room-compiler/src/main/kotlin/androidx/room/RoomProcessor.kt
@AutoService(Processor::class)
class RoomProcessor : BasicAnnotationProcessor() {

    override fun steps(): Iterable<Step> {
        return listOf(
            DatabaseProcessingStep(),
            DaoProcessingStep(),
            EntityProcessingStep()
        )
    }

    private inner class DatabaseProcessingStep : Step {
        override fun annotations(): Set<String> {
            return setOf(Database::class.java.canonicalName)
        }

        override fun process(
            elementsByAnnotation: SetMultimap<String, Element>
        ): Set<Element> {
            val databases = elementsByAnnotation[Database::class.java.canonicalName]

            databases.forEach { element ->
                val typeElement = element as TypeElement
                val database = DatabaseProcessor(
                    processingEnv,
                    typeElement
                ).process()

                // 生成DatabaseImpl类
                DatabaseWriter(database).write(processingEnv)
            }

            return emptySet()
        }
    }
}

3.2 Database代码生成

// room-compiler/src/main/kotlin/androidx/room/writer/DatabaseWriter.kt
class DatabaseWriter(val database: Database) : ClassWriter(database.typeName) {

    override fun createTypeSpecBuilder(): TypeSpec.Builder {
        val builder = TypeSpec.classBuilder(database.implTypeName)
            .addModifiers(PUBLIC)
            .superclass(database.typeName)

        // 生成DAO实例字段
        database.daoMethods.forEach { dao ->
            builder.addField(
                FieldSpec.builder(dao.dao.typeName, dao.dao.implTypeName.simpleName())
                    .addModifiers(PRIVATE, VOLATILE)
                    .build()
            )
        }

        // 生成createOpenHelper方法
        builder.addMethod(createOpenHelperMethod())

        // 生成createInvalidationTracker方法
        builder.addMethod(createInvalidationTrackerMethod())

        // 生成clearAllTables方法
        builder.addMethod(createClearAllTablesMethod())

        // 生成DAO getter方法
        database.daoMethods.forEach { dao ->
            builder.addMethod(createDaoMethod(dao))
        }

        return builder
    }

    private fun createOpenHelperMethod(): MethodSpec {
        val implVar = "_helper"
        return MethodSpec.methodBuilder("createOpenHelper")
            .addAnnotation(Override::class.java)
            .addModifiers(PROTECTED)
            .returns(SupportSQLiteOpenHelper::class.java)
            .addParameter(DatabaseConfiguration::class.java, "configuration")
            .apply {
                addStatement(
                    "\$T \$L = new \$T(\$N, new \$T(\$N, \$L, \$L) { ... })",
                    SupportSQLiteOpenHelper::class.java, implVar,
                    FrameworkSQLiteOpenHelperFactory::class.java, "configuration",
                    SupportSQLiteOpenHelper.Callback::class.java, "configuration",
                    database.version, database.version
                )

                addStatement("return \$L", implVar)
            }
            .build()
    }

    private fun createDaoMethod(dao: DaoMethod): MethodSpec {
        val daoFieldName = dao.dao.implTypeName.simpleName()
        return MethodSpec.methodBuilder(dao.name)
            .addAnnotation(Override::class.java)
            .addModifiers(PUBLIC)
            .returns(dao.dao.typeName)
            .apply {
                beginControlFlow("if (\$L != null)", daoFieldName)
                addStatement("return \$L", daoFieldName)
                nextControlFlow("else")
                beginControlFlow("synchronized(this)")
                beginControlFlow("if(\$L == null)", daoFieldName)
                addStatement("\$L = new \$T(this)", daoFieldName, dao.dao.implTypeName)
                endControlFlow()
                addStatement("return \$L", daoFieldName)
                endControlFlow()
                endControlFlow()
            }
            .build()
    }
}

3.3 DAO代码生成

// room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
class DaoWriter(val dao: Dao, val dbParam: DatabaseWriter.DaoParamField) : ClassWriter(dao.typeName) {

    override fun createTypeSpecBuilder(): TypeSpec.Builder {
        val builder = TypeSpec.classBuilder(dao.implTypeName)
            .addModifiers(PUBLIC, FINAL)
            .addSuperinterface(dao.typeName)

        // 添加数据库引用字段
        builder.addField(
            FieldSpec.builder(dbParam.type, dbParam.name, PRIVATE, FINAL).build()
        )

        // 添加构造器
        builder.addMethod(createConstructor())

        // 生成所有方法实现
        dao.queryMethods.forEach { method ->
            builder.addMethod(createQueryMethod(method))
        }

        dao.insertionMethods.forEach { method ->
            builder.addMethod(createInsertMethod(method))
        }

        dao.updateMethods.forEach { method ->
            builder.addMethod(createUpdateMethod(method))
        }

        dao.deletionMethods.forEach { method ->
            builder.addMethod(createDeleteMethod(method))
        }

        return builder
    }

    private fun createQueryMethod(method: QueryMethod): MethodSpec {
        val returnType = method.returnType
        val methodBuilder = MethodSpec.methodBuilder(method.name)
            .addAnnotation(Override::class.java)
            .addModifiers(PUBLIC)
            .returns(returnType.typeName)

        // 添加参数
        method.parameters.forEach { param ->
            methodBuilder.addParameter(param.typeName, param.name)
        }

        // 生成SQL查询代码
        val queryVar = scope.getTmpVar("_sql")
        methodBuilder.addStatement(
            "final \$T \$L = \$S",
            String::class.java, queryVar, method.query.original
        )

        // 生成RoomSQLiteQuery
        val roomSqlQueryVar = scope.getTmpVar("_statement")
        methodBuilder.addStatement(
            "final \$T \$L = \$T.acquire(\$L, \$L)",
            RoomSQLiteQuery::class.java, roomSqlQueryVar,
            RoomSQLiteQuery::class.java, queryVar,
            method.parameters.size
        )

        // 绑定参数
        method.parameters.forEachIndexed { index, param ->
            bindQueryParameter(methodBuilder, param, index + 1, roomSqlQueryVar)
        }

        // 执行查询并处理结果
        if (returnType is LiveDataType) {
            // LiveData返回类型
            createLiveDataQuery(methodBuilder, method, roomSqlQueryVar)
        } else {
            // 普通返回类型
            createNormalQuery(methodBuilder, method, roomSqlQueryVar)
        }

        return methodBuilder.build()
    }

    private fun createInsertMethod(method: InsertionMethod): MethodSpec {
        val methodBuilder = MethodSpec.methodBuilder(method.name)
            .addAnnotation(Override::class.java)
            .addModifiers(PUBLIC)
            .returns(method.returnType.typeName)

        method.parameters.forEach { param ->
            methodBuilder.addParameter(param.typeName, param.name)
        }

        // 生成插入代码
        methodBuilder.addStatement("\$L.assertNotSuspendingTransaction()", dbParam.name)
        methodBuilder.addStatement("\$L.beginTransaction()", dbParam.name)

        methodBuilder.beginControlFlow("try")

        val insertStmtVar = scope.getTmpVar("_stmt")
        methodBuilder.addStatement(
            "\$T \$L = \$L.getInsertionStatement()",
            SupportSQLiteStatement::class.java, insertStmtVar, method.entity.insertionAdapterField
        )

        // 绑定参数并执行
        method.parameters.forEach { param ->
            bindInsertParameter(methodBuilder, param, insertStmtVar)
            methodBuilder.addStatement("\$L.executeInsert()", insertStmtVar)
        }

        methodBuilder.addStatement("\$L.setTransactionSuccessful()", dbParam.name)

        if (method.returnType.typeName != TypeName.VOID) {
            methodBuilder.addStatement("return \$L", scope.getTmpVar("_result"))
        }

        methodBuilder.nextControlFlow("finally")
        methodBuilder.addStatement("\$L.endTransaction()", dbParam.name)
        methodBuilder.endControlFlow()

        return methodBuilder.build()
    }
}

四、数据库迁移机制

4.1 Migration接口

// room-runtime/src/main/java/androidx/room/migration/Migration.java
public abstract class Migration {

    public final int startVersion;
    public final int endVersion;

    public Migration(int startVersion, int endVersion) {
        this.startVersion = startVersion;
        this.endVersion = endVersion;
    }

    /**
     * 执行迁移逻辑
     */
    public abstract void migrate(@NonNull SupportSQLiteDatabase database);
}

// 使用示例
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
    @Override
    public void migrate(@NonNull SupportSQLiteDatabase database) {
        // 添加新列
        database.execSQL("ALTER TABLE users ADD COLUMN phone TEXT");
    }
};

static final Migration MIGRATION_2_3 = new Migration(2, 3) {
    @Override
    public void migrate(@NonNull SupportSQLiteDatabase database) {
        // 创建新表
        database.execSQL("CREATE TABLE IF NOT EXISTS `orders` " +
            "(`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
            "`user_id` INTEGER NOT NULL, " +
            "`total` REAL NOT NULL)");

        // 创建索引
        database.execSQL("CREATE INDEX `index_orders_user_id` ON `orders` (`user_id`)");
    }
};

// 应用迁移
AppDatabase db = Room.databaseBuilder(context, AppDatabase.class, "database")
    .addMigrations(MIGRATION_1_2, MIGRATION_2_3)
    .build();

4.2 迁移路径查找

多步迁移

版本1

MIGRATION_1_2

版本2

MIGRATION_2_3

版本3

MIGRATION_3_4

版本4

MIGRATION_1_3

// room-runtime/src/main/java/androidx/room/migration/MigrationPathFinder.java
class MigrationPathFinder {

    /**
     * 查找迁移路径
     */
    public List<Migration> findMigrationPath(int start, int end) {
        if (start == end) {
            return Collections.emptyList();
        }

        boolean migratingUp = end > start;
        List<Migration> result = new ArrayList<>();
        return findUpMigrationPath(result, migratingUp, start, end);
    }

    private List<Migration> findUpMigrationPath(
        List<Migration> result,
        boolean upgrade,
        int start,
        int end
    ) {
        while (upgrade ? start < end : start > end) {
            Set<Migration> targetNodes = mMigrations.get(start);
            if (targetNodes == null) {
                return null;
            }

            int targetVersion = upgrade ? end : start;
            int finalVersion = upgrade ? start : end;

            Migration migration = null;
            int minDistance = Integer.MAX_VALUE;

            // 查找最短路径
            for (Migration m : targetNodes) {
                int potentialVersion = upgrade ? m.endVersion : m.startVersion;
                if (potentialVersion <= targetVersion && potentialVersion > finalVersion) {
                    int distance = Math.abs(potentialVersion - targetVersion);
                    if (distance < minDistance) {
                        migration = m;
                        minDistance = distance;
                    }
                }
            }

            if (migration == null) {
                return null;
            }

            result.add(migration);
            start = upgrade ? migration.endVersion : migration.startVersion;
        }

        return result;
    }
}

4.3 自动迁移(Room 2.4+)

@Database(
    entities = [User::class],
    version = 2,
    autoMigrations = [
        AutoMigration(from = 1, to = 2)
    ]
)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

// 复杂自动迁移(需要自定义逻辑)
@Database(
    entities = [User::class],
    version = 3,
    autoMigrations = [
        AutoMigration(
            from = 2,
            to = 3,
            spec = Migration2To3::class
        )
    ]
)
abstract class AppDatabase : RoomDatabase() {

    @DeleteColumn(tableName = "users", columnName = "temp_data")
    @RenameColumn(tableName = "users", fromColumnName = "name", toColumnName = "user_name")
    class Migration2To3 : AutoMigrationSpec
}

五、LiveData和Flow集成

5.1 LiveData支持

@Dao
interface UserDao {

    // 返回LiveData,自动监听数据变化
    @Query("SELECT * FROM users")
    fun getAllUsersLive(): LiveData<List<User>>

    @Query("SELECT * FROM users WHERE id = :userId")
    fun getUserLive(userId: Long): LiveData<User>
}

// 使用
val usersLiveData = database.userDao().getAllUsersLive()
usersLiveData.observe(lifecycleOwner) { users ->
    // 数据更新时自动触发
    adapter.submitList(users)
}

5.2 LiveData实现原理

// room-runtime/src/main/java/androidx/room/RoomTrackingLiveData.java
class RoomTrackingLiveData<T> extends LiveData<T> {

    final RoomDatabase mDatabase;
    final boolean mInTransaction;
    final Callable<T> mComputeFunction;

    private final InvalidationTracker.Observer mObserver;

    private AtomicBoolean mInvalid = new AtomicBoolean(true);
    private AtomicBoolean mComputing = new AtomicBoolean(false);
    private AtomicBoolean mRegisteredObserver = new AtomicBoolean(false);

    final Runnable mRefreshRunnable = new Runnable() {
        @Override
        public void run() {
            if (mRegisteredObserver.compareAndSet(false, true)) {
                mDatabase.getInvalidationTracker().addObserver(mObserver);
            }

            boolean computed;
            do {
                computed = false;
                if (mComputing.compareAndSet(false, true)) {
                    try {
                        T value = null;
                        while (mInvalid.compareAndSet(true, false)) {
                            computed = true;
                            try {
                                // 执行查询
                                value = mComputeFunction.call();
                            } catch (Exception e) {
                                throw new RuntimeException("Exception while computing database"
                                    + " live data.", e);
                            }
                        }
                        if (computed) {
                            postValue(value);
                        }
                    } finally {
                        mComputing.set(false);
                    }
                }
            } while (computed && mInvalid.get());
        }
    };

    RoomTrackingLiveData(
        RoomDatabase database,
        String[] tableNames,
        boolean inTransaction,
        Callable<T> computeFunction
    ) {
        mDatabase = database;
        mInTransaction = inTransaction;
        mComputeFunction = computeFunction;
        mObserver = new InvalidationTracker.Observer(tableNames) {
            @Override
            public void onInvalidated(@NonNull Set<String> tables) {
                ArchTaskExecutor.getInstance().executeOnMainThread(mInvalidRefreshRunnable);
            }
        };
    }

    @Override
    protected void onActive() {
        super.onActive();
        mDatabase.getQueryExecutor().execute(mRefreshRunnable);
    }

    @Override
    protected void onInactive() {
        super.onInactive();
        ArchTaskExecutor.getInstance().executeOnDiskIO(mInvalidationRunnable);
    }
}

5.3 Flow支持(Kotlin协程)

@Dao
interface UserDao {

    // 返回Flow
    @Query("SELECT * FROM users")
    fun getAllUsersFlow(): Flow<List<User>>

    @Query("SELECT * FROM users WHERE id = :userId")
    fun getUserFlow(userId: Long): Flow<User?>
}

// 使用
lifecycleScope.launch {
    database.userDao().getAllUsersFlow()
        .collect { users ->
            // 数据更新时自动触发
            adapter.submitList(users)
        }
}

六、性能优化

6.1 批量操作

@Dao
interface UserDao {

    // 批量插入
    @Insert
    suspend fun insertUsers(users: List<User>)

    // 批量更新
    @Update
    suspend fun updateUsers(users: List<User>)

    // 批量删除
    @Delete
    suspend fun deleteUsers(users: List<User>)

    // 事务保证原子性
    @Transaction
    suspend fun updateUsersAndOrders(users: List<User>, orders: List<Order>) {
        insertUsers(users)
        insertOrders(orders)
    }
}

6.2 索引优化

@Entity(
    tableName = "users",
    indices = [
        // 单列索引
        @Index(value = ["email"], unique = true),
        // 复合索引
        @Index(value = ["name", "age"]),
        // 全文搜索索引(需要FTS)
        @Index(value = ["content"], orders = [Index.Order.ASC])
    ]
)
data class User(
    @PrimaryKey(autoGenerate = true)
    val id: Long = 0,
    val name: String,
    val age: Int,
    val email: String
)

6.3 查询优化

查询优化

使用索引

减少JOIN

投影查询

分页加载

复合索引

唯一索引

@Relation

@Embedded

仅查询需要的列

PagingSource

LimitOffset

// [通过] 投影查询(只查询需要的列)
@Query("SELECT id, name FROM users")
fun getUserNames(): List<UserName>

data class UserName(val id: Long, val name: String)

// [通过] 分页查询
@Query("SELECT * FROM users LIMIT :limit OFFSET :offset")
fun getUsersPaged(limit: Int, offset: Int): List<User>

// [通过] 使用Paging 3
@Query("SELECT * FROM users ORDER BY name ASC")
fun getUsersPagingSource(): PagingSource<Int, User>

七、测试支持

7.1 内存数据库测试

@RunWith(AndroidJUnit4::class)
class UserDaoTest {

    private lateinit var database: AppDatabase
    private lateinit var userDao: UserDao

    @Before
    fun createDb() {
        val context = ApplicationProvider.getApplicationContext<Context>()
        database = Room.inMemoryDatabaseBuilder(
            context,
            AppDatabase::class.java
        ).build()
        userDao = database.userDao()
    }

    @After
    fun closeDb() {
        database.close()
    }

    @Test
    fun insertAndGetUser() = runBlocking {
        val user = User(name = "Alice", age = 25, email = "alice@example.com")
        val id = userDao.insertUser(user)

        val loadedUser = userDao.getUserById(id)
        assertThat(loadedUser.name, equalTo("Alice"))
    }

    @Test
    fun getAllUsers() = runBlocking {
        val users = listOf(
            User(name = "Alice", age = 25, email = "alice@example.com"),
            User(name = "Bob", age = 30, email = "bob@example.com")
        )
        userDao.insertUsers(users)

        val allUsers = userDao.getAllUsers()
        assertThat(allUsers.size, equalTo(2))
    }
}

7.2 Migration测试

@RunWith(AndroidJUnit4::class)
class MigrationTest {

    private val TEST_DB = "migration-test"

    @get:Rule
    val helper: MigrationTestHelper = MigrationTestHelper(
        InstrumentationRegistry.getInstrumentation(),
        AppDatabase::class.java.canonicalName,
        FrameworkSQLiteOpenHelperFactory()
    )

    @Test
    fun migrate1To2() {
        // 创建版本1数据库并插入数据
        helper.createDatabase(TEST_DB, 1).apply {
            execSQL("INSERT INTO users (id, name, age) VALUES (1, 'Alice', 25)")
            close()
        }

        // 执行迁移到版本2
        helper.runMigrationsAndValidate(TEST_DB, 2, true, MIGRATION_1_2)

        // 验证迁移后的数据
        val db = getMigratedRoomDatabase()
        val user = db.userDao().getUserById(1)
        assertThat(user.name, equalTo("Alice"))
    }

    private fun getMigratedRoomDatabase(): AppDatabase {
        val database = Room.databaseBuilder(
            InstrumentationRegistry.getInstrumentation().targetContext,
            AppDatabase::class.java,
            TEST_DB
        ).addMigrations(MIGRATION_1_2, MIGRATION_2_3).build()

        helper.closeWhenFinished(database)
        return database
    }
}

八、总结与最佳实践

8.1 核心优势

  1. 编译时验证:SQL语法错误在编译期发现
  2. 类型安全:避免运行时类型转换错误
  3. LiveData集成:自动监听数据变化
  4. 迁移管理:清晰的版本升级路径
  5. 测试友好:支持内存数据库测试

8.2 最佳实践

// [通过] 推荐:使用单例模式
class DatabaseProvider {
    companion object {
        @Volatile
        private var INSTANCE: AppDatabase? = null

        fun getDatabase(context: Context): AppDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    "app_database"
                )
                .addMigrations(MIGRATION_1_2, MIGRATION_2_3)
                .build()
                INSTANCE = instance
                instance
            }
        }
    }
}

// [通过] 推荐:使用suspend函数
@Dao
interface UserDao {
    @Insert
    suspend fun insertUser(user: User): Long

    @Query("SELECT * FROM users")
    suspend fun getAllUsers(): List<User>
}

// [通过] 推荐:使用事务保证数据一致性
@Transaction
suspend fun transferMoney(fromId: Long, toId: Long, amount: Double) {
    updateBalance(fromId, -amount)
    updateBalance(toId, amount)
}

// [未通过] 避免:在主线程执行数据库操作
// 使用协程或RxJava处理异步操作

// [未通过] 避免:频繁打开关闭数据库
// 使用单例模式复用数据库实例

8.3 性能数据

  • 查询性能:接近原生SQLite(额外开销<5%)
  • 编译时间:增加10-30秒(中型项目)
  • APK增量:约100-300KB(生成的代码)
  • 内存占用:约1-3MB(数据库连接池)

Room通过编译时代码生成和类型安全设计,为Android开发者提供了优雅的数据库访问方案,是现代Android应用的标准选择。


参考资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龚寿生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值