当前位置: 首页 > article >正文

(二十三)安卓开发中数据存储之Room详解

在安卓开发中,Room 是一个强大的本地数据库解决方案,它是 Android Jetpack 的一部分,基于 SQLite 构建,提供了更高层次的抽象。Room 简化了数据库操作,减少了样板代码,同时支持与 LiveData 和 ViewModel 的集成,使得数据管理更加高效和响应式。本文将结合代码示例和具体使用场景,详细讲解 Room 的核心组件、设置步骤以及实际应用。


Room 的核心组件

Room 数据库主要由以下三个组件组成:

  1. Entity(实体)
    代表数据库中的一个表。每个实体类通过注解映射到数据库中的表结构。
  2. Dao(数据访问对象)
    一个接口,定义了访问数据库的具体方法,例如插入、查询、更新和删除。
  3. Database(数据库)
    一个继承自 RoomDatabase 的抽象类,包含数据库的版本信息和所有 Dao 的实例。

下面,我们将通过一个具体场景——“管理用户信息”——来逐步展示 Room 的使用。


设置 Room 数据库

添加依赖

首先,在项目中添加 Room 的依赖。打开模块级别的 build.gradle 文件,添加以下代码:

dependencies {def room_version = "2.4.0" // 请确保使用最新版本implementation "androidx.room:room-runtime:$room_version"annotationProcessor "androidx.room:room-compiler:$room_version"// 如果使用 Kotlin,替换 annotationProcessor 为 kapt// kapt "androidx.room:room-compiler:$room_version"
}

同步项目后,Room 就可以在你的应用中使用。


示例场景:管理用户信息

假设我们开发一个简单的安卓应用,需要存储用户的姓名和 ID,并在应用中实现用户的增删改查功能。以下是实现步骤。

1. 创建 Entity(实体)

我们定义一个 User 类,表示用户信息表。使用 @Entity 注解标记类,@PrimaryKey 指定主键,@ColumnInfo 指定列名。

import androidx.room.Entity;
import androidx.room.PrimaryKey;
import androidx.room.ColumnInfo;@Entity(tableName = "users")
public class User {@PrimaryKey(autoGenerate = true) // 主键,自动递增public int id;@ColumnInfo(name = "first_name") // 列名:first_namepublic String firstName;@ColumnInfo(name = "last_name")  // 列名:last_namepublic String lastName;// 构造函数public User(String firstName, String lastName) {this.firstName = firstName;this.lastName = lastName;}
}

在这个例子中:

  • 表名为 users
  • id 是主键,自动生成。
  • firstNamelastName 分别存储用户的名字和姓氏。

2. 创建 Dao(数据访问对象)

接下来,定义一个 UserDao 接口,用于操作 users 表。使用注解如 @Insert@Query 等指定数据库操作。

import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import androidx.room.Delete;
import java.util.List;@Dao
public interface UserDao {@Insertvoid insert(User user); // 插入用户@Query("SELECT * FROM users") // 查询所有用户List<User> getAllUsers();@Query("SELECT * FROM users WHERE id = :userId") // 根据 ID 查询用户User getUserById(int userId);@Updatevoid update(User user); // 更新用户@Deletevoid delete(User user); // 删除用户
}

UserDao 提供了基本的 CRUD(创建、读取、更新、删除)操作。

3. 创建 Database(数据库)

定义一个 AppDatabase 抽象类,继承自 RoomDatabase,指定实体和版本号。

import androidx.room.Database;
import androidx.room.RoomDatabase;@Database(entities = {User.class}, version = 1) // 包含 User 实体,版本为 1
public abstract class AppDatabase extends RoomDatabase {public abstract UserDao userDao(); // 提供 UserDao 实例
}

4. 初始化数据库

为了避免重复创建数据库实例,我们使用单例模式初始化 AppDatabase

import android.content.Context;
import androidx.room.Room;public class DatabaseClient {private static AppDatabase appDatabase;public static AppDatabase getInstance(Context context) {if (appDatabase == null) {appDatabase = Room.databaseBuilder(context.getApplicationContext(),AppDatabase.class, "my-database") // 数据库名为 my-database.build();}return appDatabase;}
}

在应用中使用 Room

现在,我们可以在 Activity 中使用 Room 数据库执行操作。例如,在 MainActivity 中插入一个用户并查询所有用户。

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import java.util.List;public class MainActivity extends AppCompatActivity {private UserDao userDao;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 初始化数据库和 DaoAppDatabase db = DatabaseClient.getInstance(this);userDao = db.userDao();// 插入一个用户User newUser = new User("John", "Doe");userDao.insert(newUser);// 查询所有用户并打印List<User> users = userDao.getAllUsers();for (User user : users) {System.out.println(user.firstName + " " + user.lastName);}}
}

运行结果:控制台将输出 John Doe,表示用户数据已成功存储并读取。


进阶场景:数据库迁移

假设我们需要为 User 表添加一个 email 字段。数据库结构变更需要进行迁移。

修改 Entity

更新 User 类,添加 email 字段:

@Entity(tableName = "users")
public class User {@PrimaryKey(autoGenerate = true)public int id;@ColumnInfo(name = "first_name")public String firstName;@ColumnInfo(name = "last_name")public String lastName;@ColumnInfo(name = "email") // 新增 email 字段public String email;public User(String firstName, String lastName, String email) {this.firstName = firstName;this.lastName = lastName;this.email = email;}
}

定义迁移逻辑

将数据库版本从 1 升级到 2,并提供迁移代码:

import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;public class DatabaseMigrations {public static final Migration MIGRATION_1_2 = new Migration(1, 2) {@Overridepublic void migrate(SupportSQLiteDatabase database) {database.execSQL("ALTER TABLE users ADD COLUMN email TEXT"); // 添加 email 列}};
}

更新 Database 初始化

DatabaseClient 中添加迁移:

appDatabase = Room.databaseBuilder(context.getApplicationContext(),AppDatabase.class, "my-database").addMigrations(DatabaseMigrations.MIGRATION_1_2) // 添加迁移.build();

迁移后,应用可以继续使用更新后的数据库 schema。


响应式数据:结合 LiveData 和 ViewModel

Room 支持与 LiveData 集成,实现数据的实时更新。例如,我们希望用户列表在数据变更时自动刷新 UI。

修改 Dao 返回 LiveData

@Dao
public interface UserDao {@Query("SELECT * FROM users")LiveData<List<User>> getAllUsersLive(); // 返回 LiveData// 其他方法保持不变
}

创建 ViewModel

import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import java.util.List;public class UserViewModel extends ViewModel {private LiveData<List<User>> usersLiveData;public UserViewModel(AppDatabase db) {usersLiveData = db.userDao().getAllUsersLive();}public LiveData<List<User>> getUsersLiveData() {return usersLiveData;}
}

在 Activity 中观察数据

import androidx.lifecycle.ViewModelProvider;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);AppDatabase db = DatabaseClient.getInstance(this);UserViewModel viewModel = new ViewModelProvider(this).get(UserViewModel.class);// 观察 LiveData 并更新 UIviewModel.getUsersLiveData().observe(this, users -> {for (User user : users) {System.out.println(user.firstName + " " + user.lastName);}// 这里可以更新 UI,例如刷新 RecyclerView});}
}

效果:每次数据库中的用户数据发生变化,observe 块都会自动执行,实时更新界面。


总结

Room 是安卓开发中一个简单而强大的本地数据存储工具。通过 EntityDaoDatabase 三个核心组件,我们可以轻松实现数据的增删改查。结合代码示例,我们展示了如何:

  1. 设置 Room 数据库并定义基本结构。
  2. 在应用中执行 CRUD 操作。
  3. 处理数据库迁移以适应 schema 变更。
  4. 使用 LiveData 和 ViewModel 实现响应式数据管理。

在实际开发中,合理设计数据库结构、优化查询方法以及妥善处理迁移,是确保数据存储高效和稳定的关键。

相关文章:

(二十三)安卓开发中数据存储之Room详解

在安卓开发中&#xff0c;Room 是一个强大的本地数据库解决方案&#xff0c;它是 Android Jetpack 的一部分&#xff0c;基于 SQLite 构建&#xff0c;提供了更高层次的抽象。Room 简化了数据库操作&#xff0c;减少了样板代码&#xff0c;同时支持与 LiveData 和 ViewModel 的…...

AIoT 智变浪潮演讲实录 | 刘浩然:让硬件会思考:边缘大模型网关助力硬件智能革新

4 月 2 日&#xff0c;由火山引擎与英特尔联合主办的 AIoT “智变浪潮”技术沙龙在深圳成功举行&#xff0c;活动聚焦 AI 硬件产业的技术落地与生态协同&#xff0c;吸引了芯片厂商、技术方案商、品牌方及投资机构代表等 700 多位嘉宾参会。 会上&#xff0c;火山引擎边缘智能高…...

【Windows】系统安全移除移动存储设备指南:告别「设备被占用」弹窗

Windows系统安全移除移动存储设备指南&#xff1a;告别「设备被占用」弹窗 解决移动硬盘和U盘正在被占用无法弹出 一、问题背景 使用Windows系统时&#xff0c;经常遇到移动硬盘/U盘弹出失败提示「设备正在使用中」&#xff0c;即使已关闭所有可见程序。本文将系统梳理已验证…...

C++运算符重载全面总结

C运算符重载全面总结 运算符重载是C中一项强大的特性&#xff0c;它允许程序员为自定义类型定义运算符的行为。以下是关于C运算符重载的详细总结&#xff1a; 一、基本概念 1. 什么是运算符重载 运算符重载是指为自定义类型&#xff08;类或结构体&#xff09;重新定义或重…...

ArmSoM Sige5 CM5:RK3576 上 Ultralytics YOLOv11 边缘计算新标杆

在计算机视觉技术加速落地的今天&#xff0c;ArmSoM 正式宣布其基于 ​​Rockchip RK3576​​ 的旗舰产品 ​​Sige5 开发板​​ 和 ​​CM5 核心板​​ 全面支持 Ultralytics YOLOv11 模型的 RKNN 部署。这一突破标志着边缘计算领域迎来新一代高性能、低功耗的 AI 解决方案&am…...

【计算机网络】什么是路由?核心概念与实战详解

&#x1f4cc; 引言 路由&#xff08;Routing&#xff09;是互联网的“导航系统”&#xff0c;负责将数据包从源设备精准送达目标设备。无论是浏览网页、发送消息还是视频通话&#xff0c;背后都依赖路由技术。本文将用通俗类比技术深度的方式&#xff0c;解析路由的核心机制。…...

【ubuntu】linux开机自启动

目录 开机自启动&#xff1a; /etc/rc.loacl system V 使用/etc/rc*.d/系统运行优先级 遇到的问题&#xff1a; 1. Linux 系统启动阶段概述 方法1&#xff1a;/etc/rc5.d/ 脚本延时日志 方法二&#xff1a;使用 udev 规则来触发脚本执行 开机自启动&#xff1a; /etc/…...

dnf install openssl失败的原因和解决办法

网上有很多编译OpenSSL源码(3.x版本)为RPM包的文章&#xff0c;这些文章在安装RPM包时都是执行rpm -ivh openssl-xxx.rpm --nodeps --force 这个命令能在缺少依赖包的情况下能强行执行安装 其实根据Centos的文档&#xff0c;安装RPM包一般是执行yum install或dnf install。后者…...

Java 在人工智能领域的突围:从企业级架构到边缘计算的技术革新

一、Java AI 的底层逻辑&#xff1a;从语言特性到生态重构 在 Python 占据 AI 开发主导地位的当下&#xff0c;Java 正通过技术重构实现突围。作为拥有 30 年企业级开发经验的编程语言&#xff0c;Java 的核心优势在于强类型安全、内存管理能力和分布式系统支持&#xff0c;这…...

操作系统导论——第19章 分页:快速地址转换(TLB)

使用分页作为核心机制来实现虚拟内存&#xff0c;可能会带来较高的性能开销。使用分页&#xff0c;就要将内存地址空间切分成大量固定大小的单元&#xff08;页&#xff09;&#xff0c;并且需要记录这些单元的地址映射信息。因为这些映射信息一般存储在物理内存中&#xff0c;…...

计算机网络:流量控制与可靠传输机制

目录 基本概念 流量控制&#xff1a;别噎着啦&#xff01; 可靠传输&#xff1a;快递必达服务 传输差错&#xff1a;现实中的意外 滑动窗口 基本概念 换句话说&#xff1a;批量发货排队验收 停止-等待协议 SW&#xff08;发1份等1份&#xff09; 超时重传&#xff1a;…...

SaaS、Paas、IaaS、MaaS、BaaS五大云计算服务模式

科普版&#xff1a;通俗理解五大云计算服务模式 1. SaaS&#xff08;软件即服务&#xff09; 一句话解释&#xff1a;像“租用公寓”&#xff0c;直接使用现成的软件&#xff0c;无需操心维护。 案例&#xff1a;使用钉钉办公、在网页版WPS编辑文档。服务提供商负责软件更新和…...

计算机网络 - 三次握手相关问题

通过一些问题来讨论 TCP 协议中的三次握手机制 说一下三次握手的大致过程&#xff1f;为什么需要三次握手&#xff1f;2 次不可以吗&#xff1f;第三次握手&#xff0c;可以携带数据吗&#xff1f;第二次呢&#xff1f;三次握手连接阶段&#xff0c;最后一次ACK包丢失&#xf…...

通过使用 include 语句加载并执行一个CMake脚本来引入第三方库

通过使用 include 语句加载并执行一个CMake脚本来引入第三方库 当项目中使用到第三方库时&#xff0c;可以通过使用 include 语句来加载并执行一个CMake脚本&#xff0c;在引入的CMake脚本中进行第三方库的下载、构建和库查找路径的设置等操作&#xff0c;以这种方式简化项目中…...

架构生命周期(高软57)

系列文章目录 架构生命周期 文章目录 系列文章目录前言一、软件架构是什么&#xff1f;二、软件架构的内容三、软件设计阶段四、构件总结 前言 本节讲明架构设计的架构生命周期概念。 一、软件架构是什么&#xff1f; 二、软件架构的内容 三、软件设计阶段 四、构件 总结 就…...

JMeter使用

1.简介 1.1 打开方式 ①点击bat,打开 ②添加JMeter系统环境变量,输⼊命令jmeter即可启动JMeter⼯具 1.2 配置 简体中文 放大字体 1.3 使用 ①添加线程组 ②创建http请求 2. 组件 2.1 线程组 控制JMeter将⽤于执⾏测试的线程数&#xff0c;也可以把⼀个线程理解为⼀个测…...

Ant Design Vue 表格复杂数据合并单元格

Ant Design Vue 表格复杂数据合并单元格 官方合并效果 官方示例 表头只支持列合并&#xff0c;使用 column 里的 colSpan 进行设置。 表格支持行/列合并&#xff0c;使用 render 里的单元格属性 colSpan 或者 rowSpan 设值为 0 时&#xff0c;设置的表格不会渲染。 <temp…...

Fiddler为什么可以看到一次HTTP请求数据?

1、作为代理服务器 Fiddler作为代理服务器&#xff0c;拦截了设备与互联网服务器之间的所有HTTP和HTTPS流量。当客户端&#xff08;如浏览器&#xff09;发送请求时&#xff0c;请求先到达Fiddler&#xff0c;然后由Fiddler转发到目标服务器&#xff1b;服务器的响应也会返回给…...

第十九讲 | XGBoost 与集成学习:精准高效的地学建模新范式

&#x1f7e8; 一、为什么要学习集成学习&#xff1f; 集成学习&#xff08;Ensemble Learning&#xff09; 是一种将多个弱学习器&#xff08;如决策树&#xff09;组合成一个强学习器的策略。它在地理学、生态学、遥感分类等领域表现尤为突出。 &#x1f4cc; 应用优势&#…...

基于 GoFrame 框架的电子邮件发送实践:优势、特色与经验分享

1. 引言 如果你是一位有1-2年Go开发经验的后端开发者&#xff0c;可能已经熟悉了Go语言在性能和并发上的天然优势&#xff0c;也曾在项目中遇到过邮件发送的需求——无论是用户注册时的激活邮件、系统异常时的通知&#xff0c;还是营销活动中的批量促销邮件&#xff0c;邮件功…...

ECharts 如何实现柱状图悬停时,整个背景区域均可触发点击事件

1. 前言 ECharts 柱状图的点击事件默认仅响应柱子本身的点击&#xff0c;本文旨在实现整个背景区域均可触发点击事件 2. 实现思路 核心&#xff1a;全局监听 坐标判断 数据转换 通过 getZr() 监听整个画布点击&#xff0c;结合像素坐标判断是否在图表区域内‌通过 containPi…...

金融简单介绍及金融诈骗防范

在当今社会&#xff0c;金融学如同一股无形却强大的力量&#xff0c;深刻影响着我们生活的方方面面。无论是个人的日常收支、投资理财&#xff0c;还是国家的宏观经济调控&#xff0c;都与金融学紧密相连。​ 一、金融学的概念​ 金融学&#xff0c;简单来说&#xff0c;是研…...

cursor+高德MCP:制作一份旅游攻略

高德开放平台 | 高德地图API (amap.com) 1.注册成为开发者 2.进入控制台选择应用管理----->我的应用 3.新建应用 4.点击添加Key 5.在高德开发平台找到MCP的文档 6.按照快速接入的步骤&#xff0c;进行操作 一定要按照最新版的cursor, 如果之前已经安装旧的版本卸载掉重新安…...

软件版本命名规范Semantic Versioning

​语义化版本控制&#xff08;Semantic Versioning&#xff0c;简称 SemVer&#xff09;是一种广泛采用的版本号管理规范&#xff0c;旨在通过版本号传达软件更新的性质和影响&#xff0c;帮助开发者和用户理解每次发布的变更内容 &#x1f522; 版本号结构 语义化版本号通常采…...

Uniapp: 大纲

目录 一、基础巩固1.1、Uniapp:下拉选择框ba-tree-picker1.2、Uniapp&#xff1a;确认框1.3、Uniapp&#xff1a;消息提示1.4、Uniapp&#xff1a;获取当前定位坐标 二、项目配置2.1、Uniapp&#xff1a;修改端口号2.2、Uniapp&#xff1a;本地存储 一、基础巩固 1.1、Uniapp:…...

Kubernetes控制平面组件:API Server Webhook 授权机制 详解

云原生学习路线导航页&#xff08;持续更新中&#xff09; kubernetes学习系列快捷链接 Kubernetes架构原则和对象设计&#xff08;一&#xff09;Kubernetes架构原则和对象设计&#xff08;二&#xff09;Kubernetes架构原则和对象设计&#xff08;三&#xff09;Kubernetes控…...

【KWDB 创作者计划】_产品技术解读_1

【KWDB 创作者计划】_产品技术解读_1 一、存储引擎:高性能混合存储架构1. 存储模型设计2. 存储压缩与编码3. 持久化策略二、KWDB 组件源码解析1. 核心模块分层架构2. 关键组件源码剖析三、KWDB 特性代码通读1. 实时分析能力(Real-Time OLAP)2. 混合负载隔离(HTAP)3. 智能索…...

Python Cookbook-6.2 定义常量

任务 你需要定义一些模块级别的变量(比如命名的常量)&#xff0c;而且客户代码无法将其重新绑定。 解决方案 你可以把任何对象当做模块一样安装。将下列代码存为一个模块const.py&#xff0c;并放入你的Python的sys.path 指定的目录中: class _const(object):class ConstEr…...

【Python爬虫】简单案例介绍2

本文继续接着我的上一篇博客【Python爬虫】简单案例介绍1-CSDN博客 目录 跨页 3.2 环境准备 跨页 当对单个页面的结构有了清晰的认识并成功提取数据后&#xff0c;接下来就需要考虑页面之间的跨页问题。此时我们便迎来了下一个关键任务&#xff1a;如何实现跨页爬取&#xf…...

【神经网络】python实现神经网络(四)——误差反向传播的基础理论

一.反向传播 本章将介绍能够高效计算权重参数的梯度的方法——误差反向传播法,这里简单介绍一下什么是反向传播,加入有个函数y = f(x),那么它的反向传播为图下这个样子: 反向传播的计算顺序是,将输入信号E乘以节点的局部导数,然后将结果传递给下一个节点。这里所…...