前言
本章我们基于重构的方式进行一个 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(mCachedPreferenceKey);
if (!TextUtils.isEmpty(cacheStr)) {
try {
// 反序列化
NETWORK_DATA data = new Gson().fromJson(new JSONObject(cacheStr).getString("data"),
(Class<NETWORK_DATA>) GenericUtils.getGenericType(this));
if (data != null) {
onSuccess(data, true);
}
if (isNeedUpdate()) {
load();
return;
}
} catch (JSONException e) {
// do nothing
}
}
load();
}
然后,Fragment 中调用这个 getCachedDataAndLoad 方法进行数据的拉取;
model = new NewsChannelModel();
model.register(this);
model.getCachedDataAndLoad();
mNewsListModel = new NewsListModel(getArguments().getString(BUNDLE_KEY_PARAM_CHANNEL_ID),
getArguments().getString(BUNDLE_KEY_PARAM_CHANNEL_NAME));
mNewsListModel.register(this);
mNewsListModel.getCachedDataAndLoad();
运行,可以看到,我们的页面在无网状态下依然可以展示数据,但是如果我们的 App 是在无网的时候安装并运行的,那么依然是会展示一片空白,这个时候,其实是需要我们进行一些数据的预置的,我们可以增加一下预置数据的获取逻辑;
public abstract class BaseMvvmModel<NETWORK_DATA, RESULT_DATA> implements MvvmDataObserver<NETWORK_DATA> {
private String mPreDefinedData;
```
public BaseMvvmModel(boolean isPaging, String cachedPreferenceKey, String preDefinedData, int... initPageNumber) {
if (isPaging && initPageNumber != null && initPageNumber.length > 0) {
INIT_PAGE_NUMBER = initPageNumber[0];
} else {
INIT_PAGE_NUMBER = -1;
}
this.mCachedPreferenceKey = cachedPreferenceKey;
// 赋值预置数据
this.mPreDefinedData = preDefinedData;
}
// 获取预置数据
```
public void getCachedDataAndLoad() {
if (!isLoading) {
isLoading = true;
}
if (mCachedPreferenceKey != null) {
String cacheStr = BasicDataPreferenceUtil.getInstance().getString(mCachedPreferenceKey);
if (!TextUtils.isEmpty(cacheStr)) {
try {
// 反序列化
NETWORK_DATA data = new Gson().fromJson(new JSONObject(cacheStr).getString("data"),
(Class<NETWORK_DATA>) GenericUtils.getGenericType(this));
if (data != null) {
onSuccess(data, true);
}
if (isNeedUpdate()) {
load();
return;
}
} catch (JSONException e) {
// do nothing
}
}
// 从预置数据中取数据
if (mPreDefinedData != null) {
NETWORK_DATA data = new Gson().fromJson(mPreDefinedData,
(Class<NETWORK_DATA>) GenericUtils.getGenericType(this));
if (data != null) {
onSuccess(data, true);
}
}
}
load();
}
}
子 Model 中传入预置数据
public class NewsChannelModel extends BaseMvvmModel<NewsChannelsBean, List<NewsChannelsBean.ChannelList>> {
public static final String PREDEFINED_CHANNELS = "{"showapi_res_body":{"channelList":[{"channelId":"5572a108b3cdc86cf39001cd","name":"国内焦点"},{"channelId":"5572a108b3cdc86cf39001ce","name":"国际焦点"},{"channelId":"5572a108b3cdc86cf39001cf","name":"军事焦点"},{"channelId":"5572a108b3cdc86cf39001d0","name":"财经焦点"},{"channelId":"5572a108b3cdc86cf39001d1","name":"互联网焦点"},{"channelId":"5572a108b3cdc86cf39001d2","name":"房产焦点"},{"channelId":"5572a108b3cdc86cf39001d3","name":"汽车焦点"},{"channelId":"5572a108b3cdc86cf39001d4","name":"体育焦点"},{"channelId":"5572a108b3cdc86cf39001d5","name":"娱乐焦点"},{"channelId":"5572a108b3cdc86cf39001d6","name":"游戏焦点"},{"channelId":"5572a108b3cdc86cf39001d7","name":"教育焦点"},{"channelId":"5572a108b3cdc86cf39001d8","name":"女人焦点"},{"channelId":"5572a108b3cdc86cf39001d9","name":"科技焦点"},{"channelId":"5572a109b3cdc86cf39001da","name":"社会焦点"},{"channelId":"5572a109b3cdc86cf39001db","name":"国内最新"},{"channelId":"5572a109b3cdc86cf39001dc","name":"台湾最新"},{"channelId":"5572a109b3cdc86cf39001dd","name":"港澳最新"},{"channelId":"5572a109b3cdc86cf39001de","name":"国际最新"},{"channelId":"5572a109b3cdc86cf39001df","name":"军事最新"},{"channelId":"5572a109b3cdc86cf39001e0","name":"财经最新"},{"channelId":"5572a109b3cdc86cf39001e1","name":"理财最新"},{"channelId":"5572a109b3cdc86cf39001e2","name":"宏观经济最新"},{"channelId":"5572a109b3cdc86cf39001e3","name":"互联网最新"},{"channelId":"5572a109b3cdc86cf39001e4","name":"房产最新"},{"channelId":"5572a109b3cdc86cf39001e5","name":"汽车最新"},{"channelId":"5572a109b3cdc86cf39001e6","name":"体育最新"},{"channelId":"5572a10ab3cdc86cf39001e7","name":"国际足球最新"},{"channelId":"5572a10ab3cdc86cf39001e8","name":"国内足球最新"},{"channelId":"5572a10ab3cdc86cf39001e9","name":"CBA最新"},{"channelId":"5572a10ab3cdc86cf39001ea","name":"综合体育最新"},{"channelId":"5572a10ab3cdc86cf39001eb","name":"娱乐最新"},{"channelId":"5572a10ab3cdc86cf39001ec","name":"电影最新"},{"channelId":"5572a10ab3cdc86cf39001ed","name":"电视最新"},{"channelId":"5572a10ab3cdc86cf39001ee","name":"游戏最新"},{"channelId":"5572a10ab3cdc86cf39001ef","name":"教育最新"},{"channelId":"5572a10ab3cdc86cf39001f0","name":"女人最新"},{"channelId":"5572a10ab3cdc86cf39001f1","name":"美容护肤最新"},{"channelId":"5572a10ab3cdc86cf39001f2","name":"情感两性最新"},{"channelId":"5572a10ab3cdc86cf39001f3","name":"健康养生最新"},{"channelId":"5572a10ab3cdc86cf39001f4","name":"科技最新"},{"channelId":"5572a10bb3cdc86cf39001f5","name":"数码最新"},{"channelId":"5572a10bb3cdc86cf39001f6","name":"电脑最新"},{"channelId":"5572a10bb3cdc86cf39001f7","name":"科普最新"},{"channelId":"5572a10bb3cdc86cf39001f8","name":"社会最新"},{"channelId":"57463656a44a13cf","name":"旅游最新"},{"channelId":"9a15d3d4b5cab17f34e6d7976b1b1f30","name":"电商最新"},{"channelId":"5b4fe11ec1239b0805568da6e90fd875","name":"物流最新"},{"channelId":"3f4794412eaa17ed","name":"创业最新"}],"ret_code":0,"totalNum":48},"showapi_res_code":0,"showapi_res_error":""}";
public NewsChannelModel() {
super(false, "NEWS_CHANNEL_PREF_KEY", PREDEFINED_CHANNELS);
}
}
运行效果如下,可以看到界面上不那么空白了,通过预置数据;
Jetpack 的 ViewModel + LiveData
千呼万唤,ViewModel 来了,接下来我们要引入 ViewModel 用来监听网络数据的返回,以及结合 LiveData 实现数据更新的处理;
我们分别定义两个 ViewModel,以及对应的 ViewModel 中声明 LiveData 来观测数据的变化并及时更新到UI
HeadlineNewsViewModel 持有 LiveData 和 Model,并实现 IBaseModelListener 来接受网络请求回调结果,待结果返回之后,由 LiveData 将数据 post 到 View 层;
public class HeadlineNewsViewModel extends ViewModel implements IBaseModelListener<List<NewsChannelsBean.ChannelList>> {
private NewsChannelModel model;
public MutableLiveData<List<NewsChannelsBean.ChannelList>> dataList = new MutableLiveData<>();
public HeadlineNewsViewModel() {
model = new NewsChannelModel();
model.register(this);
model.getCachedDataAndLoad();
}
@Override
public void onLoadSuccess(BaseMvvmModel model, List<NewsChannelsBean.ChannelList> channelLists, PageResult... pageResults) {
if (model instanceof NewsChannelModel) {
dataList.postValue(channelLists);
}
}
@Override
public void onLoadFail(int errorCode, String errorMsg) {
}
}
HeadlineNewsFragment 中持有 ViewModel 并获取 LiveData 监听数据变化;
public class HeadlineNewsFragment extends Fragment {
public HeadlineNewsFragmentAdapter adapter;
FragmentHomeBinding viewDataBinding;
private HeadlineNewsViewModel mHeadlineNewsViewModel = new HeadlineNewsViewModel();
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
viewDataBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_home, container, false);
adapter = new HeadlineNewsFragmentAdapter(getChildFragmentManager());
viewDataBinding.tablayout.setTabMode(TabLayout.MODE_SCROLLABLE);
viewDataBinding.viewpager.setAdapter(adapter);
viewDataBinding.tablayout.setupWithViewPager(viewDataBinding.viewpager);
viewDataBinding.viewpager.setOffscreenPageLimit(1);
mHeadlineNewsViewModel.dataList.observe(this, channelLists -> {
if (adapter != null) {
adapter.setChannels(channelLists);
}
});
return viewDataBinding.getRoot();
}
}
同样的 NewListViewModel 中也这样处理下,但是不同的地方在于这个页面需要分页和加载更多,ViewModel 提供对应的 refresh 和 loadNextPage,同时声明 ViewStatus 类型的 LiveData 用来更新界面状态;
public class NewsListViewModel extends ViewModel implements IBaseModelListener<ArrayList<BaseViewModel>> {
private NewsListModel model;
public MutableLiveData<ArrayList<BaseViewModel>> dataList = new MutableLiveData<>();
public MutableLiveData<ViewStatus> viewStatus = new MutableLiveData<>();
public NewsListViewModel(String channelId, String channelName) {
dataList.setValue(new ArrayList());
model = new NewsListModel(channelId, channelName);
model.register(this);
model.getCachedDataAndLoad();
}
public void refresh() {
model.refresh();
}
public void loadNextPage() {
model.loadNextPage();
}
@Override
public void onLoadSuccess(BaseMvvmModel model, ArrayList<BaseViewModel> baseViewModels, PageResult... pageResults) {
if (model instanceof NewsListModel) {
if (pageResults[0].isFirstPage) {
dataList.getValue().clear();
}
if (pageResults[0].isEmpty) {
if (pageResults[0].isFirstPage) {
viewStatus.postValue(ViewStatus.EMPTY);
} else {
viewStatus.postValue(ViewStatus.NO_MORE_DATA);
}
} else {
dataList.getValue().addAll(baseViewModels);
dataList.postValue(dataList.getValue());
viewStatus.postValue(ViewStatus.SHOW_CONTENT);
}
}
}
@Override
public void onLoadFail(int errorCode, String errorMsg) {
}
}
NewListFragment 中获取 dataList 并 observer
public class NewsListFragment extends Fragment {
private NewsListRecyclerViewAdapter mAdapter;
private FragmentNewsBinding viewDataBinding;
private NewsListViewModel mNewsListViewModel;
ArrayList<BaseViewModel> contentList = new ArrayList<>();
protected final static String BUNDLE_KEY_PARAM_CHANNEL_ID = "bundle_key_param_channel_id";
protected final static String BUNDLE_KEY_PARAM_CHANNEL_NAME = "bundle_key_param_channel_name";
public static NewsListFragment newInstance(String channelId, String channelName) {
NewsListFragment fragment = new NewsListFragment();
Bundle bundle = new Bundle();
bundle.putString(BUNDLE_KEY_PARAM_CHANNEL_ID, channelId);
bundle.putString(BUNDLE_KEY_PARAM_CHANNEL_NAME, channelName);
fragment.setArguments(bundle);
return fragment;
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
viewDataBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_news, container, false);
mAdapter = new NewsListRecyclerViewAdapter(getContext());
viewDataBinding.listview.setHasFixedSize(true);
viewDataBinding.listview.setLayoutManager(new LinearLayoutManager(getContext()));
viewDataBinding.listview.setAdapter(mAdapter);
mNewsListViewModel = new NewsListViewModel(getArguments().getString(BUNDLE_KEY_PARAM_CHANNEL_ID),
getArguments().getString(BUNDLE_KEY_PARAM_CHANNEL_NAME));
mNewsListViewModel.dataList.observe(this, baseViewModels -> {
mAdapter.setData(contentList);
viewDataBinding.refreshLayout.finishRefresh();
viewDataBinding.refreshLayout.finishLoadMore();
});
viewDataBinding.refreshLayout.setOnRefreshListener(refreshLayout -> mNewsListViewModel.refresh());
viewDataBinding.refreshLayout.setOnLoadMoreListener(refreshLayout -> mNewsListViewModel.loadNextPage());
return viewDataBinding.getRoot();
}
}
BaseMvvmViewModel
我们前面是用来 ViewModel,但是没有抽取一个 base 层,它们其实是有公共的地方可以抽取出来的,我们来创建 BaseMvvmViewModel 并重构下;
public abstract class BaseMvvmViewModel<MODEL extends BaseMvvmModel, DATA> extends ViewModel implements IBaseModelListener<List<DATA>> {
public MutableLiveData<List<DATA>> dataList = new MutableLiveData<>();
public MutableLiveData<ViewStatus> viewStatus = new MutableLiveData<>();
protected MODEL model;
public BaseMvvmViewModel() {
// viewStatus.postValue(ViewStatus.LOADING);
}
public abstract MODEL createModel();
public void getCacheDataAndLoad() {
createAndRegisterModel();
if (model != null) {
model.getCachedDataAndLoad();
}
}
public void refresh() {
createAndRegisterModel();
if (model != null) {
model.refresh();
}
}
public void loadNextPage() {
createAndRegisterModel();
if (model != null) {
model.loadNextPage();
}
}
/**
* activity/fragment 销毁的时候会调用 viewmodel 的这个方法,进行业务处理
*/
@Override
protected void onCleared() {
super.onCleared();
}
private void createAndRegisterModel() {
if (model == null) {
model = createModel();
model.register(this);
}
}
@Override
public void onLoadSuccess(BaseMvvmModel model, List<DATA> data, PageResult... pageResults) {
if (model.isPaging()) {
if (pageResults[0].isEmpty) {
if (pageResults[0].isFirstPage) {
viewStatus.postValue(ViewStatus.EMPTY);
} else {
viewStatus.postValue(ViewStatus.NO_MORE_DATA);
}
} else {
if (pageResults[0].isFirstPage) {
dataList.postValue(data);
} else {
dataList.getValue().addAll(data);
dataList.postValue(dataList.getValue());
}
viewStatus.postValue(ViewStatus.SHOW_CONTENT);
}
} else {
dataList.postValue(data);
viewStatus.postValue(ViewStatus.SHOW_CONTENT);
}
}
@Override
public void onLoadFail(int errorCode, String errorMsg) {
viewStatus.postValue(ViewStatus.REFRESH_ERROR);
}
}
子 ViewModel 继承这个 BaseMvvmViewModel,并修改相关逻辑;
public class NewsListViewModel extends BaseMvvmViewModel<NewsListModel, BaseViewModel> {
private final String mChannelId;
private final String mChannelName;
public NewsListViewModel(String channelId, String channelName) {
this.mChannelId = channelId;
this.mChannelName = channelName;
}
@Override
public NewsListModel createModel() {
return new NewsListModel(mChannelId, mChannelName);
}
public void refresh() {
model.refresh();
}
public void loadNextPage() {
model.loadNextPage();
}
}
HeadlineNewsViewModel 重构如下:
public class HeadlineNewsViewModel extends BaseMvvmViewModel<NewsChannelModel, NewsChannelsBean.ChannelList> {
@Override
public NewsChannelModel createModel() {
return new NewsChannelModel();
}
}
因为删除了构造方法中的调用 model 层的逻辑,所以需要我们在 Fragment 中手动调用一下数据的拉取;
mNewsListViewModel = new NewsListViewModel(getArguments().getString(BUNDLE_KEY_PARAM_CHANNEL_ID),
getArguments().getString(BUNDLE_KEY_PARAM_CHANNEL_NAME));
mNewsListViewModel.getCacheDataAndLoad();
mHeadlineNewsViewModel = new HeadlineNewsViewModel();
mHeadlineNewsViewModel.getCacheDataAndLoad();
运行,可以正常的拉取数据并展示,但是,我们在使用 ViewPager + Fragment 的时候,通常都是懒加载的方式,也就是 Fragment 可见的时候,我们才会 load 并展示数据,我们来实现一下懒加载;
懒加载
这里我们借助 LifeCycle 来监听页面 onResume 的时候,来进行数据的加载;因此我们的 BaseMvvmViewModel 需要实现 LifeCycleObserver 接口;
public abstract class BaseMvvmViewModel<MODEL extends BaseMvvmModel, DATA> extends ViewModel implements LifecycleObserver, IBaseModelListener<List<DATA>> {
// ...
// 省略部分代码
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
private void onResume() {
if (dataList == null || dataList.getValue() == null || dataList.getValue().isEmpty()) {
createAndRegisterModel();
model.getCachedDataAndLoad();
} else {
dataList.postValue(dataList.getValue());
viewStatus.postValue(viewStatus.getValue());
}
}
}
然后,对应的 Fragment 中添加下 observer,同时也不用主动调用了 getCacheDataAndLoad 方法了;
getLifecycle().addObserver(mNewsListViewModel);
HeadlineNewsFragment
getLifecycle().addObserver(mHeadlineNewsViewModel);
BaseView
到这里的时候,可以看到,我们的 base 下的 mvvm 中 BaseMvvmModel 和 BaseMvvmViewModel 都有了,就差 BaseMvvmView 了,我们来抽取下;
public abstract class BaseMvvmFragment<VIEW extends ViewDataBinding, VIEWMODEL extends BaseMvvmViewModel, DATA> extends Fragment implements Observer {
protected VIEWMODEL viewmodel;
protected VIEW viewDataBinding;
public abstract int getLayoutId();
public abstract VIEWMODEL getViewmodel();
public abstract String getFragmentTag();
public abstract void onNetworkResponded(List<DATA> dataList, boolean isDataUpdated);
public abstract void onViewCreated();
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(getFragmentTag(), "onCreate");
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
viewDataBinding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
return viewDataBinding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
viewmodel = getViewmodel();
getLifecycle().addObserver(viewmodel);
viewmodel.dataList.observe(this, this);
viewmodel.viewStatus.observe(this, this);
onViewCreated();
}
@Override
public void onChanged(Object o) {
if (o instanceof ArrayList) {
onNetworkResponded((List<DATA>) o, true);
}
}
}
对应的子 Fragment 继承这个 Base 然后重写对应的实现即可;
public class NewsListFragment extends BaseMvvmFragment<FragmentNewsBinding, NewsListViewModel, BaseViewModel> {
private NewsListRecyclerViewAdapter mAdapter;
ArrayList<BaseViewModel> contentList = new ArrayList<>();
protected final static String BUNDLE_KEY_PARAM_CHANNEL_ID = "bundle_key_param_channel_id";
protected final static String BUNDLE_KEY_PARAM_CHANNEL_NAME = "bundle_key_param_channel_name";
public static NewsListFragment newInstance(String channelId, String channelName) {
NewsListFragment fragment = new NewsListFragment();
Bundle bundle = new Bundle();
bundle.putString(BUNDLE_KEY_PARAM_CHANNEL_ID, channelId);
bundle.putString(BUNDLE_KEY_PARAM_CHANNEL_NAME, channelName);
fragment.setArguments(bundle);
return fragment;
}
@Override
public int getLayoutId() {
return R.layout.fragment_news;
}
@Override
public NewsListViewModel getViewmodel() {
return viewmodel = new NewsListViewModel(getArguments().getString(BUNDLE_KEY_PARAM_CHANNEL_ID),
getArguments().getString(BUNDLE_KEY_PARAM_CHANNEL_NAME));
}
@Override
public String getFragmentTag() {
return getArguments().getString(BUNDLE_KEY_PARAM_CHANNEL_ID);
}
@Override
public void onNetworkResponded(List<BaseViewModel> baseViewModels, boolean isDataUpdated) {
if (isDataUpdated) {
mAdapter.setData(contentList);
viewDataBinding.refreshLayout.finishRefresh();
viewDataBinding.refreshLayout.finishLoadMore();
}
}
@Override
public void onViewCreated() {
mAdapter = new NewsListRecyclerViewAdapter(getContext());
viewDataBinding.listview.setHasFixedSize(true);
viewDataBinding.listview.setLayoutManager(new LinearLayoutManager(getContext()));
viewDataBinding.listview.setAdapter(mAdapter);
viewDataBinding.refreshLayout.setOnRefreshListener(refreshLayout -> viewmodel.refresh());
viewDataBinding.refreshLayout.setOnLoadMoreListener(refreshLayout -> viewmodel.loadNextPage());
}
}
ViewModelProvider
这里我们不能每次都去创建 ViewModel,否则就会导致每次都重新请求数据,这里借助 ViewModelProvider 来控制 ViewModel 的创建;这里直接上代码了
public static class NewsListViewModelFactory implements ViewModelProvider.Factory {
private String mChannelId;
private String mChannelName;
public NewsListViewModelFactory(String channelId, String channelName) {
this.mChannelId = channelId;
this.mChannelName = channelName;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
return (T) new NewsListViewModel(mChannelId, mChannelName);
}
}
Fragment 中 createViewModel 重构如下:
@Override
public NewsListViewModel getViewmodel() {
return new ViewModelProvider(getActivity(), new NewsListViewModel.NewsListViewModelFactory(getArguments().getString(BUNDLE_KEY_PARAM_CHANNEL_ID),
getArguments().getString(BUNDLE_KEY_PARAM_CHANNEL_NAME))).get(getArguments().getString(BUNDLE_KEY_PARAM_CHANNEL_ID), NewsListViewModel.class);
}
好了,MVVM 的上中下就全部讲完了;
欢迎三连
来都来了,点个关注,点个赞吧,你的支持是我最大的动力~~