如何在Android中使用Realm資料庫
我們都知道使用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 。