1. 程式人生 > >如何在Android中使用Realm資料庫

如何在Android中使用Realm資料庫

我們都知道使用SQLite的本地資料庫,它在Android 開發中用於內部儲存器儲存,主要儲存本地資料,如聯絡人,電話詳細資訊等。現在我發現一個比SQLite更輕的資料庫,被稱為Realm資料庫,我想我們可以徹底放棄SQLite了。

  1. 簡單 -資料將以Object的形式儲存在資料庫中,因此可以方便地從資料庫中檢索資料。
  2. Fast - Realm資料庫比SQLite資料庫快於資料庫的查詢執行和維護。
  3. 跨平臺 - 支援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 。