Android使用Realm數(shù)據(jù)庫實現(xiàn)App中的收藏功能(代碼詳解)
前 言
App數(shù)據(jù)持久化功能是每個App必不可少的功能,而Android最常用的數(shù)據(jù)持久化方式主要有以下的五種方式:
- 使用SharedPreferences存儲數(shù)據(jù);
- 文件存儲數(shù)據(jù);
- SQLite數(shù)據(jù)庫存儲數(shù)據(jù);
- 使用ContentProvider存儲數(shù)據(jù);
- 網(wǎng)絡(luò)存儲數(shù)據(jù)。
其中前四種都是緩存數(shù)據(jù)到本地,這篇主要講的是使用第三種方式來實現(xiàn)App中的收藏功能,不過不用Android原生自帶SQLite數(shù)據(jù)庫來存儲數(shù)據(jù),而是使用第三方的Realm數(shù)據(jù)庫來來存儲數(shù)據(jù)。
Realm 本質(zhì)上是一個嵌入式數(shù)據(jù)庫,他并不是基于SQLite所構(gòu)建的。它擁有自己的數(shù)據(jù)庫存儲引擎,可以高效且快速地完成數(shù)據(jù)庫的構(gòu)建操作。和SQLite不同的是,它允許你在持久層直接和數(shù)據(jù)對象工作。在它之上是一個函數(shù)式風(fēng)格的查詢api,眾多的努力讓它比傳統(tǒng)的SQLite 操作更快 。
Realm不僅支持Android的接入,還支持IOS的接入。那么接下來就使用Realm數(shù)據(jù)庫實現(xiàn)Android App中的收藏功能吧。
需求分析
要使用Realm數(shù)據(jù)庫實現(xiàn)App中的收藏功能,需要實現(xiàn)以下幾個功能和步驟:
- 在Android項目接入Realm數(shù)據(jù)庫;
- 創(chuàng)建收藏數(shù)據(jù)庫表;
- 實現(xiàn)對收藏數(shù)據(jù)庫表進(jìn)行增刪查的操作;
- 數(shù)據(jù)庫表增刪查時界面實時渲染和更新。
接入Realm數(shù)據(jù)庫
然后在AS的外層build.gradle里的buildscript.dependencies添加Realm數(shù)據(jù)庫相關(guān)的依賴
buildscript {
dependencies {
...
classpath "io.realm:realm-gradle-plugin:6.0.1"
}
}
然后在AS的內(nèi)層build.gradle里添加Realm數(shù)據(jù)庫相關(guān)的依賴
在頂部 apply plugin: ‘com.android.application' 下添加 apply plugin: ‘realm-android' 的聲明,如下圖

在定義的Application里初始化數(shù)據(jù)庫相關(guān)的配置(包括數(shù)據(jù)庫名稱和版本號,具體代碼看Demo源碼)
/**
* 數(shù)據(jù)庫相關(guān)的配置
*
* @param context
*/
private void initRealm(Context context) {
try {
Realm.init(context);
RealmConfiguration config = new RealmConfiguration.Builder()
.name(RealmConstant.REALM_DB_NAME)
.schemaVersion(RealmConstant.REALM_DB_VERSION)
.build();
Realm.setDefaultConfiguration(config);
} catch (Exception e) {
e.printStackTrace();
}
}
創(chuàng)建收藏數(shù)據(jù)庫表
首先先創(chuàng)建一張收藏數(shù)據(jù)庫表,代碼如下:
/**
* 電影收藏數(shù)據(jù)庫表名
*/
public class MovieCollectDBModel extends RealmObject {
// 影視id,主鍵
@Required
@PrimaryKey
@Index
private String movieId;
// 影視名稱
@Required
private String movieName;
// 影視海報、封面
@Required
private String poster;
// 影視標(biāo)簽
@Required
private String movieLabel;
// 影視類型
@Required
private String videoType;
// 添加到數(shù)據(jù)的時間
@Required
private String createdTime;
// 以下省略字段的 set 和 get
}
該類繼承于Realm的RealmObject類,其中字段movieId作為該表的主鍵,也是數(shù)據(jù)在表里的唯一性。
收藏數(shù)據(jù)庫表的增刪查
添加影視數(shù)據(jù)到到數(shù)據(jù)庫的操作(增)
/**
* 添加收藏數(shù)據(jù)到本地數(shù)據(jù)庫
*
* @param mvId
* @param mvName
* @param movieImg
* @param movieLabel
* @param videoType
*/
public void addCollectToDB(String mvId, String mvName, String movieImg, String movieLabel, String videoType) {
MovieCollectDBModel movieCollectDBModel = new MovieCollectDBModel();
movieCollectDBModel.setMovieId(mvId);
movieCollectDBModel.setMovieName(mvName);
movieCollectDBModel.setPoster(movieImg);
movieCollectDBModel.setMovieLabel(movieLabel);
movieCollectDBModel.setVideoType(videoType);
movieCollectDBModel.setCreatedTime(BaseUtil.getNowDateTimeFormat());
mTransaction = mRealm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
realm.copyToRealmOrUpdate(movieCollectDBModel);
}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
L.i("收藏數(shù)據(jù)添加成功!");
ToastUtil.showToast(String.format("%s 已收藏", mvName));
}
}, new Realm.Transaction.OnError() {
@Override
public void onError(Throwable error) {
L.i("收藏數(shù)據(jù)添加失?。? + error.toString());
ToastUtil.showToast(String.format("%s 收藏失敗", mvName));
}
});
}
上面的代碼是Realm數(shù)據(jù)插入數(shù)據(jù)到數(shù)據(jù)庫的操作,這是一樣異步插入數(shù)據(jù)的方式。在業(yè)務(wù)場景的需要,可以改新增和更新數(shù)據(jù)的插入方式,比如插入數(shù)據(jù)時如果數(shù)據(jù)庫中已經(jīng)存在了該數(shù)據(jù)則進(jìn)行更新操作,否則進(jìn)行了新增操作。
從數(shù)據(jù)庫中刪除一條影視數(shù)據(jù)的操作(刪)
/**
* 通過鍵值對來刪除指定收藏數(shù)據(jù)
*
* @param key
* @param value
*/
public void deleteByKeyCollectData(String key, String value) {
RealmResults<MovieCollectDBModel> movieCollectDBModels = mRealm.where(MovieCollectDBModel.class)
.equalTo(key, value)
.findAllAsync();
movieCollectDBModels.addChangeListener(new RealmChangeListener<RealmResults<MovieCollectDBModel>>() {
@Override
public void onChange(RealmResults<MovieCollectDBModel> movieCollectDBModels1) {
if (movieCollectDBModels1.isEmpty()) return;
mRealm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
movieCollectDBModels1.deleteFromRealm(0);
}
});
}
});
}
從數(shù)據(jù)庫中刪除全部影視數(shù)據(jù)的操作(刪)
/**
* 清除全部收藏的數(shù)據(jù)庫內(nèi)容
*/
public void deleteAllCollectData() {
RealmResults<MovieCollectDBModel> movieCollectDBModels = mRealm.where(MovieCollectDBModel.class)
.findAll();
mRealm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
if (movieCollectDBModels.isEmpty()) {
ToastUtil.showToast("暫無收藏數(shù)據(jù)可刪除");
return;
}
movieCollectDBModels.deleteAllFromRealm();
ToastUtil.showToast("收藏數(shù)據(jù)已全部清空");
}
});
}
從數(shù)據(jù)庫中查詢?nèi)坑耙晹?shù)據(jù)的操作(查)
/**
* 查詢所有的收藏數(shù)據(jù)
*
* @param listener
*/
public void queryAllCollectData(RealmChangeListener<RealmResults<MovieCollectDBModel>> listener) {
RealmResults<MovieCollectDBModel> movieCollectDBModels = mRealm.where(MovieCollectDBModel.class)
.sort("createdTime", Sort.DESCENDING)
.findAllAsync();
movieCollectDBModels.addChangeListener(listener);
}
當(dāng)前用戶進(jìn)入收藏界面的時候,需要從數(shù)據(jù)庫中拉取全部的收藏數(shù)據(jù),可以剛才添加收藏時間進(jìn)行倒序排序。
數(shù)據(jù)庫表增刪查時界面的渲染和更新
當(dāng)用戶進(jìn)入主頁的時候,首次從網(wǎng)絡(luò)獲取數(shù)據(jù),然后遍歷一下網(wǎng)絡(luò)獲取數(shù)據(jù)是否已經(jīng)有數(shù)據(jù)操作收藏數(shù)據(jù)庫中,如果有的話則需要提示用戶該數(shù)據(jù)已經(jīng)被收藏并更改收藏圖標(biāo)。
public class MainActivity extends AppCompatActivity {
private MovieAdapter adapter;
private List<MovieDataModel.DataBean> mList = new ArrayList<>();
private RealmDBHelp mRealmDBHelp;
private boolean refreshType;
@BindView(R.id.mTitleBar)
TitleBar mTitleBar;
@BindView(R.id.homeMovieList)
RecyclerView homeMovieList;
@BindView(R.id.refreshLayout)
RefreshLayout refreshLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initRefresh();
}
private void initView() {
ButterKnife.bind(this);
mTitleBar.setOnTitleBarListener(new OnTitleBarListener() {
@Override
public void onLeftClick(View v) {
}
@Override
public void onTitleClick(View v) {
}
@Override
public void onRightClick(View v) {
// 跳轉(zhuǎn)至我的收藏界面
startActivity(new Intent(MainActivity.this, MovieCollectActivity.class));
}
});
// 初始化本地數(shù)據(jù)庫幫助類
mRealmDBHelp = new RealmDBHelp();
}
private void initRefresh() {
// 開啟自動加載功能(非必須)
refreshLayout.setEnableAutoLoadMore(true);
refreshLayout.setOnRefreshListener(new OnRefreshListener() {
@Override
public void onRefresh(@NonNull final RefreshLayout refreshLayout) {
refreshLayout.getLayout().postDelayed(new Runnable() {
@Override
public void run() {
refreshType = true;
// 加載數(shù)據(jù)
initData();
refreshLayout.finishRefresh();
refreshLayout.resetNoMoreData();//setNoMoreData(false);
}
}, 2000);
}
});
refreshLayout.setOnLoadMoreListener(new OnLoadMoreListener() {
@Override
public void onLoadMore(@NonNull final RefreshLayout refreshLayout) {
refreshLayout.getLayout().postDelayed(new Runnable() {
@Override
public void run() {
refreshType = false;
ToastUtil.showToast("暫無更多的數(shù)據(jù)啦");
// 將不會再次觸發(fā)加載更多事件
refreshLayout.finishLoadMoreWithNoMoreData();
refreshLayout.setEnableLoadMore(false);
refreshLayout.finishLoadMore();
}
}, 2000);
}
});
//觸發(fā)自動刷新
refreshLayout.autoRefresh();
}
private void initData() {
// 模擬從網(wǎng)絡(luò)獲取到j(luò)son數(shù)據(jù) app\src\main\assets\movieData.json
String jsonData = BaseUtil.getAssetsJson(this, "movieData.json");
// L.i("jsonData:" + jsonData);
if (TextUtils.isEmpty(jsonData)) {
ToastUtil.showToast("Json數(shù)據(jù)為空");
return;
}
if (mList != null) mList.clear();
// 將json數(shù)據(jù)解析為Java實體對象
MovieDataModel movieDataModel = new Gson().fromJson(jsonData, MovieDataModel.class);
List<MovieDataModel.DataBean> dataBeanList = movieDataModel.getData();
for (MovieDataModel.DataBean dataBean : dataBeanList) {
MovieDataModel.DataBean data = new MovieDataModel.DataBean();
data.setMvid(dataBean.getMvid());
data.setName(dataBean.getName());
data.setPoster(dataBean.getPoster());
data.setUpdateStatus(dataBean.getUpdateStatus());
data.setTag(dataBean.getTag());
data.setVideo_type(dataBean.getVideo_type());
mList.add(data);
}
// 查詢本地收藏數(shù)據(jù)庫中是否存在收藏的數(shù)據(jù)(更改收藏圖標(biāo)用的)
queryAllCollectData();
try {
// 設(shè)置適配器
homeMovieList.setLayoutManager(new GridLayoutManager(this, 3));
adapter = new MovieAdapter(this, mList);
homeMovieList.setAdapter(adapter);
} catch (Exception e) {
e.printStackTrace();
}
// 設(shè)置條目的點擊事件
adapter.setItemClikListener(new MovieAdapter.OnItemClikListener() {
@Override
public void onItemClik(View view, int position) {
String mvId = mList.get(position).getMvid();
String mvName = mList.get(position).getName();
String movieImg = mList.get(position).getPoster();
String movieTag = mList.get(position).getTag();
String videoType = mList.get(position).getVideo_type();
boolean isCollect = mList.get(position).isCollect();
if (isCollect) {
// 如果已經(jīng)收藏了則清除該條收藏
// 根據(jù)Id來刪除一條數(shù)據(jù)
mRealmDBHelp.deleteByKeyCollectData("movieId", mvId);
mList.get(position).setCollect(false);
ToastUtil.showToast(String.format("%s 已取消收藏", mvName));
} else {
// 添加收藏數(shù)據(jù)到本地數(shù)據(jù)庫
mRealmDBHelp.addCollectToDB(mvId, mvName, movieImg, movieTag, videoType);
mList.get(position).setCollect(true);
}
// 通知適配器數(shù)據(jù)改變重新更新界面
adapter.notifyDataSetChanged();
}
@Override
public void onItemLongClik(View view, int position) {
}
});
}
/**
* 查詢本地收藏數(shù)據(jù)庫中是否存在收藏的數(shù)據(jù)(更改收藏圖標(biāo)用的)
*/
private void queryAllCollectData() {
mRealmDBHelp.queryAllCollectData(new RealmChangeListener<RealmResults<MovieCollectDBModel>>() {
@Override
public void onChange(RealmResults<MovieCollectDBModel> movieCollectDBModels) {
if (movieCollectDBModels.isEmpty()) {
return;
}
for (MovieCollectDBModel movieCollectDBModel : movieCollectDBModels) {
for (int i = 0; i < mList.size(); i++) {
if (mList.get(i).getMvid().equals(movieCollectDBModel.getMovieId())) {
mList.get(i).setCollect(true);
break;
}
}
}
}
});
}
@Override
protected void onStop() {
super.onStop();
mRealmDBHelp.closeTransaction();
}
@Override
protected void onDestroy() {
super.onDestroy();
mRealmDBHelp.close();
}
}
收藏界面的代碼,包括查詢和刪除操作。
public class MovieCollectActivity extends AppCompatActivity {
private RealmDBHelp mRealmDBHelp;
private List<MovieCollectModel> mList = new ArrayList<>();
private MovieCollectAdapter adapter;
private boolean refreshType;
@BindView(R.id.mTitleBar)
TitleBar mTitleBar;
@BindView(R.id.movieCollectList)
RecyclerView movieCollectList;
@BindView(R.id.empty_view)
RelativeLayout empty_view;
@BindView(R.id.refreshLayout)
RefreshLayout refreshLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_movie_collect);
initView();
initRefresh();
}
private void initView() {
ButterKnife.bind(this);
mTitleBar.setOnTitleBarListener(new OnTitleBarListener() {
@Override
public void onLeftClick(View v) {
finish();
}
@Override
public void onTitleClick(View v) {
}
@Override
public void onRightClick(View v) {
deleteRealmDB();
}
});
mRealmDBHelp = new RealmDBHelp();
}
private void initRefresh() {
// 開啟自動加載功能(非必須)
refreshLayout.setEnableAutoLoadMore(true);
refreshLayout.setOnRefreshListener(new OnRefreshListener() {
@Override
public void onRefresh(@NonNull final RefreshLayout refreshLayout) {
refreshLayout.getLayout().postDelayed(new Runnable() {
@Override
public void run() {
refreshType = true;
// 加載數(shù)據(jù)
initData();
refreshLayout.finishRefresh();
refreshLayout.resetNoMoreData();//setNoMoreData(false);
}
}, 2000);
}
});
refreshLayout.setOnLoadMoreListener(new OnLoadMoreListener() {
@Override
public void onLoadMore(@NonNull final RefreshLayout refreshLayout) {
refreshLayout.getLayout().postDelayed(new Runnable() {
@Override
public void run() {
refreshType = false;
ToastUtil.showToast("暫無更多的數(shù)據(jù)啦");
// 將不會再次觸發(fā)加載更多事件
refreshLayout.finishLoadMoreWithNoMoreData();
refreshLayout.setEnableLoadMore(false);
refreshLayout.finishLoadMore();
}
}, 2000);
}
});
//觸發(fā)自動刷新
refreshLayout.autoRefresh();
}
private void initData() {
queryAllCollectData();
}
/**
* 查詢本地收藏數(shù)據(jù)庫中所有的數(shù)據(jù)
*/
private void queryAllCollectData() {
mRealmDBHelp.queryAllCollectData(new RealmChangeListener<RealmResults<MovieCollectDBModel>>() {
@Override
public void onChange(RealmResults<MovieCollectDBModel> movieCollectDBModels) {
if (movieCollectDBModels.isEmpty()) {
return;
}
if (mList != null) mList.clear();
for (MovieCollectDBModel movieCollectDBModel : movieCollectDBModels) {
MovieCollectModel data = new MovieCollectModel();
data.setMovieId(movieCollectDBModel.getMovieId());
data.setMovieName(movieCollectDBModel.getMovieName());
data.setPoster(movieCollectDBModel.getPoster());
data.setMovieTag(movieCollectDBModel.getMovieLabel());
data.setVideoType(movieCollectDBModel.getVideoType());
data.setCreatedTime(movieCollectDBModel.getCreatedTime());
mList.add(data);
}
runOnUiThread(new Runnable() {
@Override
public void run() {
parsingMovieList();
}
});
}
});
}
private void parsingMovieList() {
movieCollectList.setLayoutManager(new GridLayoutManager(this, 3));
adapter = new MovieCollectAdapter(this, mList);
movieCollectList.setAdapter(adapter);
if (mList.isEmpty()) {
empty_view.setVisibility(View.VISIBLE);
movieCollectList.setVisibility(View.GONE);
} else {
movieCollectList.setVisibility(View.VISIBLE);
empty_view.setVisibility(View.GONE);
}
adapter.setItemClikListener(new MovieCollectAdapter.OnItemClikListener() {
@Override
public void onItemClik(View view, int position) {
// 根據(jù)Id來刪除一條數(shù)據(jù)
deleteByKeyCollectData(position);
}
@Override
public void onItemLongClik(View view, int position) {
}
});
}
/**
* 根據(jù)Id來刪除一條數(shù)據(jù)
*
* @param position
*/
private void deleteByKeyCollectData(int position) {
IOSMsgDialog.Companion.init(getSupportFragmentManager())
.setTitle("清除影視提示")
.setMessage("是否要清除該條影視收藏的數(shù)據(jù)?")
.setAnimStyle(R.style.LDialogScaleAnimation)
.setNegativeButton("否", new View.OnClickListener() {
@Override
public void onClick(View v) {
}
})
.setPositiveButton("是", new View.OnClickListener() {
@Override
public void onClick(View v) {
// 根據(jù)Id來刪除一條數(shù)據(jù)
mRealmDBHelp.deleteByKeyCollectData("movieId", mList.get(position).getMovieId());
mList.remove(position);
adapter.notifyDataSetChanged();
}
}, Color.RED)
.setDismissListener(new OnDialogDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
}
}).setCancelableOutside(true).show();
}
/**
* 刪除本地數(shù)據(jù)庫中全部的收藏數(shù)據(jù)
*/
private void deleteRealmDB() {
IOSMsgDialog.Companion.init(getSupportFragmentManager())
.setTitle("清空全部收藏數(shù)據(jù)提示")
.setMessage("是否要立即清空全部我的收藏的數(shù)據(jù)?如果要清除單條數(shù)據(jù)的話請點擊單條數(shù)據(jù)列表。")
.setAnimStyle(R.style.LDialogScaleAnimation)
.setNegativeButton("否", new View.OnClickListener() {
@Override
public void onClick(View v) {
}
})
.setPositiveButton("是", new View.OnClickListener() {
@Override
public void onClick(View v) {
// 清除全部收藏的數(shù)據(jù)庫內(nèi)容
mRealmDBHelp.deleteAllCollectData();
mList.clear();
empty_view.setVisibility(View.VISIBLE);
movieCollectList.setVisibility(View.GONE);
adapter.notifyDataSetChanged();
}
}, Color.RED)
.setDismissListener(new OnDialogDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
}
}).setCancelableOutside(true).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
mRealmDBHelp.close();
}
}
界面運行效果圖如下:

apk安裝包下載體驗地址:
可以掃描以下二維碼進(jìn)行下載安裝,或者點擊以下鏈接 http://app.fukaimei.top/MovieCollect 進(jìn)行下載安裝體驗。
總結(jié)
到此這篇關(guān)于Android使用Realm數(shù)據(jù)庫實現(xiàn)App中的收藏功能(代碼詳解)的文章就介紹到這了,更多相關(guān)android realm數(shù)據(jù)庫實現(xiàn)app收藏內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android Studio如何獲取SQLite數(shù)據(jù)并顯示到ListView上
這篇文章主要介紹了Android Studio獲取SQLite數(shù)據(jù)并顯示到ListView上,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-03-03
Android 判斷某個服務(wù)(service)是否運行
這篇文章主要介紹了 Android 判斷某個服務(wù)(service)是否運行的相關(guān)資料,需要的朋友可以參考下2017-06-06
Kotlin基礎(chǔ)學(xué)習(xí)之lambda中return語句詳解
這篇文章主要給大家介紹了關(guān)于Kotlin基礎(chǔ)學(xué)習(xí)之lambda中return語句的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或使用Kotlin具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-07-07
Android 通過代碼設(shè)置、打開wifi熱點及熱點連接的實現(xiàn)代碼
這篇文章主要介紹了Android 通過代碼設(shè)置、打開wifi熱點及熱點連接的實現(xiàn)代碼,本文通過實例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2018-05-05

