Room在SQLite之上提供了一个抽象层来访问数据库,可以在充分利用SQLite强大功能的同时对数据库进行流畅的访问。
添加依赖
compile "android.arch.persistence.room:runtime:1.0.0-beta1"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0-beta1"
可能发生的错误
Error:Failed to resolve: android.arch.persistence.room:runtime:1.0.0-beta1
Error:Failed to resolve: annotationProcessor
解决办法:
往工程级别下的gradle文件里添加google的maven仓库
allprojects {
repositories {
jcenter()
maven {
url 'https://maven.google.com'
}
}
}
Tips:这里使用了RxJava2.0里的Completable以及Maybe用来处理数据库操作,以及ui更新。
为什么用Maybe不用Single?Maybe是发送0个或1个数据,而single是发送一个数据并且结果要么成功要么错误抛异常。
创建一个DAO接口,这个接口主要是提供数据库增删改查方法,编译的时候会生成一个BookDao_Impl的实现类:
BookDao.java
@Dao
public interface BookDao {
//这里使用Maybe
@Query("SELECT * FROM book")
Maybe> getAllBooks();
@Query("SELECT * FROM book where bookid = :id")
Maybe getBookById(int id);
@Insert
void insertAll(Book... books);
@Delete
void delete(Book book);
@Update
void updateBooks(Book... books);
}
Entity: 一个实体类对应一张表
Book.java
@Entity(tableName = "book")
public class Book {
@PrimaryKey(autoGenerate = true)
private int bookid;
@ColumnInfo(name = "book_name")
private String bookName;
@ColumnInfo(name = "author")
private String author;
public void setAuthor(String author) {
this.author = author;
}
public String getAuthor() {
return author;
}
public void setBookid(int bookid) {
this.bookid = bookid;
}
public int getBookid() {
return bookid;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getBookName() {
return bookName;
}
}
Database:抽象类,编译的时候会生成一个BookDatabase_Impl实现类,实现数据库和表的创建打开,并且实现bookDao()方法
BookDatabase.java
@Database(entities = {Book.class}, version = 1)
public abstract class BookDatabase extends RoomDatabase {
public abstract BookDao bookDao();
}
DatabaseCallback.java 用于更新ui和提示操作结果
public interface DatabaseCallback {
void onLoadBooks(List books);
void onAdded();
void onDeleted();
void onUpdated();
void onError(String err);
}
创建一个Manager方便我们对数据库进行管理操作:
注意:数据库的操作要在子线程中进行,不要在主线程进行操作
LocalCacheManager.java
public class LocalCacheManager {
private static LocalCacheManager _instance;
private BookDatabase db;
private static final String DB_NAME = "book-database";
public static LocalCacheManager getInstance(Context context) {
if (_instance == null) {
_instance = new LocalCacheManager(context);
}
return _instance;
}
public LocalCacheManager(Context context) {
db = Room.databaseBuilder(context, BookDatabase.class, DB_NAME).build();
}
public void getBooks(final DatabaseCallback databaseCallback) {
db.bookDao()
.getAllBooks()
//在io线程进行数据库操作
.subscribeOn(Schedulers.io())
//在主线程进行数据反馈
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer>() {
@Override
public void accept(@NonNull List books) throws Exception {
databaseCallback.onLoadBooks(books);
}
});
}
public void addBook(final DatabaseCallback databaseCallback, final Book... books) {
//这里使用Completable是因为数据库处理完后不发射数据,只处理onComplete 和 onError 事件
Completable
.fromAction(new Action() {
@Override
public void run() throws Exception {
db.bookDao().insertAll(books);
}
})
//在主线程进行反馈
.observeOn(AndroidSchedulers.mainThread())
//在io线程进行数据库操作
.subscribeOn(Schedulers.io())
.subscribe(new CompletableObserver() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onComplete() {
databaseCallback.onAdded();
}
@Override
public void onError(Throwable e) {
databaseCallback.onError(e.getMessage());
}
});
}
public void delete(final DatabaseCallback databaseCallback, final int id) {
Completable
.fromAction(new Action() {
@Override
public void run() throws Exception {
Book book = new Book();
book.setBookid(id);
db.bookDao().delete(book);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CompletableObserver() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onComplete() {
databaseCallback.onDeleted();
}
@Override
public void onError(Throwable e) {
databaseCallback.onError(e.getMessage());
}
});
}
public void getBookById(final DatabaseCallback callback, int id) {
db.bookDao().getBookById(id).observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Consumer() {
@Override
public void accept(@NonNull Book book) throws Exception {
ArrayList list = new ArrayList<>();
list.add(book);
callback.onLoadBooks(list);
}
});
}
public void updateBook(final DatabaseCallback callback, final Book... book) {
Completable.fromAction(new Action() {
@Override
public void run() throws Exception {
db.bookDao().updateBooks(book);
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CompletableObserver() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onComplete() {
callback.onUpdated();
}
@Override
public void onError(Throwable e) {
callback.onError(e.getMessage());
}
});
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener, DatabaseCallback {
TextView panel;
Button btnAdd, btnDelete, btnSelect,btnUpdate;
EditText etName, etId, etAuthor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnAdd = findViewById(R.id.add);
btnDelete = findViewById(R.id.delete);
btnSelect = findViewById(R.id.select);
etName = findViewById(R.id.name);
etId = findViewById(R.id.id);
etAuthor = findViewById(R.id.author);
panel = findViewById(R.id.panel);
btnUpdate = findViewById(R.id.update);
btnAdd.setOnClickListener(this);
btnDelete.setOnClickListener(this);
btnSelect.setOnClickListener(this);
btnUpdate.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.add:
if (etName.getText().toString().trim().length() > 0 && etAuthor.getText().toString().trim().length() > 0 && etId.getText().toString().trim().length() > 0) {
final Book book = new Book();
book.setAuthor(etAuthor.getText().toString().trim());
book.setBookName(etName.getText().toString().trim());
LocalCacheManager.getInstance(getApplicationContext()).addBook(MainActivity.this, book);
} else {
Toast.makeText(this, "Please input", Toast.LENGTH_SHORT).show();
}
break;
case R.id.delete:
if (etId.length() > 0) {
LocalCacheManager.getInstance(getApplicationContext()).delete(MainActivity.this, Integer.parseInt(etId.getText().toString().trim()));
}
break;
case R.id.select:
if (etId.length() > 0) {
LocalCacheManager.getInstance(getApplicationContext()).getBookById(MainActivity.this, Integer.parseInt(etId.getText().toString().trim()));
} else {
LocalCacheManager.getInstance(getApplicationContext()).getBooks(MainActivity.this);
}
break;
case R.id.update:
if (etId.length() > 0) {
final Book book = new Book();
book.setBookid(Integer.parseInt(etId.getText().toString().trim()));
book.setAuthor(etAuthor.getText().toString().trim());
book.setBookName(etName.getText().toString().trim());
LocalCacheManager.getInstance(getApplicationContext()).updateBook(MainActivity.this, book);
}
break;
}
}
@Override
public void onLoadBooks(List books) {
panel.setText("");
for (Book book : books) {
panel.append("\nID:" + book.getBookid() + "\nName:" + book.getBookName() + "\nAuthor:" + book.getAuthor());
}
}
@Override
public void onAdded() {
Toast.makeText(this, "Add Successfully", Toast.LENGTH_SHORT).show();
}
@Override
public void onDeleted() {
Toast.makeText(this, "Deleted Successfully", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpdated() {
Toast.makeText(this, "Updated Successfully", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(String err) {
Toast.makeText(this, err, Toast.LENGTH_SHORT).show();
}
}
运行发现警告错误:
Error: Schema export directory is not provided to the annotation processor so we cannot export the schema. You can either provide `room.schemaLocation` annotation processor argument OR set exportSchema to false.
解决办法:
在app module下的gradle文件里添加下面代码:
android {
...
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
}
}
}
...
}
相比Sqlite,Room上手非常简单,因为有关数据库以及表的创建,还有增删改查的实现,都在编译的时候自动帮你写好了,我们只需要关心在哪个线程调用哪个方法进行处理,不需要关心具体实现。但是还是建议新手有时间可以看看_Impl结尾的类里面是怎么实现的。