美文网首页
四篇文章带你快速入门Jetpck(下)之Room,WorkMan

四篇文章带你快速入门Jetpck(下)之Room,WorkMan

作者: Cache技术分享 | 来源:发表于2021-01-07 06:55 被阅读0次

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

    Jetpack

    Jetpack 是一个由多个库组成的套件,可帮助开发者遵循最佳做法,减少样板代码并编写可在各种 Android 版本和设备中一致运行的代码,让开发者精力集中编写重要的代码。

    Android Architecture Component (AAC)

    image.png

    官方推荐架构

    img

    请注意,每个组件仅依赖于其下一级的组件。例如,Activity 和 Fragment 仅依赖于视图模型。存储区是唯一依赖于其他多个类的类;在本例中,存储区依赖于持久性数据模型和远程后端数据源。

    Room

    ORM:也叫对象关系映射。

    将面相对象的语言和面相关系的数据库之间建立一种映射关系,称之为了ORM。

    ORM框架的好处就是,赋予我们可以用面相对象的思维来和数据库进行交互,绝大多数情况不用在和SQL语句打交道。

    Android推出的ORM框架,将它加入了Jetpack中,这就是我们将学习的Room。

    Room结构

    由Entity,Dao和Database三部分组成。

    • Entity:封装实际数据的实体类,每个实体类都会在数据中对应一张表,并且表中的列是根据实体类中的字段自动生成的。
    • Dao:Dao是数据访问对象的意思,通常会在这里对数据库的各项操作进行封装,在实际编程时,逻辑层就不需要和底层数据库打交道了,直接和Dao层进行交互即可。
    • Database:用于定义数据库中的关键信息,包括数据库的版本号,包含哪些实体类以及提供Dao层的访问实例。

    添加依赖

    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

    首先定义一个Entity,也就是实体类。

    @Entity
    data class User(var firstName: String, var lastName: String, var age: Int) {
    
        @PrimaryKey(autoGenerate = true)
        var id: Long = 0
    
    }
    

    这里我们在User的类名上使用@Entity注解,将它声明成了一个实体类,然后在User类中添加了一个id字段,并使用@PrimaryKey注解将它设为了主键,再把autoGenerate参数指定成true,使得主键的值是自动生成的。

    定义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的数据库升级

    数据库接口不可能在设计好了之后就永远一成不变,随着需求和版本的变更,数据库也是需要升级的。

    简单模式

    fallbackToDestructiveMigration会将当前的数据库销毁,然后在重新创建,随之而来的问题就是数据全部丢失,适合测试阶段。

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

    升级模式

    1. 修改版本号。
    2. 定义升级版本的sql语句。
    3. 执行语句。

    示例

    User

    /**
     * @Entity 将类声明成实体类
     */
    @Entity
    data class User(var firstName: String, var lastName: String, var age: Int) {
        /**
         * @PrimaryKey 注解将字段设置为主键
         * autoGenerate 为true 为主键的值为自动生成
         */
        @PrimaryKey(autoGenerate = true)
        var id: Long = 0
    }
    

    UserDao

    /**
     * @Dao 注解才能识别他是Dao
     * UserDao的内部就是根据业务需求对各种数据库操作进行的封装。
     * 一般指的是CRUD操作
     *
     * Room编写SQL语句支持在编译时动态建材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 声明了数据库的版本号以及包含哪些实体类,多个是实体类用逗号隔离开来。
     * 另外AppDatabase类必须继承自RoomDatabase类,并且一定要使用abstract关键字声明成抽象类
     */
    @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)
    //变动:1
    @Database(version = 2, entities = [User::class, Book::class])
    abstract class AppDatabase : RoomDatabase() {
        abstract fun userDao(): UserDao
        //变动:2
        abstract fun bookDao(): BookDao
    
        companion object {
        //变动: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
    
            //变动: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很适合用于处理一些要求定时执行的任务,它可以根据操作系统的版本自动选择底层是使用AlarmManager实现还是JobScheduler实现,从而降低了我们的使用成本。

    另外, WorkManager还支持周期性任务、链式任务处理等功能,是一个非常强大的工具。

    添加依赖

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

    WorkManager的基本用法

    WorkManager的基本用法其实非常简单,主要分为以下3步:

    定义一个后台任务,并实现具体的任务逻辑。

    配置该后台任务的运行条件和约束信息,并构建后台任务请求。

    将该后台任务请求传入WorkManager的enqueue()方法中,系统会在合适的时间运行。

    定义后台任务

    第一步要定义一个后台任务,这里创建一个SimpleWorker类,代码如下所示:

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

    对后台任务进行配置

    第二步,配置后台任务的运行条件和约束信息,代码如下所示:

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

    最后一步,将构建出的后台任务请求传入WorkManager的enqueue()方法中,系统就会在合适的时间去运行了,代码如下所示:

    WorkManager.getInstance(context).enqueue(request)
    

    延时启动

    setInitialDelay(1, TimeUnit.MINUTES)
    

    设置标签

    addTag("example")
    

    取消任务

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

    观察任务

    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")
                        }
                    }
               }
    

    重复执行任务

    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")
            }
        }
    }
    

    项目代码
    项目视频

    相关文章

      网友评论

          本文标题:四篇文章带你快速入门Jetpck(下)之Room,WorkMan

          本文链接:https://www.haomeiwen.com/subject/agpaoktx.html