android room详解
Posted ScottePerk
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android room详解相关的知识,希望对你有一定的参考价值。
room是官方推出的数据库映射框架。在没有room之前,比较出名的就是greendao库。既然官方都出了,还是用官方的吧。
使用room或者然后第三方框架,最好使用android 4.2以及之后的版本。因为这些新版本支持Database Inspector功能。也就是直接查看数据库的功能,以前的版本只能导出查看非常的麻烦。
可以通过View->Tool Window->Database Inspector打开这个功能。
在一些新版本中,这个功能被移动到App Inspection里面。
同时需要SDK26及以上版本。
0.踩坑指南
写的时候遇到的问题非常的多,如果大家有问题可以参考下。
1.没有创建数据库
先说一个遇到的坑,代码什么都写好了,第一次运行的时候,数据库也成功创建了。但后面再运行的时候怎么也创建不了数据库,后面发现是因为我的new Thread类没有调用start方法,根本就没有执行代码,第一次成功是因为没有写new Thread在UI线程直接报错了。虽然报错,但数据库还是创建了,这个搞了很久,还是要小心仔细,特地记录一下。
2.Database Inspector一直显示closed.
这个需要设置断点,然后调试运行,Database Inspector就能正常显示了。
3.Current JDK version 1.8 has a bug that prevents Room from being incremental
还是第一次见这种提示,直接告诉你JDK有bug。解决办法就是把JDK换成AS内置的JDK。
1.创建数据库
下面通过一个官方例子来开始学习room的使用。
首先需要引入库:
前面两个是必要的,后面的可选,配合别的库使用,需要的时候添加上就可以。
//room
def room_version="2.4.2"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
//implementation "androidx.room:room-rxjava2:$room_version"
//implementation "androidx.room:room-rxjava3:$room_version"
//implementation "androidx.room:room-guava:$room_version"
//testImplementation "androidx.room:room-testing:$room_version"
//implementation "androidx.room:room-paging:2.5.0-alpha01"
第一步:创建映射实体类
创建实体类,这个实体类就是数据库映射的接受类,对应的是数据库的user表,可以将数据库的表结构映射成Java Bean。使用注解标记相应的功能,通过名字就可以非常清楚的知道。
- @Entity 表结构实体
- @PrimaryKey 主键
- @ColumnInfo 列/字段信息
@Entity
public class User
@PrimaryKey
public int uid;
@ColumnInfo(name = "first_name")
public String firstName;
@ColumnInfo(name = "last_name")
public String lastName;
第二步:创建Dao接口
创建我们的Dao接口,这种实现方式和Retrofit是非常相似的。其实最感觉的就是SQL语句。这里我们先只关系getAll这个方法就行,语句是最简单的"SELECT * FROM user"。
对应的注解也是非常容易理解,分别对应增删改查。
- @ Query 查询
- @ Delete 删除
- @ Update 修改
- @ Insert 插入
@Dao
public interface UserDao
@Query("SELECT * FROM user")
List<User> getAll();
@Query("SELECT * FROM user WHERE uid IN (:userIds)")
List<User> loadAllByIds(int[] userIds);
@Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
"last_name LIKE :last LIMIT 1")
User findByName(String first, String last);
@Insert
void insertAll(User... users);
@Delete
void delete(User user);
第三步:创建数据库抽象接口
需要继承RoomDatabase,通过@Database指定需要创建的表。还可以指定版本。
@Database(entities = User.class, version = 1)
public abstract class AppDatabase extends RoomDatabase
public abstract UserDao userDao();
我们发现这个我们自己定义的AppDatabase还是抽象类,因为她的实现需要移除在Activity或者Fragment里面实现。通过下面的代码创建真正的实现类。到这一步,数据库,表和UserDao都已经创建好了,通过AppDatabase的userDao方法,我们就可以获取到UserDao的实现。然后就可以直接调用对应的方法实现增删改查。
//Activity中调用
AppDatabase db = Room.databaseBuilder(getApplicationContext(),
AppDatabase.class, "app.db").build();
下面是完整的测试代码,我们需要创建一个新Thread并且启动她。我们插入两条数据到user表中。
new Thread(new Runnable()
@Override
public void run()
Log.d("MainActivity", "create database");
AppDatabase db = Room.databaseBuilder(getApplicationContext(),
AppDatabase.class, "app.db").build();
UserDao userDao = db.userDao();
User user=new User(1,"Tom","Cat");
User user2=new User(2,"xiao","ming");
userDao.insertAll(user, user2);
List<User> users = userDao.getAll();
Log.d("MainActivity", "users:" + users);
).start();
如果一切正常,那么将在Database Inspector里面看到下面的内容。
2.room的增删改查实现和细节
在数据库操作中其实最重要的是SQL语句,别的操作反而是次要的。
2.1 插入操作
有三种方式可以实现插入数据。
- 使用@Insert注解声明的Dao接口方法。
下面的代码分别可以插入一个和插入多条数据,只需要传入相应的User对象。这种方式是最常用的。
@Insert()
void insert(User user);
@Insert
void insertAll(User... users);
这种方式是典型的数据库映射,也是room的这种ORM的精髓。下面的代码只是用于测试,实际项目不可能写这种代码,下面代码的最大问题是重复执行会报主键冲突错误。真实应用主键是递增或者随机的,不存在这个问题。当然测试的话,我们需要实现deleteAll方法,先把表清空了再插入。
UserDao userDao = db.userDao();
User user=new User(1,"Tom","Cat");
User user2=new User(2,"xiao","ming");
userDao.insertAll(user, user2);
2.使用@Query执行 Insert SQL语句
这种方式基本不会使用,因为违背ORM的设计理论,可能插入语句比较复杂的时候,或者出现比较奇怪问题的时候可以考虑使用。作为一种辅助手段吧。
@Query("insert into user values(1,'Tom','Cat') ")
void initTestData();
3.直接在Database Inspector使用SQL语句
这种方式在测试的时候是非常方便的。可以考虑使用。缺点非常的明显,Database Inspector每次只能执行一条SQL语句,这非常的不合理。不能同时执行多条插入语句。不知道是没有这个功能,还是我使用不正确。感觉Database Inspector功能太简单,并不好用,用来看看数据库内容还是不错的,希望将来能有所改进吧。
2.2删除操作
和插入操作类似。可以删除一条数据或者根据条件删除多条数据。
@Delete
void delete(User user);
@Query("DELETE from user where uid > 0")
void deleteAll();
UserDao userDao = db.userDao();
userDao.deleteAll();
2.3查询操作
查询专门指SELECT语句,而不是@Query注解,这个注解可以做很多事情,前面我们看到,这个注解就是用来执行SQL语句的。
@Query("SELECT * from user")
void deleteAll();
2.4更新操作
可以通过room提供的@Update注解来实现,当然也可以通过@Query直接执行SQL语句。
@Update
void updata(User user);
@Query("update user set last_name='Dog' where first_name='Tom'")
void updataByQuery();
user.setLastName("Dog");
//userDao.updataByQuery();
userDao.updata(user);
Room----Android数据库(SQLite)
1.要想使用Room首先需要在build.grade中添加依赖
1 dependencies { 2 def room_version = "2.2.2" 3 4 implementation "androidx.room:room-runtime:$room_version" 5 annotationProcessor "androidx.room:room-compiler:$room_version" // For Kotlin use kapt instead of annotationProcessor 6 7 // optional - Kotlin Extensions and Coroutines support for Room 8 implementation "androidx.room:room-ktx:$room_version" 9 10 // optional - RxJava support for Room 11 implementation "androidx.room:room-rxjava2:$room_version" 12 13 // optional - Guava support for Room, including Optional and ListenableFuture 14 implementation "androidx.room:room-guava:$room_version" 15 16 // Test helpers 17 testImplementation "androidx.room:room-testing:$room_version" 18 } 19
2.数据库可视化工具可以选择DB Browser for SQLite,具体请点击下方链接进行下载
具体案例(对Word实体进行增删改)
使用ViewModel,LiveData,Room等工具。
1.界面设计
- 上方是一个ScrollView(数据多的时候可滑动),ScrollView内有一个TextView
- 下方是四个按键,分别代表,插入、删除、清空、修改
activity_main.xml代码如下:
1 <?xml version="1.0" encoding="utf-8"?> 2 <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:app="http://schemas.android.com/apk/res-auto" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 tools:context=".MainActivity"> 8 9 <androidx.constraintlayout.widget.Guideline 10 android:id="@+id/guideline" 11 android:layout_width="wrap_content" 12 android:layout_height="wrap_content" 13 android:orientation="horizontal" 14 app:layout_constraintGuide_percent="0.6" /> 15 16 <ScrollView 17 android:layout_width="0dp" 18 android:layout_height="0dp" 19 app:layout_constraintBottom_toTopOf="@+id/guideline" 20 app:layout_constraintEnd_toEndOf="parent" 21 app:layout_constraintStart_toStartOf="parent" 22 app:layout_constraintTop_toTopOf="parent"> 23 24 <TextView 25 android:id="@+id/textView" 26 android:layout_width="match_parent" 27 android:layout_height="wrap_content" 28 android:text="TextView" 29 android:textSize="24sp" /> 30 </ScrollView> 31 32 <Button 33 android:id="@+id/buttonInsert" 34 android:layout_width="wrap_content" 35 android:layout_height="wrap_content" 36 android:text="insert" 37 app:layout_constraintBottom_toBottomOf="parent" 38 app:layout_constraintEnd_toStartOf="@+id/buttonUpdate" 39 app:layout_constraintHorizontal_bias="0.5" 40 app:layout_constraintStart_toStartOf="parent" 41 app:layout_constraintTop_toTopOf="@+id/guideline" 42 app:layout_constraintVertical_bias="0.25" /> 43 44 <Button 45 android:id="@+id/buttonUpdate" 46 android:layout_width="wrap_content" 47 android:layout_height="wrap_content" 48 android:text="update" 49 app:layout_constraintBottom_toBottomOf="@+id/buttonInsert" 50 app:layout_constraintEnd_toEndOf="parent" 51 app:layout_constraintHorizontal_bias="0.5" 52 app:layout_constraintStart_toEndOf="@+id/buttonInsert" 53 app:layout_constraintTop_toTopOf="@+id/buttonInsert" /> 54 55 <Button 56 android:id="@+id/buttonClear" 57 android:layout_width="wrap_content" 58 android:layout_height="wrap_content" 59 android:text="clear" 60 app:layout_constraintBottom_toBottomOf="@+id/buttonDelete" 61 app:layout_constraintEnd_toStartOf="@+id/buttonDelete" 62 app:layout_constraintHorizontal_bias="0.5" 63 app:layout_constraintStart_toStartOf="parent" 64 app:layout_constraintTop_toTopOf="@+id/buttonDelete" /> 65 66 <Button 67 android:id="@+id/buttonDelete" 68 android:layout_width="wrap_content" 69 android:layout_height="wrap_content" 70 android:text="delete" 71 app:layout_constraintBottom_toBottomOf="parent" 72 app:layout_constraintEnd_toEndOf="parent" 73 app:layout_constraintHorizontal_bias="0.5" 74 app:layout_constraintStart_toEndOf="@+id/buttonClear" 75 app:layout_constraintTop_toBottomOf="@+id/buttonUpdate" /> 76 </androidx.constraintlayout.widget.ConstraintLayout>
以为是学习新的知识,所以不是太注重规定,导致 一些地方写的不是很规范。
2.创建实体(Entity)
1 package com.example.roombasic; 2 3 import androidx.room.ColumnInfo; 4 import androidx.room.Entity; 5 import androidx.room.PrimaryKey; 6 7 @Entity 8 public class Word { 9 @PrimaryKey(autoGenerate = true) 10 private int id; 11 @ColumnInfo(name = "english_word") 12 private String word; 13 @ColumnInfo(name = "chinese_meaning") 14 private String chineseMeaning; 15 16 public Word(String word, String chineseMeaning) { 17 this.word = word; 18 this.chineseMeaning = chineseMeaning; 19 } 20 21 public int getId() { 22 return id; 23 } 24 25 public void setId(int id) { 26 this.id = id; 27 } 28 29 public String getWord() { 30 return word; 31 } 32 33 public void setWord(String word) { 34 this.word = word; 35 } 36 37 public String getChineseMeaning() { 38 return chineseMeaning; 39 } 40 41 public void setChineseMeaning(String chineseMeaning) { 42 this.chineseMeaning = chineseMeaning; 43 } 44 }
3.创建Dao
1 package com.example.roombasic; 2 3 import androidx.lifecycle.LiveData; 4 import androidx.room.Dao; 5 import androidx.room.Delete; 6 import androidx.room.Insert; 7 import androidx.room.Query; 8 import androidx.room.Update; 9 10 import java.util.List; 11 12 @Dao 13 public interface WordDao { 14 @Insert 15 void insertWords(Word... words); 16 17 @Update 18 void updateWords(Word... words); 19 20 @Delete 21 void deleteWords(Word... words); 22 23 @Query("DElETE FROM WORD") 24 void deleteAllWords(); 25 26 @Query("SELECT * FROM WORD ORDER BY ID DESC ") 27 LiveData<List<Word>> getAllWordsLive(); 28 }
4.创建database
1 package com.example.roombasic; 2 3 import android.content.Context; 4 5 import androidx.room.Database; 6 import androidx.room.Room; 7 import androidx.room.RoomDatabase; 8 9 @Database(entities = {Word.class}, version = 1, exportSchema = false) 10 public abstract class WordDataBase extends RoomDatabase { 11 private static WordDataBase INSTANCE; 12 13 static synchronized WordDataBase getDatabase(Context context) { 14 if (INSTANCE == null) { 15 INSTANCE = Room.databaseBuilder(context.getApplicationContext(), WordDataBase.class, "word_database").build(); 16 } 17 return INSTANCE; 18 } 19 public abstract WordDao getWordDao(); 20 }
5.创建ViewModel绑定数据
1 package com.example.roombasic; 2 3 import android.app.Application; 4 import android.os.AsyncTask; 5 6 import androidx.annotation.NonNull; 7 import androidx.lifecycle.AndroidViewModel; 8 import androidx.lifecycle.LiveData; 9 10 import java.util.List; 11 12 public class WordViewModel extends AndroidViewModel { 13 private WordDao wordDao; 14 private LiveData<List<Word>> allWordsLive; 15 16 public WordViewModel(@NonNull Application application) { 17 super(application); 18 WordDataBase wordDataBase = WordDataBase.getDatabase(application); 19 wordDao = wordDataBase.getWordDao(); 20 allWordsLive = wordDao.getAllWordsLive(); 21 } 22 23 public LiveData<List<Word>> getAllWordsLive() { 24 return allWordsLive; 25 } 26 27 void insertWords(Word... words) { 28 new InsertAsyncTask(wordDao).execute(words); 29 } 30 31 void updateWords(Word... words) { 32 new UpdateAsyncTask(wordDao).execute(words); 33 } 34 35 void deleteWords(Word... words) { 36 new DeleteAsyncTask(wordDao).execute(words); 37 } 38 39 void deleteAllWords(Word... words) { 40 new DeleteAllAsyncTask(wordDao).execute(); 41 } 42 43 static class InsertAsyncTask extends AsyncTask<Word, Void, Void> { 44 private WordDao wordDao; 45 46 public InsertAsyncTask(WordDao wordDao) { 47 this.wordDao = wordDao; 48 } 49 50 @Override 51 protected Void doInBackground(Word... words) { 52 wordDao.insertWords(words); 53 return null; 54 } 55 } 56 57 static class UpdateAsyncTask extends AsyncTask<Word, Void, Void> { 58 private WordDao wordDao; 59 60 public UpdateAsyncTask(WordDao wordDao) { 61 this.wordDao = wordDao; 62 } 63 64 @Override 65 protected Void doInBackground(Word... words) { 66 wordDao.updateWords(words); 67 return null; 68 } 69 } 70 71 static class DeleteAsyncTask extends AsyncTask<Word, Void, Void> { 72 private WordDao wordDao; 73 74 public DeleteAsyncTask(WordDao wordDao) { 75 this.wordDao = wordDao; 76 } 77 78 @Override 79 protected Void doInBackground(Word... words) { 80 wordDao.deleteWords(words); 81 return null; 82 } 83 } 84 85 static class DeleteAllAsyncTask extends AsyncTask<Void, Void, Void> { 86 private WordDao wordDao; 87 88 public DeleteAllAsyncTask(WordDao wordDao) { 89 this.wordDao = wordDao; 90 } 91 92 @Override 93 protected Void doInBackground(Void... voids) { 94 wordDao.deleteAllWords(); 95 return null; 96 } 97 } 98 }
6.在MainActivity.java中添加按键监听
1 package com.example.roombasic; 2 3 import android.os.Bundle; 4 import android.view.View; 5 import android.widget.Button; 6 import android.widget.TextView; 7 8 import androidx.appcompat.app.AppCompatActivity; 9 import androidx.lifecycle.Observer; 10 import androidx.lifecycle.ViewModelProviders; 11 12 import java.util.List; 13 14 public class MainActivity extends AppCompatActivity { 15 TextView textView; 16 Button buttonInsert, buttonDelete, buttonUpdate, buttonClear; 17 WordViewModel wordViewModel; 18 19 @Override 20 protected void onCreate(Bundle savedInstanceState) { 21 super.onCreate(savedInstanceState); 22 setContentView(R.layout.activity_main); 23 wordViewModel = ViewModelProviders.of(this).get(WordViewModel.class); 24 textView = findViewById(R.id.textView); 25 wordViewModel.getAllWordsLive().observe(this, new Observer<List<Word>>() { 26 @Override 27 public void onChanged(List<Word> words) { 28 StringBuilder text = new StringBuilder(); 29 for (int i = 0; i < words.size(); i++) { 30 Word word = words.get(i); 31 text.append(word.getId()).append(":").append(word.getWord()).append("=").append(word.getChineseMeaning()).append(" "); 32 } 33 textView.setText(text.toString()); 34 } 35 }); 36 37 buttonInsert = findViewById(R.id.buttonInsert); 38 buttonClear = findViewById(R.id.buttonClear); 39 buttonDelete = findViewById(R.id.buttonDelete); 40 buttonUpdate = findViewById(R.id.buttonUpdate); 41 42 buttonInsert.setOnClickListener(new View.OnClickListener() { 43 @Override 44 public void onClick(View v) { 45 Word word1 = new Word("Hello", "你好!"); 46 Word word2 = new Word("World", "世界!"); 47 wordViewModel.insertWords(word1,word2); 48 } 49 }); 50 51 buttonClear.setOnClickListener(new View.OnClickListener() { 52 @Override 53 public void onClick(View v) { 54 wordViewModel.deleteAllWords(); 55 } 56 }); 57 58 buttonUpdate.setOnClickListener(new View.OnClickListener() { 59 @Override 60 public void onClick(View v) { 61 Word word = new Word("Hi", "你好啊!"); 62 word.setId(9); 63 wordViewModel.updateWords(word); 64 } 65 }); 66 67 buttonDelete.setOnClickListener(new View.OnClickListener() { 68 @Override 69 public void onClick(View v) { 70 Word word = new Word("Hi", "你好啊!"); 71 word.setId(7); 72 wordViewModel.deleteWords(word); 73 } 74 }); 75 } 76 }
该实例中修改与删除使用主键进行的,以后会进行修改与完善 。
暂时只学习了一点皮毛,后续会增加一些新的内容。
以上是关于android room详解的主要内容,如果未能解决你的问题,请参考以下文章
Android Compose 新闻App网络数据Compose UI显示加载Room和DataStore使用
Android JetPack组件之Room数据库的集成与详解
如何为 Android Room 请求制作通用 AsyncTask