# Android组件化架构 – 5. 数据存储 & GreenDao,Room 站点:答源(https://www.dandroid.cn/) 原文:https://www.dandroid.cn/archives/19691 Canonical:https://www.dandroid.cn/archives/19691 作者:admin 发布日期:2023-09-10 最后更新:2023-09-10 03:37 分类:教程 内容类型:post 说明:本页是原文的纯文本镜像,供大模型与检索器完整读取;不含导航、广告、付费框。引用请以上方「原文」链接为准。 ## 正文 1. 数据的存储方式 最常用的五种数据存储方式: SharePreferences, File I/O, SQLite, ContentProvider, 网络 2. 组件化存储 Android原生的存储体系是全局的,在组件化的开发中,五种原生的存储方式是完全通用的; 比较值得介绍的是两个主流的数据库框架GreenDao,Room GreenDao 是目前众多orm数据库中最稳定,速度最快,编写体验最好的框架,并且支持RxJava, 支持sqlcipher数据库加密 // 一. greenDao的使用 //1. 项目根build.gradle文件中添加classpath buildscript { ... dependencies { ... classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' } } //2.app module的build.gradle中添加依赖 apply plugin: 'org.greenrobot.greendao' android { ... // 配置数据库相关信息 greendao { //数据库版本号 schemaVersion 1 // 设置DaoMaster、DaoSession、Dao 包名 daoPackage 'com.ljy.publicdemo.greendao' //设置DaoMaster、DaoSession、Dao目录 targetGenDir 'src/main/java' } } dependencies { ... //greendao implementation 'org.greenrobot:greendao:3.2.2' // 数据库加密 implementation 'net.zetetic:android-database-sqlcipher:3.5.6' } //3.创建一个实体类并添加注解 @Entity public class TestBean { String name; String value; } //4. Make Project, 自动在greendao目录下生成DaoMaster、DaoSession类以及TestBean对应的TestBeanDao类 //一定要先创建一个添加了@Entity注解的实体类,再make,不然无法自动生成上述类 //5. 创建一个工具类GreenDaoHelper方便使用及数据库加密操作 public class GreenDaoHelper { private static final GreenDaoHelper ourInstance = new GreenDaoHelper(); private static final String DATA_BASE_NAME = "jinYangSqlData"; private DaoSession mDaoSession; private DaoMaster daoMaster; public static GreenDaoHelper getInstance() { return ourInstance; } private GreenDaoHelper() { } public void init(Context mContext) { DaoMaster.DevOpenHelper openHelper = new DaoMaster.DevOpenHelper(mContext, DATA_BASE_NAME); //1. Database db = openHelper.getWritableDb(); //2 //加密方式的数据库,建议使用本设备的唯一标识类似于UUID的字段做个加密获得, // 这样每个机器的密钥是不同的,并且不会发生改变,如果把加密后的数据库的本地文件扒出来, // 也是查不到内容的, 使用dump仅仅可以看到表结构和列名 // Database db = openHelper.getEncryptedWritableDb("123456"); //密码错误会有运行时报错 // net.sqlcipher.database.SQLiteException: file is encrypted or is not a database: , // while compiling: select count(*) from sqlite_master; daoMaster = new DaoMaster(db); mDaoSession = daoMaster.newSession(); } public DaoSession getDaoSession() { return mDaoSession; } public void clearAllSQL() { if (daoMaster != null) { DaoMaster.dropAllTables(daoMaster.getDatabase(), true); DaoMaster.createAllTables(daoMaster.getDatabase(), true); } } } //6.application中调用工具类的初始化方法 public class MyApplication extends BaseApplication { @Override public void onCreate() { super.onCreate(); GreenDaoHelper.getInstance().init(getApplicationContext()); } ... } //7.CRUD(增删改查) class GreenDaoActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_green_dao) //GreenDaoHelper.getInstance().clearAllSQL()//表结构有变化时使用 //插入数据 val testBean = TestBean(1, "emily", "beautiful girl") GreenDaoHelper.getInstance().daoSession.testBeanDao.insert(testBean) GreenDaoHelper.getInstance().daoSession.testBeanDao.insert(TestBean(2, "yang", "smart boy")) //修改 //查询 val testBeanAll = GreenDaoHelper.getInstance().daoSession.testBeanDao.loadAll() for (it in testBeanAll) { LjyLogUtil.d("111-$it") if (it.name == "emily") { } } //修改 testBean.name = "小木南" GreenDaoHelper.getInstance().daoSession.testBeanDao.update(testBean) //查询 for (it in GreenDaoHelper.getInstance().daoSession.testBeanDao.loadAll()) { LjyLogUtil.d("222-$it") } //删除 GreenDaoHelper.getInstance().daoSession.testBeanDao.delete(testBean) //查询 for (it in GreenDaoHelper.getInstance().daoSession.testBeanDao.loadAll()) { LjyLogUtil.d("333-$it") } //清空 GreenDaoHelper.getInstance().daoSession.testBeanDao.deleteAll() LjyLogUtil.d("444-${GreenDaoHelper.getInstance().daoSession.testBeanDao.loadAll().size}") } } 另外还有一个比较常用的数据库框架realm,其插入和查询速度优于greendao, 删除速度greendao更快; greendao的包体积远小于realm, 操作更简单 ,因为greendao底层使用Android原生的SQLite3, 而realm使用本身的数据查询引擎, 需要引入额外的so库, realm支持JSON和流式api也支持rxjava; 有兴趣的话可以研究一下,其GitHub地址如下:https://github.com/realm/realm-java Room 另一个比较主流也是Google官方比较推荐的数据库框架, 和greenDao比较类似, 更优点是其sql语句,编译时编译器会做检查,整体使用上也更方便 其官方文档如下 https://developer.android.google.cn/training/data-storage/room Room 在 SQLite 上提供了一个抽象层,以便在充分利用 SQLite 的强大功能的同时,能够流畅地访问数据库。 我们强烈建议您使用 Room(而不是 SQLite)(官网原话) (我觉得,随着jetPack体系的推广,会逐渐替代greenDao等其他orm数据库) Room 包含 3 个主要组件: 数据库:包含数据库持有者,并作为应用已保留的持久关系型数据的底层连接的主要接入点; 使用 @Database 注释的类应满足以下条件: 是扩展 RoomDatabase 的抽象类。 在注释中添加与数据库关联的实体列表。 包含具有 0 个参数且返回使用 @Dao 注释的类的抽象方法。 在运行时,您可以通过调用 Room.databaseBuilder() 或 Room.inMemoryDatabaseBuilder() 获取 Database 的实例。 Entity:表示数据库中的表。 DAO:包含用于访问数据库的方法。 //room的使用 //1. build.gradle中添加依赖 apply plugin: 'kotlin-kapt'//这行如果下面使用了kapt一定要添加 dependencies { //room数据库 def room_version = "2.2.5" implementation "androidx.room:room-runtime:$room_version" // For Kotlin use kapt instead of annotationProcessor // annotationProcessor "androidx.room:room-compiler:$room_version" kapt "androidx.room:room-compiler:$room_version" // optional - Kotlin Extensions and Coroutines support for Room implementation "androidx.room:room-ktx:$room_version" // optional - RxJava support for Room implementation "androidx.room:room-rxjava2:$room_version" // optional - Guava support for Room, including Optional and ListenableFuture implementation "androidx.room:room-guava:$room_version" // Test helpers testImplementation "androidx.room:room-testing:$room_version" } //2. 创建实体类,并添加 @Entity 注解 @Entity data class User( @PrimaryKey val uid: Int, @ColumnInfo(name = "user_name") var userName: String?, @ColumnInfo(name = "user_age") var userAge: Int? ) //3. 创建UserDao @Dao interface UserDao { @Query("SELECT * FROM user") fun getAll(): List @Query("SELECT * FROM user WHERE uid IN (:userIds)") fun loadAllByIds(userIds: IntArray?): List @Query("SELECT * FROM user WHERE user_name LIKE :userName AND user_age LIKE :userAge LIMIT 1") fun findByNameAndAge(userName: String,userAge: Int): User @Query("SELECT * FROM user WHERE user_name LIKE :userName LIMIT 1") fun findByName(userName: String): User @Query("SELECT * FROM user WHERE user_age LIKE :userAge ") fun findByAge(userAge: Int): List // @Insert @Insert(onConflict = OnConflictStrategy.REPLACE) //解决 Caused by: android.database.sqlite.SQLiteConstraintException: //UNIQUE constraint failed: User.uid (code 1555 SQLITE_CONSTRAINT_PRIMARYKEY) fun insertAll(vararg users: User) @Delete fun delete(user: User) @Update fun update(user: User) } //3. 创建常量类管理数据库名称及版本 public interface Constants { /** * 数据库名称 */ String DB_NAME = "JinYangDataBase.db"; /** * 数据库版本 */ int DB_VERSION = 2; } //4. 创建AppDatabase @Database(entities = [User::class], version = Constants.DB_VERSION) abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao companion object { val instance = AppDatabaseHolder.db } private object AppDatabaseHolder { val db: AppDatabase = Room .databaseBuilder( MyApplication.myApplicationContext, AppDatabase::class.java, Constants.DB_NAME ) .allowMainThreadQueries() //允许在主线程中查询 .build() } } //5. CRUD(增删改查) class RoomActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_room) //验证单例 LjyLogUtil.d( AppDatabase.instance.toString()) LjyLogUtil.d( AppDatabase.instance.toString()) //遍历删除 for (it in AppDatabase.instance.userDao().getAll()) { AppDatabase.instance.userDao().delete(it) LjyLogUtil.d("delete:$it") } //插入 val user1 = User(1, "Yang", 18) val user2 = User(2, "abc", 22) val user3 = User(3, "def", 22) val user4 = User(4, "ghi", 22) val user5 = User(4, "qwerty", 66) AppDatabase.instance.userDao().insertAll(user1, user2,user3,user4,user5) //查询 for (it in AppDatabase.instance.userDao().getAll()) { LjyLogUtil.d("getAll:$it") } //修改 user1.userName="emily" AppDatabase.instance.userDao().update(user1) //loadAllByIds for (it in AppDatabase.instance.userDao().loadAllByIds(intArrayOf(1,2,3,4))){ LjyLogUtil.d("loadAllByIds:$it") } //findByName val findByName=AppDatabase.instance.userDao().findByName("abc") LjyLogUtil.d("findByName:$findByName") //findByAge for (it in AppDatabase.instance.userDao().findByAge(22)){ LjyLogUtil.d("findByAge:$it") } } } 3. 组件化数据库 一般Android中数据库的运用范围是整个app,但是组件化架构中使用关系型数据库的时候,就需要考虑解耦; 因为ORM数据库映射框架的原理就是将实体对象转化为数据映射,各组件中的实体类如果放在自身module中是无法传递的; 如果都放在baseModule中可以解决上述问题,更优的实现方案是创建一个dataBase module,在base module中添加此module的依赖; 整个组件化架构可以分为如下几层: 应用层:app module 组件层:各业务模块组件.如login,pay,user 基础层: base module, 可以放一些mvp,mvvm的基类 框架层: XXXBus,XXXDataBase, Utils, Weights等 --- 由 答源 DAN 底座 1.4.1 生成 · 2026-11-13T11:58:25+00:00