赞
踩
在多module环境中,将数据类单独写在一个module中,只需引入到其他类中即可实现数据库的访问进行增删改查操作
在使用LitePal框架时,发现数据插入等操作极为方便,只需object.insert()即可实现数据写入,但是经常使用是的Room框架并且也挺依赖它可以获取LiveData对象的,所以自己整理了一个统一的管理类Repo,采用工厂模式找到各个Repo类并进行对应的Dao层操作
这里引用Google对Room的解释
处理大量结构化数据的应用可极大地受益于在本地保留这些数据。最常见的使用场景是缓存相关的数据,这样一来,当设备无法访问网络时,用户仍然可以在离线状态下浏览该内容。
Room 持久性库在 SQLite 上提供了一个抽象层,以便在充分利用 SQLite 的强大功能的同时,能够流畅地访问数据库。具体来说,Room 具有以下优势:
- 针对 SQL 查询的编译时验证。
- 可最大限度减少重复和容易出错的样板代码的方便注解。
- 简化了数据库迁移路径。
出于这些方面的考虑,我们强烈建议您使用 Room,而不是直接使用 SQLite API。
因为该模块是采用工厂模式进行设计,所以各个Repo类的获取均通过反射得到
引用百度百科对反射机制的解释
Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。
class Repo { companion object { @SuppressLint("StaticFieldLeak") var context: Context? = null /** * 保存各个Repo类 */ private val instance: MutableMap<String, BaseRepo<*>?> = HashMap(3) /** * 在创建application中使用 * @param context 上下文对象 */ @JvmStatic fun init(context: Context) { Repo.context = context } /** * 获取指定的数据仓库 * @param c 类 * @param <T> repo * @return T </T> */ @JvmStatic fun <T : BaseRepo<*>> get(c: Class<T>): T { if (instance[c.name] == null) { try { val repo = Class.forName(c.name).newInstance() as T // 通过反射获取BaseRepo的init初始化函数 try { val initMethod = c.superclass.getDeclaredMethod( "init", Context::class.java ) initMethod.isAccessible = true initMethod.invoke(repo, context) } catch (e: NoSuchMethodException) { e.printStackTrace() } catch (e: InvocationTargetException) { e.targetException.printStackTrace() } instance[c.name] = repo } catch (e: IllegalAccessException) { e.printStackTrace() } catch (e: InstantiationException) { e.printStackTrace() } catch (e: ClassNotFoundException) { e.printStackTrace() } } return instance[c.name] as T!! } } }
open class BaseRepo<D : BaseDao<*>> { /** * 该类对应的dao层对象 */ lateinit var dao: D /** * 从Room配置类中获取该dao层对象 */ @Synchronized fun init(context: Context?) { val instance = AbstractDatabase.getInstance(context!!) val entityClass = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<D> val methods = AbstractDatabase::class.java.methods for (method in methods) { if (entityClass.name.contains(method.returnType.name)) { try { dao = method.invoke(instance) as D break } catch (e: IllegalAccessException) { e.printStackTrace() } catch (e: InvocationTargetException) { e.printStackTrace() } } } } }
最初使用Kotlin时发现泛型会被转为Any类型,导致方法报错,所以这里使用Java
public interface BaseDao<T extends BaseEntity> { /** * 插入一条数据 * @param t 数据源 */ @Insert void insert(T t); /** * 插入一组数据 * @param ts 数据源 */ @Insert void insert(T... ts); /** * 插入一组数据 * @param ts 数据源 */ @Insert void insert(List<T> ts); /** * 删除一条数据 * @param t 数据源 */ @Delete void delete(T t); /** * 删除一组数据 * @param ts 数据源 */ @Delete void delete(T... ts); /** * 删除一组数据 * @param ts 数据源 */ @Delete void delete(List<T> ts); /** * 更新一条数据 * @param t 数据源 */ @Update void update(T t); /** * 更新一组数据 * @param ts 数据源 */ @Update void update(T... ts); /** * 更新一组数据 * @param ts 数据源 */ @Update void update(List<T> ts); @RawQuery List<T> query(SupportSQLiteQuery sql); @RawQuery int queryInt(SupportSQLiteQuery sql); }
最初使用Kotlin时发现泛型会被转为Any类型,导致方法报错,所以这里使用Java
public class BaseEntity { private String tag = "BaseEntity"; @Ignore public void insert() { Log.d(tag, "插入数据 " + this.toString()); Repo.get(getRepo()).dao.insert(this); } /** * 清空数据通过查询所有数据再删除实现 */ @Ignore public void clear() { Log.d(tag, "清空数据 " + this.toString()); Repo.get(getRepo()).dao.delete( Repo.get(getRepo()).dao.query(new SimpleSQLiteQuery("select * from " + getClass().getSimpleName()))); } @Ignore public int count() { Log.d(tag, "获取总数 " + this.toString()); return Repo.get(getRepo()).dao.queryInt(new SimpleSQLiteQuery("select count(1) from " + getClass().getSimpleName())); } @Ignore public void update() { Log.d(tag, "更新数据 " + this.toString()); Repo.get(getRepo()).dao.update(this); } @Ignore public void delete() { Log.d(tag, "删除数据 " + this.toString()); Repo.get(getRepo()).dao.delete(this); } @Ignore private Class getRepo() { Class c = getClass(); Class dao = null; try { dao = Class.forName("com.example.entity.repo." + c.getSimpleName() + "Repo"); } catch (ClassNotFoundException e) { e.printStackTrace(); } return dao; } }
/** * 数据类必须继承BaseEntity */ @Entity data class Teacher( /** * 唯一id,自增 */ @PrimaryKey(autoGenerate = true) val id: Int?, /** * 名字 */ @ColumnInfo var name: String = "", /** * 1 男 * 0 女 */ @ColumnInfo var gender: Int = 0 ): BaseEntity() { var tag: String = "Teacher" }
/**
* dao层必须继承BaseDao<T>,T为对应数据类
* 继承之后即可实现简单的增删改
* 查找需要手动编写查询接口,如下方get(string)
*/
@Dao
interface TeacherDao: BaseDao<Teacher> {
/**
* 获取指定姓名的老师
* @return LiveData<Teacher>
*/
@Query("select * from treacher where name=:name")
fun get(name: String): LiveData<Teacher>
}
class TeacherRepo: BaseRepo<TeacherDao>() {
private val map : HashMap<String, LiveData<Teacher>> = hashMapOf()
/**
* 获取指定姓名的老师
* @return LiveData<Teacher>
*/
fun get(name: String): LiveData<Teacher> {
if (map[name] == null) {
map[name] = dao.get(name)
}
return map[name]
}
}
// AbstractDatabase根据自己的需要参考官方文档进行编写
// dao获取方法名必须为get+类名,否则反射可能拿不到该dao对象
abstract fun getTeacherDao(): TeacherDao
// 要在子线程中使用
val teacher = Teacher(id = 1, name = "张三", gender = 0)
teacher.insert()
teacher.name = "李四"
teacher.update()
teacher.delete()
Teacher(null).clear()
Repo.get(TeacherRepo::class.java).get("张三").observe(this) {
Log.d("Teacher", it.gender.toString());
}
// com.example为对应数据module包名
-keep public class * extends com.example.entity.*
-keep class com.example.entity.*
-dontwarn com.example.entity.**
-keep class com.example.entity.** { *;}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。