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等