一次Room在項目中的實踐

一、前言

  • 在項目中很多很多的頁面用了很多的枚舉,而產品和需求發對完發完版本這個枚舉更改過很多次。或者前端與後端更改沒有通知我們移動端,導致出現移動端前端不一樣的展示的問題,所以決定枚舉有後端返回 key,value的形式我們自己去匹配查找value展示。這樣避免了我們因爲這個問題而發版。
  • 雖然說目前服務返回了只有幾種類型的枚舉(例如:公司類型大約有15個左右,單據類型有10多個,發票類型有10個左右......)但是以後肯定還會繼續添加,還有可能會把目前項目使用的枚舉進行遷移。所以考慮到存儲到本地避免多次請求。

二、 爲啥要使用Room

  • 目前項目中使用的時MVP模式開發,如果以後改爲MVVM對於數據請求頁面緩存優化更友好(Room返回livedata使用viewmodel觀察數據變化進行更新)。

  • 在網上搜索了一下Greedao與Room對比插入和查詢,Room更佔優勢

三、基本使用

  • 導入依賴
roomRuntime = "androidx.room:room-runtime:$room_version",
roomCompiler = "androidx.room:room-compiler:$room_version",
// RxJava support for Room
roomRxjava2 = "android.arch.persistence.room:rxjava2:$room_version",
roomKapt = "androidx.room:room-compiler:2.2.5"
  • 插件
apply plugin: 'kotlin-kapt'
  • 創建表
@Entity
data class EnumData(
        @PrimaryKey
        var id: Long = 0,

        var costApplicationTypes: HashMap<String, String>? = null,

        var noCostApplicationTypes: HashMap<String, String>? = null,

        var zongbuInvoiceTitles: HashMap<String, String>? = null,

        var firmTypes: HashMap<String, String>? = null
)
  • 註解

    @Entity 需要在創建的表

    @PrimaryKey鍵 (@PrimaryKey(autoGenerate = true)設置鍵自增)

    @TypeConverters(TypeConverter::class)TypeConverter需要自己創建並寫兩個方法使用@TypeConverter標註例:

class MapTypeConverter {

    @TypeConverter
    fun stringToMap(value: String): HashMap<String, String> {
        return Gson().fromJson(value, object : TypeToken<HashMap<String, String>>() {}.type)
    }
    @TypeConverter
    fun mapToString(value: HashMap<String, String>?): String {
        return if (value == null) "" else Gson().toJson(value)
    }
}

​ 上面是map的轉換,也就是說如果你想要往數據庫中寫入Map數據需要MapTypeConverter和在其上使用@TypeConverters

  • 創建 AppDatabase
@Database(entities = [EnumData::class], version = 4, exportSchema = false)
@TypeConverters(MapTypeConverter::class)
abstract class AppDatabase : RoomDatabase() {
    abstract fun enumDataDao(): EnumDataDao

    companion object {
        @Volatile
        private var INSTANCE: AppDatabase? = null
        fun getInstance(context: Context): AppDatabase =
                INSTANCE ?: synchronized(this) {
                    INSTANCE ?: buildDatabase(context).also {
                        INSTANCE = it
                    }
                }

        private fun buildDatabase(context: Context) =
                Room.databaseBuilder(
                        context.applicationContext,
                        AppDatabase::class.java, "admin.db"
                )
                        //數據結構發生改變不加fallbackToDestructiveMigration()會引起APP崩潰。作用:更新版本之後清空數據庫
                        .fallbackToDestructiveMigration()
                        //可在主線程操作
                        .allowMainThreadQueries()
                        .build()
    }
}

@TypeConverters(MapTypeConverter::class)如還有List的數據需要添加list的Converter

  • 創建EnumDataDao
/**
 *
 * @Description Room 數據庫 增刪改查方法
 * @date 2020/9/15 4:13 PM
 * @author BryceCui
 * @Version 1.0
 *
 * 1、Completable:只有onComplete和onError方法,即只有“完成”和“錯誤”兩種狀態,不會返回具體的結果。
 * 2、Single:其回調爲onSuccess和onError,查詢成功會在onSuccess中返回結果,需要注意的是,如果未查詢到結果,即查詢結果爲空,會直接走onError回調,拋出EmptyResultSetException異常。
 * 3、Maybe:其回調爲onSuccess,onError,onComplete,查詢成功,如果有數據,會先回調onSuccess再回調onComplete,如果沒有數據,則會直接回調onComplete。
 * 4、Flowable/Observable:這是返回一個可觀察的對象,查詢的部分有變化時,都會回調它的onNext方法,沒有數據變化的話,不回調。直到Rx流斷開。
 */
@Dao
interface EnumDataDao {


    @Transaction
    @Query("SELECT * FROM enumdata ORDER BY id DESC LIMIT 1")
    fun getAllEnumData():Maybe<EnumData?>

    /**
     * OnConflictStrategy.REPLACE:衝突策略是取代舊數據同時繼續事務
     * OnConflictStrategy.ROLLBACK:衝突策略是回滾事務
     *  OnConflictStrategy.ABORT:衝突策略是終止事務
     *  OnConflictStrategy.FAIL:衝突策略是事務失敗
     *  OnConflictStrategy.IGNORE:衝突策略是忽略衝突
     */
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertEnumData(data: EnumData)


}
  • 插入使用
/**
 * 插入枚舉到數據庫
 */
fun insertEnumOnRoom(response: EnumData): Disposable? {
    return Single.fromCallable {
        AppDatabase.getInstance(AdminApplication.instance!!).enumDataDao().insertEnumData(response)
    }.subscribeOn(Schedulers.io()).observeOn(Schedulers.io()).doOnError {
        Log.e("doOnError", "doOnError")
    }.doOnSuccess {
        Log.e("doOnSuccess", "doOnSuccess")
    }.subscribe()
}
  • 查詢使用
/**
 * 從數據庫獲取所有枚舉
 */
fun getAllEnumOnRoom() {
          AppDatabase.getInstance(AdminApplication.instance!!).enumDataDao().getAllEnumData().subscribeOn(Schedulers.io()).observeOn(Schedulers.io())
                    .doOnSuccess {
                        AdminApplication.instance!!.enumData = it
                        Log.e("doOnNext", it.toString())
                    }
                    .doOnError {
                        Log.e("getAllEnum", it.toString())
                    }.doOnComplete {
                        Log.e("getAllEnum", "doOnComplete")
                    }.subscribe()
}

四、數據庫升級需要注意

  • 升級需要自己寫升級的SQL語句(都有哪些改動)
  • 自行百度

五、總結

  • 增刪改查需要自己寫SQL語句有點......

  • 對非基礎類型存儲不是很友好需要自己寫TypeConverters而寫好像一個List<T> 一種 T 需要一個。這個目前沒有找到寫一種的方法。

  • 最後 寫的不好 不喜勿噴哈!!! 但接受糾正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章