Android Room 使用案例


本內容主要介紹 Android 中使用 Room 保存數據到本地數據庫的方法。

 以下是 Android Room 的官方介紹文檔:

Room Persistence Library
(Room 庫的簡單介紹) https://developer.android.com/topic/libraries/architecture/room
Save data in a local database using Room
(Room 的使用指南) https://developer.android.com/training/data-storage/room/
Android Room with a View - Java
(Room 的使用實例) https://codelabs.developers.google.com/codelabs/android-room-with-a-view/#0

一、簡介
  Room 是一個對象關系映射(ORM)庫。可以很容易將 SQLite 表數據轉換為 Java 對象。Room 在編譯時檢查 SQLite 語句。

  Room 為 SQLite 提供一個抽象層,以便在充分利用 SQLite 的同時,可以流暢地進行數據庫訪問。

1.1 添加依賴

 在build.gradle 添加依賴

dependencies {

    def room_version = "2.2.0-beta01"

    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version"

}

然后再在build.gradle 添加如下,不然build不過:

android {
    ...
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [
                    "room.schemaLocation":"$projectDir/schemas".toString(),
                    "room.incremental":"true",
                    "room.expandProjection":"true"]
            }
        }
    }
}

1.2 Room 組件
  Room 有 3 個主要的組件:

Database:包含數據庫持有者,並作為與 App 持久關聯數據的底層連接的主要訪問點。

用 @Database 注解的類應滿足以下條件:

是一個繼承至 RoomDatabase 的抽象類。
在注解中包含與數據庫相關聯的實體列表。
包含一個具有 0 個參數的抽象方法,並返回用 @Dao 注解的類。
在運行時,您可以通過調用 Room.databaseBuilder() 或 Room.inMemoryDatabaseBuilder() 獲取 Database 實例。

Entity:表示數據庫內的表(Table)。

DAO:包含用於訪問數據庫的方法。

1.3 Room 各組件間關系
  Room 的大致使用方法如下:

App 通過 Room 的 Database 獲取與數據庫相關的數據庫訪問對象(DAO)。
然后,App 使用 DAO 從數據庫中獲取 Entity,並且將 Entity 的變化保存到數據庫中。
最后,APP 使用 Entity 獲取和設置數據庫中表的數據。
  Room 中各組件之間的關系如圖-1 所示:

二、Entity(實體)
  在使用 Room 持久化庫(Room persistence library)時,需要將相關字段集定義為 Entity。對於每一個 Entity,在與其相關的 Database 對象中會創建一個表(Table)。

必須通過 Database 類的 entities 數組引用這個 Entity 類。

  下面的代碼片段展示如何定義 Entity:

package com.example.jetpackdemo;

import androidx.room.Entity;
import androidx.room.PrimaryKey;

@Entity
public class Word {

    @PrimaryKey(autoGenerate = true)
    private  Integer id;

    private String word;

    private String chinese;

    public Word(String word, String chinese) {
        this.word = word;
        this.chinese = chinese;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getWord() {
        return word;
    }

    public void setWord(String word) {
        this.word = word;
    }

    public String getChinese() {
        return chinese;
    }

    public void setChinese(String chinese) {
        this.chinese = chinese;
    }

    @Override
    public String toString() {
        return "Word{" +
                "id=" + id +
                ", word='" + word + '\'' +
                ", chinese='" + chinese + '\'' +
                '}';
    }
}

法。在提供 getter 和 setter 方法時,需要遵守 Room 中的 JavaBeans 協議。

2.1 設置 Table 名稱
  Room 默認使用類名作為數據庫的 Table 名稱。可以通過 @Entity 的 tableName 屬性設置 Table 的名稱。

(注意:在 SQLite 中,Table 名稱是不區分大小寫的。)

@Entity(tableName = "word")
public class Word { }

三、DAO(Data access object)
  在 Room 持久化庫中,使用數據訪問對象(data access objects, DAOs)訪問 App 的數據。Dao 對象集合是 Room 的主要組件,因為每個 DAO 提供訪問 App 的數據庫的抽象方法。

  通過使用 DAO 訪問數據庫,而不是通過查詢構造器或直接查詢,可以分離數據庫架構的不同組件。此外,在測試應用時,DAOs 可以輕松模擬數據庫訪問。

  DAO 可以是接口(interface),也可以是抽象類(abstract class)。如果是一個抽象類,可以有一個構造函數,其只接收一個 RoomDatabase 參數。在編譯時,Room 為每個 DAO 創建具體實現。

package com.example.jetpackdemo;

import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;

import java.util.List;

@Dao
public interface WordDao {

    @Insert
    void insert(Word...words);

    @Update
    void update(Word... words);

    @Query("delete from word")
    void deleteAll();

    @Query("select * from word")
    List<Word> findAll();

}

注意:除非在構造器上調用 allowMainThreadQueries(),否則 Room 不支持在主線程上進行數據庫訪問,因為它可能會長時間鎖定 UI。不過異步查詢(返回 LiveData 或 Flowable 實例的查詢)不受此規則約束,因為它們在需要時會在后台線程進行異步查詢。

3.4.2 帶參數的查詢

  大多數情況下,需要將參數傳遞到查詢中以執行篩選操作,例如僅需要顯示大於某一年齡的 User。這時,我們可以使用方法參數。

@Dao
public interface MyDao {
    @Query("SELECT * FROM user WHERE age > :minAge")
    public User[] loadAllUsersOlderThan(int minAge);
}

在編譯時,Room 使用 minAge 方法參數匹配 :minAge 綁定參數。如果存在匹配錯誤,將出現編譯錯誤。

  還可以在查詢中傳遞多個參數或者多次引用它們。

@Dao
public interface MyDao {
    @Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge")
    public User[] loadAllUsersBetweenAges(int minAge, int maxAge);

    @Query("SELECT * FROM user WHERE first_name LIKE :search " +
           "OR last_name LIKE :search")
    public List<User> findUserWithName(String search);
}

四、Database

  在 Room 持久化庫中,通過 @Database 類訪問數據庫。

4.1 定義 Database

  下面的代碼片段展示如何定義 Database:

package com.example.jetpackdemo;

import androidx.room.Database;
import androidx.room.RoomDatabase;

@Database(entities = {Word.class},version = 1, exportSchema = false)
public abstract class WordDatabase extends RoomDatabase {
    public abstract WordDao getWordDao();
}

五、測試案例

創建一個RoomActivity

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".RoomActivity">

    <ScrollView
        android:id="@+id/scrollView2"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        app:layout_constraintBottom_toTopOf="@+id/guideline2"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" />
    </ScrollView>

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.6" />

    <Button
        android:id="@+id/button5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:text="@string/btn5"
        app:layout_constraintBottom_toTopOf="@+id/guideline3"
        app:layout_constraintEnd_toStartOf="@+id/guideline4"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/guideline3" />

    <Button
        android:id="@+id/button6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:text="@string/btn6"
        app:layout_constraintBottom_toTopOf="@+id/guideline3"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="@+id/guideline4"
        app:layout_constraintTop_toTopOf="@+id/guideline3" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.75" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.5" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.875513" />

    <Button
        android:id="@+id/button7"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/btn8"
        app:layout_constraintBottom_toTopOf="@+id/guideline5"
        app:layout_constraintEnd_toStartOf="@+id/guideline4"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/guideline5" />

    <Button
        android:id="@+id/button8"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:text="@string/btn9"
        app:layout_constraintBottom_toTopOf="@+id/guideline5"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="@+id/guideline4"
        app:layout_constraintTop_toTopOf="@+id/guideline5" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:text="TextView"
        app:layout_constraintBottom_toBottomOf="@+id/scrollView2"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
package com.example.jetpackdemo;

import androidx.appcompat.app.AppCompatActivity;
import androidx.room.Insert;
import androidx.room.Room;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.util.List;

public class RoomActivity extends AppCompatActivity {

    private WordDatabase wordDatabase;
    private WordDao wordDao;
    private Button btn5,btn6,btn7,btn8;
    private TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_room);

        wordDatabase = Room.databaseBuilder(this,WordDatabase.class,"word_database").allowMainThreadQueries().build();
        wordDao = wordDatabase.getWordDao();
        btn5 = findViewById(R.id.button5);
        btn6 = findViewById(R.id.button6);
        btn7 = findViewById(R.id.button7);
        btn8 = findViewById(R.id.button8);

        tv = findViewById(R.id.textView);

        btn5.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Word word1 = new Word("hello","你好");
                Word word2 = new Word("world","世界");

                wordDao.insert(word1,word2);
                show();
            }
        });

        btn6.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                show();
            }
        });

        btn7.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Word word = new Word("hi","你好");
                word.setId(5);
                wordDao.update(word);
                show();
            }
        });

        btn8.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                wordDao.deleteAll();
                show();
            }
        });

    }

    private void show()
    {
        List<Word> list = wordDao.findAll();
        StringBuilder sb =new StringBuilder();
        for (Word w : list){
            sb.append(w.toString()+"\n");
        }
        tv.setText(sb.toString());
    }

}

 六、效果:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM