android room 简书,[Android开发] Room的使用

416f0c55498e

Room库是对SQLite数据库的抽象,使用上更加方便高效。

它包含以下几部分:

Entity:它代表数据库中一条数据(数据库表中的一行)。

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

Database:数据库持有者,充当与应用程序持久化的、关系型的数据的底层连接的主要访问点。用@Database注解的类应满足以下条件:

1.是一个继承 RoomDatabase 的抽象类。

2.在注释中包含与数据库相关联的实体列表。

3.包含一个具有0个参数的抽象方法,并返回用@Dao注释的类。

4.在运行时,可以通过调用Room.databaseBuilder()或Room.inMemoryDatabaseBuilder()获取数据库实例。

Migration:数据库版本迁移,数据发生改变时就需要从低版本迁移到高版本。

AsyncTask:开启子线程。数据库操作比较耗时,需开启子线程。

Repository:将数据访问放到这一层,目的是与应用程序解耦。

RecyclerView、RecyclerView Adapter

文中的Demo:

一. Entity、Dao、Database、liveData

接下来使用Entity、Dao、Database来创建一个数据库工程,并结合ViewModel中的liveData。

1. 要使用room,先根据自己的需要添加相应的依赖

在应用或模块的 build.gradle 文件中添加所需工件的依赖项:

dependencies {

def room_version = "2.2.5"

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

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

// optional - RxJava support for Room

implementation "androidx.room:room-rxjava2:$room_version"

// optional - Guava support for Room, including Optional and ListenableFuture

implementation "androidx.room:room-guava:$room_version"

// optional - Test helpers

testImplementation "androidx.room:room-testing:$room_version"

}

2. 分别创建Entity、Dao、Database文件

Entity:

package com.example.roomdemo;

import androidx.room.ColumnInfo;

import androidx.room.Entity;

import androidx.room.PrimaryKey;

@Entity

public class Word {

@PrimaryKey(autoGenerate = true)

private int id;

@ColumnInfo(name = "English")

private String word;

@ColumnInfo(name = "chinese")

private String chinese;

public Word(String word, String chinese) {

this.word = word;

this.chinese = chinese;

}

public int getId() {

return id;

}

public void setId(int 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;

}

}

Dao:

package com.example.roomdemo;

import androidx.lifecycle.LiveData;

import androidx.room.Dao;

import androidx.room.Delete;

import androidx.room.Insert;

import androidx.room.Query;

import androidx.room.Update;

import java.util.List;

@Dao

public interface WordDao {

@Insert

void insertWords(Word... words);

@Update

void updateWords(Word... words);

@Delete

void deleteWords(Word... words);

@Query("DELETE FROM WORD")

void deleteAllWords();

@Query("SELECT * FROM WORD ORDER BY ID DESC")

LiveData> getAllWords();

}

Database:

package com.example.roomdemo;

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();

}

在MainActivity中的使用:

package com.example.roomdemo;

import androidx.appcompat.app.AppCompatActivity;

import androidx.lifecycle.LiveData;

import androidx.lifecycle.Observer;

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 MainActivity extends AppCompatActivity {

WordDatabase wordDatabase;

WordDao wordDao;

TextView textView;

Button buttonInsert, buttonDelete, buttonUpdate, buttonClear;

LiveData> allWordsLive;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

wordDatabase = Room.databaseBuilder(this, WordDatabase.class, "word_database").allowMainThreadQueries().build();

wordDao = wordDatabase.getWordDao();

textView = findViewById(R.id.textView);

allWordsLive = wordDao.getAllWords();

allWordsLive.observe(this, new Observer>() {

@Override

public void onChanged(List words) {

StringBuilder text = new StringBuilder();

for (Word word: words) {

text.append(word.getId()).append("、").append(word.getWord()).append(":").append(word.getChinese()).append("\n");

}

textView.setText(text);

}

});

buttonInsert = findViewById(R.id.button1);

buttonDelete = findViewById(R.id.button2);

buttonUpdate = findViewById(R.id.button3);

buttonClear = findViewById(R.id.button4);

buttonInsert.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

Word word1 = new Word("Hello", "你好");

Word word2 = new Word("World", "世界");

wordDao.insertWords(word1, word2);

}

});

buttonDelete.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

Word word = new Word("aa", "bb");

word.setId(30);

wordDao.deleteWords(word);

}

});

buttonUpdate.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

Word word = new Word("Thanks", "谢谢");

word.setId(29);

wordDao.updateWords(word);

}

});

buttonClear.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

wordDao.deleteAllWords();

}

});

}

}

界面就不说,可以看RoomLiveDataDemo。

二. AsyncTask和Repository的使用

接下来,我们将上个Demo进行如下优化:

将DataBase做成单粒。

public abstract class WordDatabase extends RoomDatabase {

private static WordDatabase INSTANCE;

static synchronized WordDatabase getDatabase(Context context) {

if (INSTANCE == null) {

INSTANCE = Room.databaseBuilder(context.getApplicationContext(), WordDatabase.class,"wordDatabase").build();

}

return INSTANCE;

}

public abstract WordDao getWordDao();

}

使用AsyncTask开启子线程,将操作数据库等耗时操作放到子线程中执行。

static class InsertAsyncTask extends AsyncTask {

private WordDao wordDao;

public InsertAsyncTask(WordDao wordDao) {

this.wordDao = wordDao;

}

@Override

protected Void doInBackground(Word... words) {

wordDao.insertWords(words);

return null;

}

}

注意:AsyncTask类作为内部类时,需用static修饰,否则会内存泄漏。

将数据操作的部分封装到repository中。ViewModel只负责管理界面上的数据,而数据的获取应该交给repository管理

完整代码可切到room_AsyncTaskAndRepository分支查看。

三. 结合RecyclerView的使用

RecyclerView需要Adapter去设置数据:

recyclerView = findViewById(R.id.recyclerView);

myAdapter1 = new MyAdapter(false);

myAdapter2 = new MyAdapter(true);

recyclerView.setLayoutManager(new LinearLayoutManager(this));

recyclerView.setAdapter(myAdapter1);

这里创建了两个adapter,一个是普通的线形列表,一个是卡片列表。recyclerView需要设置setLayoutManager,这里设置的线形布局LinearLayoutManager,如果需要网格布局,可以设置GridLayoutManager。

adapter中需要一个内部类ViewHolder,去承载子视图,我理解像iOS的contentView:

public class MyAdapter extends RecyclerView.Adapter {

ListallWords = new ArrayList<>();

boolean useCardView;

public MyAdapter(boolean useCardView) {

this.useCardView = useCardView;

}

public void setAllWords(List allWords) {

this.allWords = allWords;

}

@NonNull

@Override

public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

LayoutInflater inflater = LayoutInflater.from(parent.getContext());

View itemView;

if (useCardView) {

itemView = inflater.inflate(R.layout.cell_card,parent,false);

} else {

itemView = inflater.inflate(R.layout.cell_normal,parent,false);

}

return new MyViewHolder(itemView);

}

@Override

public void onBindViewHolder(@NonNull final MyViewHolder holder, int position) {

Word word = allWords.get(position);

holder.textViewNumber.setText(String.valueOf(position + 1));

holder.textViewEnglish.setText(word.getWord());

holder.textViewChinese.setText(word.getChinese());

holder.itemView.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

Uri uri = Uri.parse("https://www.baidu.com");

Intent intent = new Intent(Intent.ACTION_VIEW);

intent.setData(uri);

holder.itemView.getContext().startActivity(intent);

}

});

}

@Override

public int getItemCount() {

return allWords.size();

}

static class MyViewHolder extends RecyclerView.ViewHolder {

TextView textViewNumber, textViewEnglish, textViewChinese;

public MyViewHolder(@NonNull View itemView) {

super(itemView);

textViewNumber = itemView.findViewById(R.id.textViewNumber);

textViewEnglish = itemView.findViewById(R.id.textViewEnglish);

textViewChinese = itemView.findViewById(R.id.textViewChinese);

}

}

}

完整Demo,请参考上面room_recyclerView。

四. 数据库版本迁移

数据库版本迁移,简单来说,就是保证现有数据不丢失情况下,对数据库结构进行一些改动,比如添加、删除一些字段。

1. 添加字段

比如在Word中加一个test字段:

416f0c55498e

因为数据库结构已改变,重新运行会报错:

Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.

如果不在乎数据丢不丢失,可以直接用fallbackToDestructiveMigration,它会将原有数据库删除,重新创建数据库。

做数据库迁移需要:

首先要改Database版本号。

其次设置迁移策略。

如下图红色标出了Database中针对此次变更所做的修改:

416f0c55498e

2. 删除字段

删除字段,迁移时比较麻烦,sqlite没有直接的drop操作,需要以下四步操作来做迁移:

创建一个新表。

将旧表中对应的内容插入到新表中。

删除旧表。

将新表改名为旧表的名。

416f0c55498e

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值