2020-01-08 18:49:43

文章目录

  • Room简介
  • 添加依赖
  • 创建实体类
  • 创建DAO
  • Room database
  • 创建 Repository
  • 创建ViewModel
  • 创建页面
  • @Dao编译后的产物
  • @Database编译后生成的类
  • 调用流程

2018年谷歌I/O 发布了一系列辅助android开发者的实用工具,合称Jetpack,以帮助开发者构建出色的 Android 应用。
这次发布的 Android Jetpack 组件覆盖以下 4 个方面:Architecture、Foundation、Behavior 以及 UI。该系列博客介绍一下Jetpack中常用组件,本篇介绍Room,结合ViewModel和LiveData完成上图的结构。最后借助于https://github.com/android/sunflower 来写一个完整的应用

Room简介

原文地址:https://developer.android.google.cn/training/data-storage/room/index.html

Room 持久性库在 SQLite 的基础上提供了一个抽象层,让用户能够在充分利用 SQLite 的强大功能的同时,获享更强健的数据库访问机制。
Room 包含 3 个主要组件:

  • 数据库:包含数据库持有者,并作为应用已保留的持久关系型数据的底层连接的主要接入点。

    使用 @Database 注释的类应满足以下条件:

    • 是扩展 RoomDatabase 的抽象类。

    • 在注释中添加与数据库关联的实体列表。

    • 包含具有 0 个参数且返回使用 @Dao 注释的类的抽象方法。

    在运行时,您可以通过调用 Room.databaseBuilder() 或 Room.inMemoryDatabaseBuilder() 获取 Database 的实例。

  • Entity:表示数据库中的表。

  • DAO:包含用于访问数据库的方法。

添加依赖

选择合适的版本

// Room components
implementation "androidx.room:room-runtime:2.2.3"
annotationProcessor "androidx.room:room-compiler:2.2.3"
androidTestImplementation "androidx.room:room-testing:2.2.3"// Lifecycle components
implementation "androidx.lifecycle:lifecycle-extensions:2.1.0"
annotationProcessor "androidx.lifecycle:lifecycle-compiler:2.1.0"// UI
implementation "com.google.android.material:material:1.0.0"

创建实体类

@Entity(tableName = "word_table")
public class Word {@PrimaryKey@NonNull@ColumnInfo(name = "word")private String mWord;public Word(String word) {this.mWord = word;}public String getWord(){return this.mWord;}
}

解释一下常用的注解:

@Entity :数据表的实体类。
@PrimaryKey: 每一个实体类都需要一个唯一的标识。
@ColumnInfo :数据表中字段名称。
@Ignore :标注不需要添加到数据表中的属性。
@Embedded :实体类中引用其他实体类。
@ForeignKey:外键约束。

创建DAO

@Dao
public interface WordDao {// allowing the insert of the same word multiple times by passing a// conflict resolution strategy@Insert(onConflict = OnConflictStrategy.IGNORE)void insert(Word word);@Query("DELETE FROM word_table")void deleteAll();@Query("SELECT * from word_table ORDER BY word ASC")List<Word> getAlphabetizedWords();
}

@Dao :数据库操作的类。
@Query : 包含所有Sqlite语句操作。
@Insert : 插入操作。
@Delete : 删除操作。
@Update : 更新操作。

冲突条款 :我们在@Insert中添加的属性值。 就是针对出现**约束异常(ROLLBACK, ABORT, FAIL, IGNORE, and REPLACE)**的非标准处理。

ABORT : 默认值,不处理约束异常。
ROLLBACK: 与ABORT相似,不常用。
FAIL : 在批量更新或者修改时,中途出现了约束异常,就会终止后续执行,但会保留已执行的sql语句。
IGNORE : 忽略约束异常,不做任何处理保留原数据。
REPLACE: 当出现约束异常时,移除原数据 & 将新数据覆盖。

解释一波:

  • @Dao注解的类一定是接口或者抽象类,
  • void insert(Word word);声明一个插入的方法,不需要我们提供SQL语句,只需要用@Insert标记一下就好
  • @Query("DELETE FROM word_table"):@Query需要我们提供一下SQL语句

这里的WordDao会在编译时生成WordDAO_Impl类,

Room database

我们自己定义的Room database必须是继承自RoomDatabase的抽象类,通常我们会把该类做成单例模式

@Database(entities = {Word.class}, version = 1, exportSchema = false)
public abstract class WordRoomDatabase extends RoomDatabase {public abstract WordDao wordDao();private static volatile WordRoomDatabase INSTANCE;private static final int NUMBER_OF_THREADS = 4;static final ExecutorService databaseWriteExecutor =Executors.newFixedThreadPool(NUMBER_OF_THREADS);static WordRoomDatabase getDatabase(final Context context) {if (INSTANCE == null) {synchronized (WordRoomDatabase.class) {if (INSTANCE == null) {INSTANCE = Room.databaseBuilder(context.getApplicationContext(),WordRoomDatabase.class, "word_database").build();}}}return INSTANCE;}
}

我们使用@Database注解来声明这是一个Room database ,并且使用参数中的entities属性,声明该数据库中的映射表,可以设置多个表,同时设置数据库版本,方便之后进行升级。

创建 Repository

Repository是一个抽象的数据访问层,数据可以来源于数据库,也可以来源于网络,这一次层并不是必须的,但还是推荐有这么一层。

public class WordRepository {private WordDAO wordDAO;private LiveData<List<Word>> allWords;public WordRepository(Application application){WordRoomDatabase db = WordRoomDatabase.getDatabase(application);wordDAO = db.wordDAO();allWords = wordDAO.getAlphabetizedWords();}public LiveData<List<Word>> getAllWords() {return allWords;}public void insert(final Word word){WordRoomDatabase.databaseWriteExecutor.execute(new Runnable() {@Overridepublic void run() {wordDAO.insert(word);}});}}

创建ViewModel

public class WordViewModel extends AndroidViewModel {private WordRepository wordRepository;private LiveData<List<Word>> allWords;public WordViewModel(@NonNull Application application) {super(application);wordRepository = new WordRepository(application);allWords = wordRepository.getAllWords();}public LiveData<List<Word>> getAllWords() {return allWords;}public void insert(Word word) {wordRepository.insert(word);}
}

创建页面

页面包含一个RecyclerView,用于展示数据库保存的内容,一个button,点击后跳转新页面,添加新的Word

public class RoomWordActivity extends AppCompatActivity {private WordViewModel mWordViewModel;public static final int NEW_WORD_ACTIVITY_REQUEST_CODE = 1;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_room_word);RecyclerView recyclerView = findViewById(R.id.recyclerview);final WordListAdapter adapter = new WordListAdapter(this);recyclerView.setAdapter(adapter);recyclerView.setLayoutManager(new LinearLayoutManager(this));mWordViewModel = ViewModelProviders.of (this).get(WordViewModel.class);mWordViewModel.getAllWords().observe(this, new Observer<List<Word>>() {@Overridepublic void onChanged(@Nullable final List<Word> words) {// Update the cached copy of the words in the adapter.adapter.setWords(words);}});FloatingActionButton fab = findViewById(R.id.fab);fab.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Intent intent = new Intent(RoomWordActivity.this, NewWordActivity.class);startActivityForResult(intent, NEW_WORD_ACTIVITY_REQUEST_CODE);}});}public void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == NEW_WORD_ACTIVITY_REQUEST_CODE && resultCode == RESULT_OK) {Word word = new Word(data.getStringExtra(NewWordActivity.EXTRA_REPLY));mWordViewModel.insert(word);} else {Toast.makeText(getApplicationContext(),R.string.empty_not_saved,Toast.LENGTH_LONG).show();}}
}

这样我们当我们添加完数据之后,会在页面上显示。

@Dao编译后的产物

我们在WordDAO中添加了几天无关紧要的方法,

@Insert(onConflict = OnConflictStrategy.IGNORE)
void insert(Word word);@Update
void updateWord(Word word);@Query("DELETE FROM word_table")
void deleteAll();@Query("SELECT * from word_table ORDER BY word ASC")
LiveData<List<Word>> getAlphabetizedWords();@Query("select * from word_table where word=:word")
LiveData<Word> getWordByContent(String word);

我们来看一下编译时生成的类:


public final class WordDAO_Impl implements WordDAO {private final RoomDatabase __db;//对应着 void insert(Word word);方法private final EntityInsertionAdapter<Word> __insertionAdapterOfWord;//对应着  void updateWord(Word word);方法private final EntityDeletionOrUpdateAdapter<Word> __updateAdapterOfWord;//对应着 void deleteAll();方法private final SharedSQLiteStatement __preparedStmtOfDeleteAll;public WordDAO_Impl(RoomDatabase __db) {this.__db = __db;//和JDBC中PreparedStatement很像,差不多是一致的this.__insertionAdapterOfWord = new EntityInsertionAdapter<Word>(__db) {@Overridepublic String createQuery() {return "INSERT OR IGNORE INTO `word_table` (`word`) VALUES (?)";}@Overridepublic void bind(SupportSQLiteStatement stmt, Word value) {if (value.getWord() == null) {stmt.bindNull(1);} else {stmt.bindString(1, value.getWord());}}};//这里也是进行预编译this.__updateAdapterOfWord = new EntityDeletionOrUpdateAdapter<Word>(__db) {@Overridepublic String createQuery() {return "UPDATE OR ABORT `word_table` SET `word` = ? WHERE `word` = ?";}@Overridepublic void bind(SupportSQLiteStatement stmt, Word value) {if (value.getWord() == null) {stmt.bindNull(1);} else {stmt.bindString(1, value.getWord());}if (value.getWord() == null) {stmt.bindNull(2);} else {stmt.bindString(2, value.getWord());}}};//这里是删除所有,并没有参数,所以没有预编译语句this.__preparedStmtOfDeleteAll = new SharedSQLiteStatement(__db) {@Overridepublic String createQuery() {final String _query = "DELETE FROM word_table";return _query;}};}//这里是复写WordDAO中的方法,调用上面的预编译好的语句进行sql操作,没啥好说的;篇幅原因,//public LiveData<List<Word>> getAlphabetizedWords()//public LiveData<Word> getWordByContent(final String word)//没有抄过来@Overridepublic void insert(final Word word) {__db.assertNotSuspendingTransaction();__db.beginTransaction();try {__insertionAdapterOfWord.insert(word);__db.setTransactionSuccessful();} finally {__db.endTransaction();}}@Overridepublic void updateWord(final Word word) {__db.assertNotSuspendingTransaction();__db.beginTransaction();try {__updateAdapterOfWord.handle(word);__db.setTransactionSuccessful();} finally {__db.endTransaction();}}@Overridepublic void deleteAll() {__db.assertNotSuspendingTransaction();final SupportSQLiteStatement _stmt = __preparedStmtOfDeleteAll.acquire();__db.beginTransaction();try {_stmt.executeUpdateDelete();__db.setTransactionSuccessful();} finally {__db.endTransaction();__preparedStmtOfDeleteAll.release(_stmt);}}

@Database编译后生成的类

我们定义的WordRoomDatabase类在编译后生成了WordRoomDatabase_Impl类,我们来看一下类的内容,删除了方法的实现,只保留了方法:

public final class WordRoomDatabase_Impl extends WordRoomDatabase {private volatile WordDAO _wordDAO;private volatile ManDAO _manDAO;@Overrideprotected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {@Overrideprotected RoomOpenHelper.ValidationResult onValidateSchema(SupportSQLiteDatabase _db) {final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context).name(configuration.name).callback(_openCallback).build();final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);return _helper;}@Overrideprotected InvalidationTracker createInvalidationTracker() {final HashMap<String, String> _shadowTablesMap = new HashMap<String, String>(0);HashMap<String, Set<String>> _viewTables = new HashMap<String, Set<String>>(0);return new InvalidationTracker(this, _shadowTablesMap, _viewTables, "word_table","man");}@Overridepublic void clearAllTables() {}@Overridepublic WordDAO wordDAO() {if (_wordDAO != null) {return _wordDAO;} else {synchronized(this) {if(_wordDAO == null) {_wordDAO = new WordDAO_Impl(this);}return _wordDAO;}}}@Overridepublic ManDAO ManDAO() {if (_manDAO != null) {return _manDAO;} else {synchronized(this) {if(_manDAO == null) {_manDAO = new ManDAO_Impl(this);}return _manDAO;}}}
}

这里面我们发现了获取我们定义的两个DAO的方法,返回了DAO_Impl的实现类。

createInvalidationTracker是在创建RoomDatabase时调用的 ,代码在RoomDatabase类中

 public RoomDatabase() {mInvalidationTracker = createInvalidationTracker();}

createOpenHelper是在我们自己定义的RoomDatabase中调用Room.databaseBuilder().build()方法时,这个方法内部调用的。

调用流程

上面简单说明了调用过程中所涉及到的类,下面简单捋一下调用过程:

  1. 我们的WordRoomDatabase继承自RoomDatabase,并且在getDatabase方法中调用了Room.databaseBuilder().build()方法。这里的Room.databaseBuilder()方法返回的是RoomDatabase.Builder类型,这调用build()方法
  2. RoomDatabase.Builder.build方法中,只看最后几行,前面是对数据库进行的配置
T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);
db.init(configuration);
return db;

这里的DB_IMPL_SUFFIX是一个全局静态变量:

private static final String DB_IMPL_SUFFIX = "_Impl";
  1. 在返回数据库实例之前,调用了db.init方法,在init方法中调用了createOpenHelper方法,也就是上面

提到的WordRoomDatabase_Impl中实现的createOpenHelper方法。

至此,我们就可以调用DAO中的方法对数据库进行操作了


以上

JetPack中的Room相关推荐

  1. Android Jetpack中CameraX保存Bitmap

    先看看官方文档 图片拍摄 图片拍摄用例旨在拍摄高分辨率的优质照片,不仅提供简单的相机手动控制功能,还提供自动白平衡.自动曝光和自动对焦 (3A) 功能.调用方负责决定如何使用拍摄的照片,具体包括以下选 ...

  2. Jetpack All In Compose ?看各种Jetpack库在Compose中的使用

    Jeptack Compose 主要目的是提高 UI 层的开发效率,但一个完整项目还少不了逻辑层.数据层的配合.幸好 Jetpack 中不少组件库已经与 Compose 进行了适配,开发者可以使用这些 ...

  3. Android中jetpack讲解(详)--课外拓展知识讲解

    Android中jetpack讲解(详) Butter Knife(黄刀油) 介绍 用法 1.支持jdk1.8 2.添加butterknife依赖 3.安装butterknife插件 4.具体使用步骤 ...

  4. 深圳腾讯内部Jetpack宝典意外流出!极致经典,堪称Android架构组件的天花板

    简介 Jetpack是一套库.工具和指南,可以帮助开发者更轻松地编写优质应用.这些组件可以帮助开发者遵循最佳做法.让开发者摆脱编写样板代码的工作并简化复杂任务,以便开发者将精力集中放在所需的代码上. ...

  5. Android Jetpack组件之Hilt使用

    前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面. And ...

  6. 【Kotlin】Kotlin 中使用 ButterKnife ( 仅用于适配 Kotlin 语言 | 不推荐新项目使用 )

    文章目录 I . 特别注意 : ButterKnife 已停止维护 ( 新项目禁止使用该框架 ) II . Android Studio 中配置 Kotlin 和 ButterKnife 步骤 III ...

  7. WordPress基础教学:绝对必装的JetPack外挂

    Jetpack 介绍前言 这周的WordPress基础教学着实让StartPress工作室团队伤脑筋,我们一直在想该如何向大家介绍比新手更进阶一层的第二篇教学文,后来我们决定将Jetpack by W ...

  8. Jetpack 迁移到 androidX support MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  9. PHP Everywhere 插件中存在严重RCE,影响数千个 WordPress 站点

     聚焦源代码安全,网罗国内外最新资讯! 编译:代码卫士 WordPress 插件 "PHP Everywhere" 中存在多个严重漏洞,可导致攻击者在受影响系统上执行任意代码.该插 ...

  10. 由多个库组成的 Android Jetpack,到底有多厉害?

    序言 Jetpack 是一个由多个库组成的套件,可帮助开发者遵循最佳做法.减少样板代码并编写可在各种 Android 版本和设备中一致运行的代码,让开发者可将精力集中于真正重要的编码工作 根据官方的定 ...

最新文章

  1. 精选NLP、CV领域论文TOP10(附链接)
  2. 敏捷开发和测试中重现缺陷和验证缺陷的解决方案(2)
  3. Android移动开发之【Android实战项目】后台服务Service
  4. VC++中的char,wchar_t,TCHAR(转载)
  5. 概率分布的熵归一化(Entropy Normalization)
  6. 一种基于游戏引擎的AR模式探讨(上)
  7. Ubuntu16.04安装Python3.7及其pip3并切换为默认版本
  8. 在吗?我要讲件大事了,你绝对不知道CSDN公众号还有这个功能!错过后悔!
  9. openstack常见问题解决方法
  10. UIView动画---移动与变形
  11. FLEX 2.01 全套下载!
  12. 中兴机顶盒刷机后服务器连接失败,刷机后rec无法进入!!!求助
  13. 论文阅读_ICD编码_MSATT-KG
  14. 主板供电接口 图解安装详细过程
  15. 打出一个平行四边形的C语言程序,用汇编语言编写一个平行四边形输出程序,图形如下...
  16. 视频分类之 UCF-101 上的 CNN 方法详解
  17. dp2px px2dp
  18. Aimersoft iMusic for mac(音乐下载传输工具)
  19. 音视频技术开发周刊 | 250
  20. 牛逼c语言代码,这段c语言代码牛逼在哪?

热门文章

  1. PMI-PBA 商业分析师 认证简介
  2. mac 安装问题汇总
  3. 本地系统盘放到服务器上,如何将本地盘映射在云服务器上
  4. 爬虫 爬取百思不得姐网站
  5. 接下来是 Downward API,它的作用是:让 Pod 里的容器能够直接获取到这个 Pod API 对象本身的信息。
  6. 【OpenCV入门教程之五】 分离颜色通道多通道图像混合
  7. 混合多云时代:大型主机z15的新使命
  8. cocos2dx_lua读取unity手机游戏本地文件
  9. 魅族手机flyme服务器不响应,魅族手机你绝对不知道的小技巧,魅族Flyme系统超实用!...
  10. photoshop-photoshop记录