如何应对 Android 面试官 -> MVVM 实战一个新闻客户端 (下)

前言


本章我们基于重构的方式进行一个 MVVM 的实战,我们将一个新闻列表的普通实现,一步一步的改造成 MVVM 的架构模式,一共分为上中下三个章节,本章继续上一章,开始下篇的讲解;

缓存数据处理


上一章我们讲到了网络数据的缓存逻辑,但是缓存之后我们还没有使用,接下来我们来看下如何使用缓存的数据;

image.png

数据的整体来源是两个方向,一个是 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值