Room realizes unified data management - Repo

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

Keywords: Android kotlin jetpack

Added by White_Coffee on Sat, 19 Feb 2022 18:51:49 +0200