我们都知道使用SQLite的本地数据库,它在Android 开发中用于内部存储器存储,主要存储本地数据,如联系人,电话详细信息等。现在我发现一个比SQLite更轻的数据库,被称为Realm数据库,我想我们可以彻底放弃SQLite了。
- 简单 -数据将以Object的形式存储在数据库中,因此可以方便地从数据库中检索数据。
- Fast - Realm数据库比SQLite数据库快于数据库的查询执行和维护。
- 跨平台 - 支持iOS和Android。。
一些注释
@PrimaryKey:
此注释定义该字段设置为主键,且不能为空。
@Required:
此注释可用于告诉Realm强制执行检查以禁止空值。
@Ignore:
此注释用于不应该保留到磁盘的字段。
@Index:
此注释将向该字段添加搜索索引。
注意
Realm主键是不支持自动增量的。您只能通过手动递增主键来突破此限制。也就是说每一次增加数据对象的时候,都必须为它添加一个主键值,这样才会产生新的数据,否则Realm数据库中的数据将会只有一条!!
比如在该实例中,我设置title为主键,那么每次添加数据都会setTitle()进一个值!
Realm数据库如何保存数据?
Realm数据库通过将Java对象直接保存到磁盘作为对象,而不是首先将它们映射到其他数据类型(如SQLite)来工作。在Realm数据库的中心,这个叫Realm的东西,这相当于一个传统的数据库。Realm可以将不同类型的对象映射到磁盘上的一个文件。另一种查看方式是,Realm是不需要从Java对象到磁盘上持久版本的单独映射的数据库。
它是一种像你所看到的是什么保存的工作流程 - 如果对象是Realm管理对象,则用户界面中对象的更改将自动保存到数据库。Realm管理的对象与SQLite表相当。要使Java对象成为Realm管理,该类必须扩展RealmObject或实现RealmModel接口。
我们来看看我们如何在我们的应用程序中实现Realm数据库。我们将要实现的效果如下图所示:
配置:将Realm库添加到您的项目中
在build.gradle(Project)文件中为Realm数据库添加依赖关系:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.2'
classpath 'io.realm:realm-gradle-plugin:1.1.1'
}
}
allprojects {
repositories {
jcenter()
maven { url "https://jitpack.io" }
}
}
在打开您的应用程序级build.gradle(app)并应用Realm插件并安装一些库:
apply plugin: 'com.android.application'
apply plugin: 'realm-android'
// other stuff commented out for brevity
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.1.1'
compile 'com.android.support:design:24.1.1'
compile 'com.android.support:recyclerview-v7:24.1.1'
compile 'com.github.thorbenprimke:realm-recyclerview:0.9.23'
}
这里我用了一个非常棒的名为RealmRecyclerView的库,它是一个用作数据存储的RecyclerView 包装器,并且支持下拉刷新和滑动删除,省去了很多工作。
用户界面
fragment_wanted_read_layout.xml:
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<co.moonmonkeylabs.realmrecyclerview.RealmRecyclerView
android:id="@+id/want_read_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:rrvIsRefreshable="false"
app:rrvLayoutType="LinearLayout"
app:rrvSwipeToDelete="true"
app:rrvEmptyLayoutId="@layout/empty_view"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true">
</co.moonmonkeylabs.realmrecyclerview.RealmRecyclerView>
</android.support.design.widget.CoordinatorLayout>
RecyclerView中的每个项目 都会单独显示,并且填充自己的布局。现在,我们应该创建布局以显示图书(Book)的项目,其中包含Book的名称,作者,页数和图像。我们通过 在app / res / layout中创建一个 名为note_item.xml的新资源文件来创建此布局,并将其内容替换为:
want_read_item_layout.xml
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/card_books"
style="@style/AppTheme.Card.Margins"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/image_background"
android:layout_width="130dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start" />
<LinearLayout
android:id="@+id/layout_partner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="@dimen/margin_normal">
<TextView
android:id="@+id/text_books_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/margin_small"
android:paddingBottom="@dimen/margin_normal"
android:paddingLeft="@dimen/margin_large"
android:paddingRight="@dimen/margin_large"
android:paddingTop="@dimen/margin_large"
android:textSize="@dimen/text_size_large"
android:textColor="#555555"
android:maxLines="2"
android:textStyle="bold"
android:text="万历十五年"/>
<TextView
android:id="@+id/text_books_author"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/margin_large"
android:paddingRight="@dimen/margin_large"
android:textSize="@dimen/text_size_normal"
android:textStyle="italic"
android:text="黄宗仁"/>
<TextView
android:id="@+id/text_books_pages"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/margin_small"
android:paddingBottom="@dimen/margin_normal"
android:paddingLeft="@dimen/margin_large"
android:paddingRight="@dimen/margin_large"
android:paddingTop="@dimen/margin_large"
android:textSize="@dimen/text_size_normal"
android:text="999页"/>
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
我想我们现在应该通过按下Run 按钮来确定一切都正常运行。
创建数据库表
没有像Realm数据库表这样的东西,但是我们有Realm管理的对象。使用短语“创建表”是因为这是我们熟悉的说法。所以为了创建表或自动更新对象,我们需要做的就是确保我们的模型类都从RealmObject基类扩展。而在我们这样做的时候,我们可以利用Realm的功能,如轻松处理关系,以便在我们的应用程序中定义一对多的关系,例如学生和注册账号之间的关系。一个学生可以有很多注册账号,但一个注册只能有一个这样的学生。
我们需要让我们将图书列表展示到我们的Activity/Fragment,这需要我们将图书信息存储到数据库中。但首先我们需要告诉我们的应用程序我们想要存储什么。
创建一个名为Book.java的新Java类。此类将包含我们的数据的属性以及Realm数据库表的主键如ID或者Title:
public class Book extends RealmObject{
private int id;
@PrimaryKey
private String title;
private String description;
private String author;
private String imageUrl;
private String pages;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public String getPages() {
return pages;
}
public void setPages(String pages) {
this.pages = pages;
}
}
管理Realm
我们最好把有关数据库的操作都放在一个类中进行管理,就像SQlite的帮助类一样:
RealmController .class
public class RealmController {
private static String TAG = "RealmManager";
private static RealmConfiguration realmConfiguration;
private static Realm realm;
public static Realm getSaveRealm() {
return getRealm();
}
public static Realm getRealm() {
try {
if (realm == null) {
realm = Realm.getDefaultInstance();
}
} catch (Exception e) {
e.printStackTrace();
Realm.deleteRealm(realmConfiguration);
realm = Realm.getDefaultInstance();
}
return realm;
}
/**
* 初始化Realm Configuration
*/
public static void initializeRealmConfig(Context appContext) {
if(realmConfiguration == null) {
Log.d(TAG, "Initializing Realm configuration.");
setRealmConfiguration(new RealmConfiguration.Builder(appContext).initialData(new RealmInitialData())
.deleteRealmIfMigrationNeeded()
.build());
}
}
/**
* 设置Realm Configuration
*/
public static void setRealmConfiguration(RealmConfiguration realmConfiguration){
RealmController.realmConfiguration = realmConfiguration;
realm.setDefaultConfiguration(realmConfiguration);
}
private static int activityCount = 0;
/**
* 添加数据,当数据有的时候就会更新
*
* @param realObject
* @return
*/
public static void add(final RealmObject realObject) {
Realm realm = RealmController.getSaveRealm();
realm.executeTransactionAsync(new Realm.Transaction() {
public void execute(Realm realm) {
if (realObject != null) {
realm.insertOrUpdate(realObject);
}
}
});
}
/**
* 添加数据List,当数据有的时候就会更新
*/
public static void addAll(final List<? extends RealmObject> realObjects) {
if (!CollectionUtils.isEmpty(realObjects)) {
Realm realm = RealmController.getSaveRealm();
realm.executeTransaction(new Realm.Transaction() {
public void execute(Realm realm) {
realm.insertOrUpdate(realObjects);
}
});
}
}
/**
* 得到对应的本地数据的数量
*
* @param className
* @return
*/
public static long RealmDataCount(Class className) {
Realm realm = RealmController.getSaveRealm();
long count = realm.where(className).count();
return count;
}
/**
* 删除某个表
*/
public static void deleteDB(final Class className) {
Realm realm = RealmController.getSaveRealm();
realm.executeTransactionAsync(
new Realm.Transaction() {
public void execute(Realm realm) {
realm.where(className).findAll().clear();
}
}
);
}
/**
* 据id来删除指定db下的某条数据
* @param className
*/
public static void deleteById(final Class className,final long id) {
Realm realm = RealmController.getSaveRealm();
realm.executeTransactionAsync(
new Realm.Transaction() {
public void execute(Realm realm) {
realm.where(Book.class).equalTo("id",id).findFirst().deleteFromRealm();
}
}
);
}
/**
* 获取到指定db下的所有数据
*
* @return
*/
public static List<? extends RealmObject> findAll(Class className) {
Realm realm = RealmController.getRealm();
RealmResults objects = realm.where(className).findAllAsync();
return objects;
}
/**
* 根据id获得该条数据
*/
public static RealmObject findById(Class className,long id) {
Realm realm = RealmController.getRealm();
RealmObject object;
object = (RealmObject) realm.where(className).equalTo("id", id).findFirst();
return object;
}
/**
* 用于异步线程的realm 关闭
*
* @param realm
*/
public static void closeReadRealm(Realm realm) {
if (realm != null && !realm.isClosed() ) {
realm.close();
}
}
}
注释应该都写得很清楚,而且我们还为Realm数据库预先设置了5个数据:
RealmInitialData.java
ublic class RealmInitialData implements Realm.Transaction {
@Override
public void execute(Realm realm) {
Book book = new Book();
book.setId((int) (1 + System.currentTimeMillis()));
book.setAuthor("Reto Meier");
book.setTitle("Android 4 Application Development");
book.setImageUrl("https://api.androidhive.info/images/realm/1.png");
book.setPages("666页");
realm.insertOrUpdate(book);
book = new Book();
book.setId((int) (2 + System.currentTimeMillis()));
book.setAuthor("Itzik Ben-Gan");
book.setTitle("Microsoft SQL Server 2012 T-SQL Fundamentals");
book.setImageUrl("https://api.androidhive.info/images/realm/2.png");
book.setPages("111页");
realm.insertOrUpdate(book);
book = new Book();
book.setId((int) (3 + System.currentTimeMillis()));
book.setAuthor("Magnus Lie Hetland");
book.setTitle("Beginning Python: From Novice To Professional Paperback");
book.setImageUrl("https://api.androidhive.info/images/realm/3.png");
book.setPages("222页");
realm.insertOrUpdate(book);
book = new Book();
book.setId((int) (4 + System.currentTimeMillis()));
book.setAuthor("Chad Fowler");
book.setTitle("The Passionate Programmer: Creating a Remarkable Career in Software Development");
book.setImageUrl("https://api.androidhive.info/images/realm/4.png");
book.setPages("333页");
realm.insertOrUpdate(book);
book = new Book();
book.setId((int) (5 + System.currentTimeMillis()));
book.setAuthor("Yashavant Kanetkar");
book.setTitle("Written Test Questions In C Programming");
book.setImageUrl("https://api.androidhive.info/images/realm/5.png");
book.setPages("444页");
realm.insertOrUpdate(book);
}
public int hashCode(){
return RealmInitialData.class.hashCode();
}
public boolean equals(Object obj){
return obj!=null&&obj instanceof RealmInitialData;
}
}
我们使用这个语句来为Realm初始化数据:
new RealmConfiguration.Builder(appContext).initialData(new RealmInitialData())
运用Realm
我们在代码中只需要在需要的地方调用Realm管理类的方法就可以了。
首先在OnCreate()方法中定义Realm:
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RealmController.initializeRealmConfig(getActivity().getApplicationContext());
setHasOptionsMenu(true);
setRetainInstance(true);
realm = RealmController.getRealm();
if(savedInstanceState == null) {
Toast.makeText(getContext(), "Press card item for edit, long press to remove item", Toast.LENGTH_LONG).show();
}
Log.i("oncreate","onCreate");
}
在添加数据的时候调用方法:
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
// check if the request code is same as what is passed here it is 2
if(requestCode==2)
{
List<FileMsgModel> txtFiles = (List<FileMsgModel>) data.getSerializableExtra("MESSAGE");
List<Book> books = new ArrayList<>();
for(int i = 0;i<txtFiles.size();i++){
Book book = new Book();
FileMsgModel fsm = txtFiles.get(i);
book.setTitle(fsm.getFileName());
book.setPages(String.valueOf(Integer.parseInt(fsm.getFileSize())/4*4096/1000));
book.setAuthor("佚名");
book.setImageUrl("https://img1.doubanio.com/lpic/s1790028.jpg");
books.add(book);
}
RealmController.addAll(books);
setupRecycler();
}
}
设置适配器Adapter的时候从数据库中获得存储的数据:
private void setupRecycler() {
RealmResults<Book> notes = realm.where(Book.class).findAll();
adapter = new BooksAdapter(getContext(), notes);
recycler.setAdapter(adapter);
}
适配器
我们对数据进行的操作是点击编辑数据,长按删除数据:
创建一个适配器 ,该适配器将作为我们的数据集和我们的Activity之间的桥梁。在MainActivity中创建一个 名为BooksAdapter的新内部类 ,继承了RealmBasedRecyclerViewAdapter。
普通的RecyclerView只需要继承RealmRecyclerViewAdapter,这个开源库需要继承RealmBasedRecyclerViewAdapter。
public class BooksAdapter extends RealmBasedRecyclerViewAdapter<Book,BooksAdapter.CardViewHolder> {
public BooksAdapter(Context context, RealmResults<Book> data) {
super(context, data,true,false);
this.mContext = context;
}
@Override
public CardViewHolder onCreateRealmViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.want_read_item_layout,viewGroup,false);
return new CardViewHolder(view);
}
@Override
public void onBindRealmViewHolder(CardViewHolder cardViewHolder, int i) {
final Book book = realmResults.get(i);
if(book==null){
return;
}else{
cardViewHolder.bind(book);
}
}
/**
* 并且也需要继承RealmViewHolder
*/
public class CardViewHolder extends RealmViewHolder{
private final Context context;
public CardView card;
public TextView textTitle;
public TextView textAuthor;
public TextView textPages;
public ImageView imageBackground;
public CardViewHolder(View itemView) {
super(itemView);
this.context = itemView.getContext();
card = (CardView) itemView.findViewById(R.id.card_books);
textTitle = (TextView) itemView.findViewById(R.id.text_books_title);
textAuthor = (TextView) itemView.findViewById(R.id.text_books_author);
textPages = (TextView) itemView.findViewById(R.id.text_books_pages);
imageBackground = (ImageView) itemView.findViewById(R.id.image_background);
}
public void bind(final Book book){
final long id = book.getId();
textTitle.setText(book.getTitle());
textAuthor.setText(book.getAuthor());
textPages.setText(book.getPages());
if(book.getImageUrl()!=null){
Glide.with(mContext)
.load(book.getImageUrl().replace("https","http"))
.asBitmap()
.fitCenter()
.into(imageBackground);
}
card.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
RealmController.deleteById(Book.class,id);
notifyDataSetChanged();
Toast.makeText(mContext, book.getTitle() + " is removed from Realm", Toast.LENGTH_SHORT).show();
return false;
}
});
card.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View content = inflater.inflate(R.layout.edit_item,null);
final EditText editTitle = (EditText) content.findViewById(R.id.title);
final EditText editAuthor = (EditText) content.findViewById(R.id.author);
final EditText editImagethum = (EditText) content.findViewById(R.id.thumbnail);
final EditText editPages = (EditText) content.findViewById(R.id.pages);
editTitle.setText(book.getTitle());
editAuthor.setText(book.getAuthor());
editImagethum.setText(book.getImageUrl());
editPages.setText(book.getPages());
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setView(content)
.setTitle("Edit Book")
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
Book sari = (Book) RealmController.findById(Book.class,id);
RealmController.getRealm().beginTransaction();
if (sari == null) {
Book book = RealmController.getRealm().createObject(Book.class);
} else {
sari.setTitle(editTitle.getText().toString());
sari.setAuthor(editAuthor.getText().toString());
sari.setPages(editPages.getText().toString());
sari.setImageUrl(editImagethum.getText().toString());
}
RealmController.getRealm().commitTransaction();
notifyDataSetChanged();
}
})
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
});
}
}
}
实际上跟继承普通的RecyclerViewAdapter
没什么不同。
最后别忘了关闭Realm数据库
@Override
public void onDestroy() {
super.onDestroy();
RealmController.closeReadRealm(realm);
}
}
如果需要滑动删除,别忘了当我们创建我们的布局并设置app:rrvSwipeToDelete为true 。