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 插入操作

有三种方式可以实现插入数据。

  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,具体请点击下方链接进行下载

http://www.sqlitebrowser.org/

 

具体案例(对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>
View Code

以为是学习新的知识,所以不是太注重规定,导致 一些地方写的不是很规范。

 

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

JetPack开发笔记:Room数据库的详解与基础使用

android查询天气demo,基于mvp+kotlin+rxjava2+room+retrofit2

Android UI性能优化详解