前言
本章我们基于重构的方式进行一个 MVVM 的实战,我们将一个新闻列表的普通实现,一步一步的改造成 MVVM 的架构模式,一共分为上中下三个章节,本章继续上一章,开始下篇的讲解;
缓存数据处理
上一章我们讲到了网络数据的缓存逻辑,但是缓存之后我们还没有使用,接下来我们来看下如何使用缓存的数据;
数据的整体来源是两个方向,一个是 Server 端数据,一个是 SP 中的数据;
我们需要在底层数据分发的时候就来判断是否来自缓存数据,新增一个接口,同时在底层数据分发的时候进行判断并替换;
public interface MvvmDataObserver<F> {
void onSuccess(F t, boolean isFromCache);
void onFailure(String errorMsg);
}
因为我们使用了 RxJava 来做网络接口层的转换,所以我们的 BaseObserver 需要重构一下:
public class BaseObserver<T> implements Observer<T> {
private MvvmDataObserver mvvmDataObserver;
public BaseObserver(MvvmDataObserver mvvmDataObserver) {
this.mvvmDataObserver = mvvmDataObserver;
}
@Override
public void onSubscribe(Disposable d) {}
@Override
public void onNext(T t) {
mvvmDataObserver.onSuccess(t, false);
}
@Override
public void onError(Throwable e) {
mvvmDataObserver.onFailure(e);
}
@Override
public void onComplete() {}
}
也就是说将数据的分发,统一交给了 MvvmDataObserver 来分发;然后,需要 BaseMvvmModel 来实现这个 MvvmDataObserver 接口,然后子 Model 来做接口的具体实现;
public abstract class BaseMvvmModel<NETWORK_DATA, RESULT_DATA> implements MvvmDataObserver<NETWORK_DATA> {
}
NewListModel 重构如下:
public class NewsListModel extends BaseMvvmModel<NewsListBean, ArrayList<BaseViewModel>> {
private final String mChannelId;
private final String mChannelName;
private NewsListBean mNewsListBean;
public NewsListModel(String channelId, String channelName) {
super(true, channelId + channelName + "pref_key", 1);
this.mChannelId = channelId;
this.mChannelName = channelName;
}
@Override
public void load() {
TecentNetworkApi.getService(NewsApiInterface.class)
.getNewsList(mChannelId,
mChannelName, String.valueOf(mPageNum))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BaseObserver<>(this));
}
@Override
public void onSuccess(NewsListBean newsListBean, boolean isFromCache) {
ArrayList<BaseViewModel> viewModels = new ArrayList<>();
NewsListModel.this.mNewsListBean = newsListBean;
for (NewsListBean.Contentlist contentlist : newsListBean.showapiResBody.pagebean.contentlist) {
if (contentlist.imageurls != null && contentlist.imageurls.size() > 0) {
PictureTitleViewModel pictureTitleViewModel = new PictureTitleViewModel();
pictureTitleViewModel.imgUrl = contentlist.imageurls.get(0).url;
pictureTitleViewModel.title = contentlist.title;
pictureTitleViewModel.jumpUrl = contentlist.link;
viewModels.add(pictureTitleViewModel);
} else {
TitleViewModel titleViewModel = new TitleViewModel();
titleViewModel.title = contentlist.title;
titleViewModel.jumpUrl = contentlist.link;
viewModels.add(titleViewModel);
}
}
notifyResultToListener(NewsListModel.this.mNewsListBean, viewModels, isFromCache);
}
@Override
public void onFailure(Throwable throwable) {
}
}
NewsChannelModel 重构如下:
public class NewsChannelModel extends BaseMvvmModel<NewsChannelsBean, List<NewsChannelsBean.ChannelList>> {
public NewsChannelModel() {
super(false, "NEWS_CHANNEL_PREF_KEY");
}
@Override
public void load() {
TecentNetworkApi.getService(NewsApiInterface.class)
.getNewsChannels()
.compose(TecentNetworkApi.getInstance().applySchedulers(new BaseObserver<NewsChannelsBean>(this)));
}
@Override
public void onSuccess(NewsChannelsBean newsChannelsBean, boolean isFromCache) {
notifyResultToListener(newsChannelsBean, newsChannelsBean.showapiResBody.channelList, isFromCache);
}
@Override
public void onFailure(Throwable throwable) {
mReferenceIBaseModeListener.get().onLoadFail(400, throwable.getMessage());
}
}
然后我们的 notifyResultToListener 重构如下,增加了 isFromCache 的判断逻辑;
protected void notifyResultToListener(NETWORK_DATA networkData, RESULT_DATA data, boolean isFromCache) {
IBaseModelListener listener = mReferenceIBaseModeListener.get();
if (listener != null) {
if (mIsPaging) {
listener.onLoadSuccess(this, data, new PageResult(mPageNum == INIT_PAGE_NUMBER, data == null ? true : ((List) data).isEmpty(), ((List) data).size() > 10));
} else {
listener.onLoadSuccess(this, data);
}
}
if (mIsPaging) {
if (mCachedPreferenceKey != null && mPageNum == INIT_PAGE_NUMBER && !isFromCache) {
saveDataToPreference(networkData);
}
} else {
if (mCachedPreferenceKey != null && !isFromCache) {
saveDataToPreference(networkData);
}
}
if (mIsPaging) {
if (data != null && ((List) data).size() > 0 && !isFromCache) {
mPageNum++;
}
}
if (!isFromCache) {
isLoading = false;
}
}
只有非缓存的时候,我们才将数据保存到本地,根据 isFromCache 标识;
我们接下来处理 缓存的读取和网络请求的发送逻辑;同样的,我们需要在 BaseMvvmModel 中进行逻辑的判断和处理,我们增加一个 getCachedDataAndLoad 接口,先从缓存获取同时进行网络数据的获取;
public void getCachedDataAndLoad() {
if (!isLoading) {
isLoading = true;
}
String cacheStr = BasicDataPreferenceUtil.getInstance().getString(m