四篇文章帶你快速入門Jetpck(下)之Room,WorkManager

四篇文章帶你快速入門Jetpck(下)之Room漠烧,WorkManager

Jetpack

Jetpack 是一個(gè)由多個(gè)庫(kù)組成的套件腔剂,可幫助開發(fā)者遵循最佳做法媒区,減少樣板代碼并編寫可在各種 Android 版本和設(shè)備中一致運(yùn)行的代碼,讓開發(fā)者精力集中編寫重要的代碼掸犬。

Android Architecture Component (AAC)袜漩。

image.png

官方推薦架構(gòu)

img

請(qǐng)注意,每個(gè)組件僅依賴于其下一級(jí)的組件湾碎。例如宙攻,Activity 和 Fragment 僅依賴于視圖模型。存儲(chǔ)區(qū)是唯一依賴于其他多個(gè)類的類胜茧;在本例中粘优,存儲(chǔ)區(qū)依賴于持久性數(shù)據(jù)模型和遠(yuǎn)程后端數(shù)據(jù)源仇味。

Room

ORM:也叫對(duì)象關(guān)系映射。

將面相對(duì)象的語言和面相關(guān)系的數(shù)據(jù)庫(kù)之間建立一種映射關(guān)系雹顺,稱之為了ORM丹墨。

ORM框架的好處就是,賦予我們可以用面相對(duì)象的思維來和數(shù)據(jù)庫(kù)進(jìn)行交互嬉愧,絕大多數(shù)情況不用在和SQL語句打交道贩挣。

Android推出的ORM框架,將它加入了Jetpack中没酣,這就是我們將學(xué)習(xí)的Room王财。

Room結(jié)構(gòu)

由Entity,Dao和Database三部分組成裕便。

  • Entity:封裝實(shí)際數(shù)據(jù)的實(shí)體類绒净,每個(gè)實(shí)體類都會(huì)在數(shù)據(jù)中對(duì)應(yīng)一張表,并且表中的列是根據(jù)實(shí)體類中的字段自動(dòng)生成的偿衰。
  • Dao:Dao是數(shù)據(jù)訪問對(duì)象的意思挂疆,通常會(huì)在這里對(duì)數(shù)據(jù)庫(kù)的各項(xiàng)操作進(jìn)行封裝,在實(shí)際編程時(shí)下翎,邏輯層就不需要和底層數(shù)據(jù)庫(kù)打交道了缤言,直接和Dao層進(jìn)行交互即可。
  • Database:用于定義數(shù)據(jù)庫(kù)中的關(guān)鍵信息视事,包括數(shù)據(jù)庫(kù)的版本號(hào)胆萧,包含哪些實(shí)體類以及提供Dao層的訪問實(shí)例。

添加依賴

apply plugin: 'kotlin-kapt'
implementation 'androidx.room:room-runtime:2.2.5'
annotationProcessor "androidx.room:room-compiler:2.2.5"
kapt "androidx.room:room-compiler:2.2.5"

定義Entity

首先定義一個(gè)Entity俐东,也就是實(shí)體類跌穗。

@Entity
data class User(var firstName: String, var lastName: String, var age: Int) {

    @PrimaryKey(autoGenerate = true)
    var id: Long = 0

}

這里我們?cè)赨ser的類名上使用@Entity注解,將它聲明成了一個(gè)實(shí)體類犬性,然后在User類中添加了一個(gè)id字段瞻离,并使用@PrimaryKey注解將它設(shè)為了主鍵,再把a(bǔ)utoGenerate參數(shù)指定成true乒裆,使得主鍵的值是自動(dòng)生成的套利。

定義Dao

@Dao
interface UserDao {

    @Insert
    fun insertUser(user: User): Long

    @Update
    fun updateUser(newUser: User)

    @Query("select * from User")
    fun loadAllUsers(): List<User>

    @Query("select * from User where age > :age")
    fun loadUsersOlderThan(age: Int): List<User>

    @Delete
    fun deleteUser(user: User)

    @Query("delete from User where lastName = :lastName")
    fun deleteUserByLastName(lastName: String): Int

}

定義Database

@Database(version = 1, entities = [User::class])
abstract class AppDatabase : RoomDatabase() {

    abstract fun userDao(): UserDao

    companion object {

        private var instance: AppDatabase? = null

        @Synchronized
        fun getDatabase(context: Context): AppDatabase {
            instance?.let {
                return it
            }
            return Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_database").build().apply {
                instance = this
            }
        }
    }

}

Room的數(shù)據(jù)庫(kù)升級(jí)

數(shù)據(jù)庫(kù)接口不可能在設(shè)計(jì)好了之后就永遠(yuǎn)一成不變,隨著需求和版本的變更鹤耍,數(shù)據(jù)庫(kù)也是需要升級(jí)的肉迫。

簡(jiǎn)單模式

fallbackToDestructiveMigration會(huì)將當(dāng)前的數(shù)據(jù)庫(kù)銷毀,然后在重新創(chuàng)建稿黄,隨之而來的問題就是數(shù)據(jù)全部丟失喊衫,適合測(cè)試階段。

 Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_database").fallbackToDestructiveMigration().build()

升級(jí)模式

  1. 修改版本號(hào)杆怕。
  2. 定義升級(jí)版本的sql語句族购。
  3. 執(zhí)行語句壳贪。

示例

User

/**
 * @Entity 將類聲明成實(shí)體類
 */
@Entity
data class User(var firstName: String, var lastName: String, var age: Int) {
    /**
     * @PrimaryKey 注解將字段設(shè)置為主鍵
     * autoGenerate 為true 為主鍵的值為自動(dòng)生成
     */
    @PrimaryKey(autoGenerate = true)
    var id: Long = 0
}

UserDao

/**
 * @Dao 注解才能識(shí)別他是Dao
 * UserDao的內(nèi)部就是根據(jù)業(yè)務(wù)需求對(duì)各種數(shù)據(jù)庫(kù)操作進(jìn)行的封裝。
 * 一般指的是CRUD操作
 *
 * Room編寫SQL語句支持在編譯時(shí)動(dòng)態(tài)建材SQL語句語法
 */
@Dao
interface UserDao {

    @Insert
    fun insertUser(user: User): Long

    @Update
    fun updateUser(user: User)

    @Query("select * from User")
    fun loadAllUsers(): List<User>

    @Query("select * from User where age > :age")
    fun loadUsersOlderThan(age: Int): List<User>

    @Delete
    fun deleteUser(user: User)

    @Query("delete from User where lastName = :lastName")
    fun deleteUserByLastName(lastName: String): Int
}

AppDatabase

/**
 * @Database 聲明了數(shù)據(jù)庫(kù)的版本號(hào)以及包含哪些實(shí)體類寝杖,多個(gè)是實(shí)體類用逗號(hào)隔離開來违施。
 * 另外AppDatabase類必須繼承自RoomDatabase類,并且一定要使用abstract關(guān)鍵字聲明成抽象類
 */
@Database(version = 1, entities = [User::class])
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao

    companion object {
        private var instance: AppDatabase? = null

        @Synchronized
        fun getDatabase(context: Context): AppDatabase {
            instance?.let {
                return it
            }
            return Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_database").build().apply {
                instance = this
            }
        }
    }
}

RoomActivity

class RoomActivity : AppCompatActivity() {
    val TAG = this.javaClass.simpleName
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_room)
        val userDao = AppDatabase.getDatabase(this).userDao()
        val user1 = User("Y", "X", 22)
        val user2 = User("T", "Y", 33)
        btn_add.setOnClickListener {
            thread {
                user1.id = userDao.insertUser(user1)
                user2.id = userDao.insertUser(user2)
            }
        }
        btn_udpdate.setOnClickListener {
            thread {
                user1.age = 50
                userDao.updateUser(user1)
            }
        }
        btn_delete.setOnClickListener {
            thread {
                userDao.deleteUserByLastName("Y")
            }
        }
        btn_query.setOnClickListener {
            thread {
                for (user in userDao.loadAllUsers()) {
                    Log.d(TAG, user.toString())
                }
            }
        }
    }
}

Book

@Entity
data class Book(var name: String, var pages: Int) {
    @PrimaryKey(autoGenerate = true)
    var id: Long = 0
}

BookDao

@Dao
interface BookDao {
    @Insert
    fun insertBook(book: Book): Long

    @Query("select * from Book")
    fun loadAllBooks(): List<Book>
}

修改后的appdatabase

  1. version = 2
  2. User::class, Book::class
  3. abstract fun bookDao(): BookDao
  4. addMigrations(MIGRATION_1_2)
//變動(dòng):1
@Database(version = 2, entities = [User::class, Book::class])
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
    //變動(dòng):2
    abstract fun bookDao(): BookDao

    companion object {
    //變動(dòng):3
        val MIGRATION_1_2 = object : Migration(1, 2) {
            override fun migrate(database: SupportSQLiteDatabase) {
                database.execSQL("create table Book (id integer primary key autoincrement not null,name text not null,pages integer not null)")
            }
        }

        private var instance: AppDatabase? = null

        //變動(dòng):4
        @Synchronized
        fun getDatabase(context: Context): AppDatabase {
            instance?.let {
                return it
            }
            return Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_database").addMigrations(MIGRATION_1_2).build().apply {
                instance = this
            }
        }
    }
}

再次修改 增加 var author: String

@Entity
data class Book(var name: String, var pages: Int, var author: String) {
    @PrimaryKey(autoGenerate = true)
    var id: Long = 0
}

再次修改第三版 version =3

  1. version = 3
  2. MIGRATION_2_3
  3. addMigrations(MIGRATION_1_2, MIGRATION_2_3)
@Database(version = 3, entities = [User::class, Book::class])

val MIGRATION_2_3 = object : Migration(1, 2) {
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("alter table Book add column author text not null default 'unknown'")
    }
}

return Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_database").addMigrations(MIGRATION_1_2, MIGRATION_2_3).build().apply {
                instance = this
            }

WorkManager

WorkManager很適合用于處理一些要求定時(shí)執(zhí)行的任務(wù)瑟幕,它可以根據(jù)操作系統(tǒng)的版本自動(dòng)選擇底層是使用AlarmManager實(shí)現(xiàn)還是JobScheduler實(shí)現(xiàn)磕蒲,從而降低了我們的使用成本。

另外只盹, WorkManager還支持周期性任務(wù)辣往、鏈?zhǔn)饺蝿?wù)處理等功能,是一個(gè)非常強(qiáng)大的工具殖卑。

添加依賴

implementation 'androidx.work:work-runtime:2.4.0'

WorkManager的基本用法

WorkManager的基本用法其實(shí)非常簡(jiǎn)單站削,主要分為以下3步:

定義一個(gè)后臺(tái)任務(wù),并實(shí)現(xiàn)具體的任務(wù)邏輯孵稽。

配置該后臺(tái)任務(wù)的運(yùn)行條件和約束信息钻哩,并構(gòu)建后臺(tái)任務(wù)請(qǐng)求。

將該后臺(tái)任務(wù)請(qǐng)求傳入WorkManager的enqueue()方法中肛冶,系統(tǒng)會(huì)在合適的時(shí)間運(yùn)行。

定義后臺(tái)任務(wù)

第一步要定義一個(gè)后臺(tái)任務(wù)扯键,這里創(chuàng)建一個(gè)SimpleWorker類睦袖,代碼如下所示:

class SimpleWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
    override fun doWork(): Result {
        Log.d("SimpleWorker", "do work in SimpleWorker")
        return Result.success()
    }
}

對(duì)后臺(tái)任務(wù)進(jìn)行配置

第二步,配置后臺(tái)任務(wù)的運(yùn)行條件和約束信息荣刑,代碼如下所示:

val request = OneTimeWorkRequest.Builder(SimpleWorker::class.java)
    .setInitialDelay(5, TimeUnit.MINUTES)
    .build()

最后一步馅笙,將構(gòu)建出的后臺(tái)任務(wù)請(qǐng)求傳入WorkManager的enqueue()方法中,系統(tǒng)就會(huì)在合適的時(shí)間去運(yùn)行了厉亏,代碼如下所示:

WorkManager.getInstance(context).enqueue(request)

延時(shí)啟動(dòng)

setInitialDelay(1, TimeUnit.MINUTES)

設(shè)置標(biāo)簽

addTag("example")

取消任務(wù)

WorkManager.getInstance(this).cancelWorkById(request.id)
WorkManager.getInstance(this).cancelAllWork()
WorkManager.getInstance(this).cancelAllWorkByTag("example")

觀察任務(wù)

WorkManager.getInstance(this).getWorkInfoByIdLiveData(request.id)
    .observe(this) { workInfo ->
                when (workInfo.state) {
                    WorkInfo.State.SUCCEEDED -> {
                        Log.d(TAG, "WorkInfo.State.SUCCEEDED")
                    }
                    WorkInfo.State.FAILED -> {
                        Log.d(TAG, "WorkInfo.State.FAILED")
                    }
                    WorkInfo.State.RUNNING -> {
                        Log.d(TAG, "WorkInfo.State.RUNNING")
                    }
                    WorkInfo.State.CANCELLED -> {
                        Log.d(TAG, "WorkInfo.State.CANCELLED")
                    }
                    WorkInfo.State.ENQUEUED -> {
                        Log.d(TAG, "WorkInfo.State.ENQUEUED")
                    }
                }
           }

重復(fù)執(zhí)行任務(wù)

doWork方法要返回Result.retry().

setBackoffCriteria(BackoffPolicy.LINEAR, 1, TimeUnit.SECONDS)

示例

class WorkManagerActivity : AppCompatActivity() {
    val TAG = this.javaClass.simpleName
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_work_manager)
        btn_do.setOnClickListener {
            val request = OneTimeWorkRequest.Builder(SimpleWorker::class.java)
                .setInitialDelay(2, TimeUnit.SECONDS).addTag("example")
                .setBackoffCriteria(BackoffPolicy.LINEAR, 1, TimeUnit.SECONDS)
                .build()



            WorkManager.getInstance(this).enqueue(request)
            WorkManager.getInstance(this).getWorkInfoByIdLiveData(request.id)
                .observe(this) { workInfo ->
                    when (workInfo.state) {
                        WorkInfo.State.SUCCEEDED -> {
                            Log.d(TAG, "WorkInfo.State.SUCCEEDED")
                        }
                        WorkInfo.State.FAILED -> {
                            Log.d(TAG, "WorkInfo.State.FAILED")
                        }
                        WorkInfo.State.RUNNING -> {
                            Log.d(TAG, "WorkInfo.State.RUNNING")
                        }
                        WorkInfo.State.CANCELLED -> {
                            Log.d(TAG, "WorkInfo.State.CANCELLED")
                        }
                        WorkInfo.State.ENQUEUED -> {
                            Log.d(TAG, "WorkInfo.State.ENQUEUED")
                        }
                    }
                }
            //WorkManager.getInstance(this).cancelWorkById(request.id)
        }
        btn_cancel.setOnClickListener {
            //WorkManager.getInstance(this).cancelAllWork()
            WorkManager.getInstance(this).cancelAllWorkByTag("example")
        }
    }
}

項(xiàng)目代碼
項(xiàng)目視頻

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末董习,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子爱只,更是在濱河造成了極大的恐慌皿淋,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件恬试,死亡現(xiàn)場(chǎng)離奇詭異窝趣,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)训柴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門哑舒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人幻馁,你說我怎么就攤上這事洗鸵≡叫猓” “怎么了?”我有些...
    開封第一講書人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵膘滨,是天一觀的道長(zhǎng)甘凭。 經(jīng)常有香客問我,道長(zhǎng)吏祸,這世上最難降的妖魔是什么对蒲? 我笑而不...
    開封第一講書人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮贡翘,結(jié)果婚禮上蹈矮,老公的妹妹穿的比我還像新娘。我一直安慰自己鸣驱,他們只是感情好泛鸟,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著踊东,像睡著了一般北滥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上闸翅,一...
    開封第一講書人閱讀 49,837評(píng)論 1 290
  • 那天再芋,我揣著相機(jī)與錄音,去河邊找鬼坚冀。 笑死济赎,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的记某。 我是一名探鬼主播司训,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼液南!你這毒婦竟也來了壳猜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬榮一對(duì)情侶失蹤滑凉,失蹤者是張志新(化名)和其女友劉穎统扳,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體譬涡,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡闪幽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了涡匀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盯腌。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖陨瘩,靈堂內(nèi)的尸體忽然破棺而出腕够,到底是詐尸還是另有隱情级乍,我是刑警寧澤,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布帚湘,位于F島的核電站玫荣,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏大诸。R本人自食惡果不足惜捅厂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望资柔。 院中可真熱鬧焙贷,春花似錦、人聲如沸贿堰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽羹与。三九已至故硅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間纵搁,已是汗流浹背吃衅。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留腾誉,地道東北人捐晶。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像妄辩,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子山上,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容