Android Jetpack组件学习 Room

一、配置gradle

在build.gradle (Module: app)中

apply plugin: 'kotlin-kapt'
...
dependencies {
// Room components
implementation "androidx.room:room-runtime:$rootProject.roomVersion"
implementation "androidx.room:room-ktx:$rootProject.roomVersion"
kapt "androidx.room:room-compiler:$rootProject.roomVersion"
androidTestImplementation "androidx.room:room-testing:$rootProject.roomVersion"

// Lifecycle components
implementation "androidx.lifecycle:lifecycle-extensions:$rootProject.archLifecycleVersion"
kapt "androidx.lifecycle:lifecycle-compiler:$rootProject.archLifecycleVersion"
androidTestImplementation "androidx.arch.core:core-testing:$rootProject.androidxArchVersion"

// ViewModel Kotlin support
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$rootProject.archLifecycleVersion"

// Coroutines
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$rootProject.coroutines"
api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$rootProject.coroutines"
}

kotlin {
    experimental {
        coroutines "enable"
    }
}

在project的build.gradle 中添加版本号

ext {
   roomVersion = '2.1.0-alpha06'
   archLifecycleVersion = '2.1.0-alpha04'
   androidxArchVersion = '2.0.0'
   coroutines = '1.2.0'
}
2、创建实体

先创建一个简单的表
数据表
创建一个数据类

data class Word(val word: String)

为其添加注解,使其对Room数据库有意义

  • @Entity(tableName = “word_table”)
    每个@Entity类代表一个表。注释您的类声明以指示它是一个实体。如果希望它与类的名称不同,请指定表的名称。
  • @PrimaryKey
    每个实体都需要一个主键。为了简单起见,每个单词都作为自己的主键。
  • @ColumnInfo(name = “word”)
    如果希望它与成员变量的名称不同,请在表中指定列的名称。
    存储在数据库中的每个字段都必须是公共的或具有“getter”方法。
  • 可以在Room package summary reference找到完整的注释列表。
@Entity(tableName = "word_table")
class Word(@PrimaryKey @ColumnInfo(name = "word") val word: String)
3、创建DAO

什么是DAO?

  • DAO(数据访问对象在编译时验证SQL并将其与方法关联,这样你就不必再担心SQL …所有注释都很简单,如@insert
  • DAO必须是接口或抽象类。
  • 默认情况下,所有查询都必须在单独的线程上执行。
  • Room支持协程。因此,您的查询可以使用suspend修饰符进行注解,并从协序或其他suspend 函数调用。
  • Room使用DAO为代码创建一个干净的API。

实现DAO
让我们编写一个DAO,它提供查询以获取所有单词,插入单词和删除所有单词。
1.创建一个新接口WordDao并调用它。
2.对类使用注解@Dao将其标识为Room的DAO类。
3.声明一个suspend 方法来插入一个单词:suspend fun insert(word: Word)
4.使用注解@Insert。您不必提供任何SQL!(也有@Delete和@Update注解用于删除和更新一行,但是这个应用没有用到他们。)
5.声明删除所有单词的方法:fun deleteAll()。
6.删除多个实体没有方便的注解,因此使用泛型注释该方法@Query。
7.将SQL查询作为字符串参数提供给@Query注释。
@Query(“DELETE FROM word_table”)
8.创建一个方法来获得所有的返回值。
fun getAllWords(): List
9.使用SQL查询注释方法:
@Query(“SELECT * from word_table ORDER BY word ASC”)
这是完成的代码:

@Dao
interface WordDao {

    @Query("SELECT * from word_table ORDER BY word ASC")
    fun getAllWords(): List<Word>

    @Insert
    suspend fun insert(word: Word)

    @Query("DELETE FROM word_table")
    fun deleteAll()
}
4、LiveData类

当数据发生变化时,您通常需要采取一些措施,例如在UI中显示更新的数据。这意味着您必须观察数据,以便在更改时,您可以做出反应。

根据数据的存储方式,这可能很棘手。观察应用程序的多个组件之间的数据更改可以在组件之间创建明确,严格的依赖关系路径。这使得测试和调试变得困难等等。
LiveData,一个用于数据观察的生命周期库类,解决了这个问题。在方法描述中使用LiveData类型的返回值,当数据库更新时,room会生成所有必要的代码来更新LiveData。

如果独立于Room使用LiveData,您必须管理更新数据,LiveData没有公开的方法来更新存储的数据。
如果要更新存储的数据,必须使用MutableLiveData而不是LiveData,MutableLiveData类有两个公共方法setValue(T)postValue(T)
,允许您设置LiveData对象的值。通常,ViewModel中使用MutableLiveData,然后ViewModel只向观察者公开不可变的LiveData对象。

 @Query("SELECT * from word_table ORDER BY word ASC")
 fun getAllWords(): LiveData<List<Word>>
5、添加Room数据库

什么是Room

  • Room是一个位于sqlite数据库之上的数据库层。
  • Room负责处理以前使用sqliteOpenHelper处理的任务。
  • Room使用DAO向其数据库发出查询。
  • 默认情况下,为了避免较差的UI性能,Room不允许您在主线程上发出查询。当Room查询返回时LiveData,查询将在后台线程上自动运行。
  • Room提供SQLite语句的编译时检查。

您的Room数据库类必须是抽象的并扩展RoomDatabase。通常,整个应用程序只需要一个Room数据库实例。
1.创建一个public abstract 类WordRoomDatabase ,继承RoomDatabase
2.将类注释为Room数据库,声明属于数据库的实体,并设置版本号。列出实体将在数据库中创建表。
3.定义与数据库一起使用的DAO。为每个@Dao提供一个抽象的“getter”方法。

@Database(entities = [Word::class], version = 1)
public abstract class WordRoomDatabase : RoomDatabase() {
   abstract fun wordDao(): WordDao
}

4.将WordRoomDatabase设置为单例,以防止同时打开数据库的多个实例。

@Database(entities = arrayOf(Word::class), version = 1)
public abstract class WordRoomDatabase : RoomDatabase() {

   abstract fun wordDao(): WordDao

   companion object {
        @Volatile
        private var INSTANCE: WordRoomDatabase? = null

        fun getDatabase(context: Context): WordRoomDatabase {
            val tempInstance = INSTANCE
            if (tempInstance != null) {
                return tempInstance
            }
            synchronized(this) {
                val instance = Room.databaseBuilder(
                        context.applicationContext,
                        WordRoomDatabase::class.java, 
                        "Word_database"
                    ).build()
                INSTANCE = instance
                return instance
            }
        }
   }
}

修改数据库架构时,您需要更新版本号并定义迁移策略
作为一个样本,销毁和重新创建策略就足够了。但是,对于真正的应用程序,您必须实施迁移策略。请参阅了解使用Room进行迁移

6、创建Repository

Repository类抽象了对多个数据源的访问。Repository不是体系结构组件库的一部分,而是代码分离和体系结构的建议最佳实践。Repository类为应用程序其余部分的数据访问提供了API。
Repository管理查询并允许您使用多个后端。在最常见的示例中,Repository实现了决定是从网络中获取数据还是使用本地数据库中缓存的结果的逻辑。
Repository class
实现Repository
1.创建一个名为的public class WordRepository。
2.将DAO声明为构造函数中的私有属性
3.将单词列表添加为公共属性并对其进行初始化。Room在单独的线程上执行所有查询。当数据发生变化LiveData就会通知观察者。
4.将单词列表添加为公共属性并初始化它。room在单独的线程上执行所有查询。LiveData将在数据更改时通知观察者。
5.为insert()方法添加包装。您必须在非UI线程上调用它,否则您的应用程序将崩溃。Room确保您不会在主线程上执行任何长时间运行的操作,从而阻塞UI。添加@workerthread注释,以标记需要从非UI线程调用此方法。添加suspend修饰符以告诉编译器需要从协程或其他suspend 函数调用此修饰符。

class WordRepository(private val wordDao: WordDao) {

    val allWords: LiveData<List<Word>> = wordDao.getAllWords()

    @WorkerThread
    suspend fun insert(word: Word) {
        wordDao.insert(word)
    }
}
7、创建ViewModel

ViewModel向UI提供数据,并在配置更改后继续运行。充当存储库和UI之间的通信中心。还可以使用ViewModel在fragment之间共享数据。视图模型是lifecycle library的一部分。
ViewModel
ViewModel以一种生命周期意识的方式保存应用程序的UI数据,这种方式可以在配置更改后存活下来。将应用程序的UI数据与活动类和片段类分离可以让您更好地遵循单一的责任原则:activity和fragment负责将数据绘制到屏幕上,而ViewModel可以负责保存和处理UI所需的所有数据。

//创建一个名为WordViewModel的类,该类将应用程序作为参数并扩展AndroidViewModel。
class WordViewModel(application: Application) : AndroidViewModel(application) {

    //添加私有成员变量以保存对存储库的引用。
    private val repository: WordRepository
    //使用LiveData并缓存getAlphabetizedWords返回的内容有几个好处:
    //我们可以将观察者放在数据上(而不是轮询更改)并只更新
    //数据实际更改时的UI。
    //存储库通过ViewModel与UI完全分离。

    //添加私有的livedata成员变量以缓存单词列表。
    val allWords: LiveData<List<Word>>

    //创建一个init块,该块从WordRoomDatabase获取对WordDAO的引用,并基于它构造WordRepository。
    init {
        val wordsDao = WordRoomDatabase.getDatabase(application, viewModelScope).wordDao()
        repository = WordRepository(wordsDao)
        //在init块中,使用来自存储库的数据初始化allWords属性:
        allWords = repository.allWords
    }

    /**
     * 启动新协程以非阻塞方式插入数据
     * 创建一个包装insert()方法,该方法调用存储库的insert()方法。
     * 这样,insert()的实现就完全隐藏在UI中。
     * 我们希望从主线程调用insert()方法,
     * 因此我们将基于前面定义的协程范围启动一个新的协程。
     * 因为我们正在执行数据库操作,所以我们使用的是IO调度程序。
     */
    fun insert(word: Word) = viewModelScope.launch {
        repository.insert(word)
    }
}

重要提示:ViewModel不是OnSaveInstanceState()方法的替代品,因为ViewModel在进程关闭后无法生存。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值