Room realizes unified data management - Repo
In the multi module environment, the data classes are written in one module separately, and the database can be accessed for addition, deletion, modification and query only by introducing them into other classes
in use LitePal When using the framework, it is very convenient to find operations such as data insertion, which only needs object Insert () can realize data writing, but it is often used in the Room framework and depends on it to obtain LiveData objects. Therefore, I sorted out a unified management class Repo, found each Repo class in the factory mode and operated on the corresponding Dao layer
principle
Room
Google is quoted here Room Interpretation
Applications that handle large amounts of structured data can greatly benefit from retaining this data locally. The most common usage scenario is to cache relevant data, so that when the device cannot access the network, users can still browse the content offline.
The Room persistence library provides an abstraction layer on SQLite to make full use of the powerful functions of SQLite and access the database smoothly. Specifically, Room has the following advantages:
- Compile time validation for SQL queries.
- Convenient annotations that minimize duplication and error prone boilerplate code.
- Simplified database migration path.
For these reasons, we strongly recommend that you use Room instead of using SQLite API directly.
reflex
Because the module is designed in factory mode, each Repo class is obtained through reflection
Baidu Encyclopedia Reflection mechanism Explanation of
Java's reflection mechanism refers to that in the running state of the program, you can construct the object of any class, understand the class to which any object belongs, understand the member variables and methods of any class, and call the properties and methods of any object. This function of dynamically obtaining program information and dynamically calling objects is called the reflection mechanism of Java language. Reflection is regarded as the key to dynamic language.
Concrete implementation
Repo(Kotlin)
class Repo { companion object { @SuppressLint("StaticFieldLeak") var context: Context? = null /** * Save each Repo class */ private val instance: MutableMap<String, BaseRepo<*>?> = HashMap(3) /** * Use in creating an application * @param context Context object */ @JvmStatic fun init(context: Context) { Repo.context = context } /** * Get the specified data warehouse * @param c class * @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 // Get the init initialization function of BaseRepo through reflection 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!! } } }
BaseRepo(Kotlin)
open class BaseRepo<D : BaseDao<*>> { /** * dao layer object corresponding to this class */ lateinit var dao: D /** * Get the dao layer object from the Room configuration class */ @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() } } } } }
BaseDao(Java)
When Kotlin was first used, it was found that the generic type would be converted to Any type, resulting in method error, so Java is used here
public interface BaseDao<T extends BaseEntity> { /** * Insert a piece of data * @param t data source */ @Insert void insert(T t); /** * Insert a set of data * @param ts data source */ @Insert void insert(T... ts); /** * Insert a set of data * @param ts data source */ @Insert void insert(List<T> ts); /** * Delete a piece of data * @param t data source */ @Delete void delete(T t); /** * Delete a set of data * @param ts data source */ @Delete void delete(T... ts); /** * Delete a set of data * @param ts data source */ @Delete void delete(List<T> ts); /** * Update a piece of data * @param t data source */ @Update void update(T t); /** * Update a set of data * @param ts data source */ @Update void update(T... ts); /** * Update a set of data * @param ts data source */ @Update void update(List<T> ts); @RawQuery List<T> query(SupportSQLiteQuery sql); @RawQuery int queryInt(SupportSQLiteQuery sql); }
BaseEntity(Java)
When Kotlin was first used, it was found that the generic type would be converted to Any type, resulting in method error, so Java is used here
public class BaseEntity { private String tag = "BaseEntity"; @Ignore public void insert() { Log.d(tag, "insert data " + this.toString()); Repo.get(getRepo()).dao.insert(this); } /** * Clearing data is realized by querying all data and then deleting */ @Ignore public void clear() { Log.d(tag, "wipe data " + 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, "Get total " + this.toString()); return Repo.get(getRepo()).dao.queryInt(new SimpleSQLiteQuery("select count(1) from " + getClass().getSimpleName())); } @Ignore public void update() { Log.d(tag, "Update data " + this.toString()); Repo.get(getRepo()).dao.update(this); } @Ignore public void delete() { Log.d(tag, "Delete data " + 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; } }
Preparatory work (Kotlin)
Data class
/** * Data classes must inherit BaseEntity */ @Entity data class Teacher( /** * Unique id, self increasing */ @PrimaryKey(autoGenerate = true) val id: Int?, /** * name */ @ColumnInfo var name: String = "", /** * 1 male * 0 female */ @ColumnInfo var gender: Int = 0 ): BaseEntity() { var tag: String = "Teacher" }
Dao(Kotlin)
/** * dao The layer must inherit BaseDao < T >, and t is the corresponding data class * Simple addition, deletion and modification can be realized after inheritance * The query interface needs to be written manually to find, such as get(string) below */ @Dao interface TeacherDao: BaseDao<Teacher> { /** * Get the teacher with the specified name * @return LiveData<Teacher> */ @Query("select * from treacher where name=:name") fun get(name: String): LiveData<Teacher> }
Repo(Kotlin)
class TeacherRepo: BaseRepo<TeacherDao>() { private val map : HashMap<String, LiveData<Teacher>> = hashMapOf() /** * Get the teacher with the specified name * @return LiveData<Teacher> */ fun get(name: String): LiveData<Teacher> { if (map[name] == null) { map[name] = dao.get(name) } return map[name] } }
Room configuration class (Kotlin)
// AbstractDatabase is written according to its own needs with reference to official documents // dao get method name must be get + class name, otherwise reflection may not get the dao object abstract fun getTeacherDao(): TeacherDao
Specific use (Kotlin)
Addition, deletion and modification
// To use in a child thread val teacher = Teacher(id = 1, name = "Zhang San", gender = 0) teacher.insert() teacher.name = "Li Si" teacher.update() teacher.delete() Teacher(null).clear()
other
Repo.get(TeacherRepo::class.java).get("Zhang San").observe(this) { Log.d("Teacher", it.gender.toString()); }
matters needing attention
- When packaging release, add confusion to prevent reflection from missing classes
// com.example is the package name of the corresponding data module -keep public class * extends com.example.entity.* -keep class com.example.entity.* -dontwarn com.example.entity.** -keep class com.example.entity.** { *;}
- In baseentity In the getrepo () method, you must modify the package name reflected in it to the class of your project data module