MVVM模式基类抽取Fragment的一些小实践经验
很多情况下不使用Activity,而是使用fragment,那么从开发角度出发,抽取基类就显得再普通不过了,下面简单记录一下实践的经验吧。
前提
首先得了解一下androdid应用打开后加载数据带显示的这样一个流程,这个步骤大致可以分为以下:
- 打开应用
- 访问权限
- 进入主页面
3.1 显示无需加载数据的那一部分UI
3.2 显示需要加载数据部分的缺省图
3.3 等待数据加载后显示并隐藏缺省图 - 某些操作唤起弹窗
- … …
暂时列举这些
提取共性
从上述可以看到有些是所有的页面都具有的,这就是共性,比如网络情况,加载缺省图,弹窗,主题变更这些
BaseFragment
BaseFragment一般继承Fragment,那么回忆一下Fragment的生命周期
那么思考一下,比如说每一个Fragment都需要绑定Activity,那么onAttach方法就属于共性,这个主要就是拿到context,再比如说每一个Fragment都需要监听网络对吧,我们可以在BaseFragment或者Activity注册网络的监听,接收后调用方法,方法空实现,让子类重写做自己的处理,还有可以封装一些弹窗的方法等,大致如下:
public class BaseFragment extends Fragment {
private BroadcastReceiver mNetworkReceiver;
private Context mContext;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
mContext = context;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//这里主要完成一些与UI无关的操作,初始化Fragment,当然,一般也不在这里初始化数据
}
public void registerNetworkChanged() {
mNetworkReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//从这里解析结果,调onNetworkChanged()传递结果出去
}
};
IntentFilter filter = new IntentFilter();
filter.addAction("这里写网络的这个广播类型");
mContext.registerReceiver(mNetworkReceiver,filter);
}
protected void onNetworkChanged(boolean connect){
}
protected void unRegisterNetworkChange() {
try {
if (null != mNetworkReceiver) {
mContext.unregisterReceiver(mNetworkReceiver);
mNetworkReceiver = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void showToastByStr(String msg){
//弹出弹窗的具体实现
}
public void showToastByRsd(int rsd){
//弹出弹窗的具体实现
}
}
到这就很奇怪,那还有其他生命周期呢,加载view呢
这个一般不再最顶层的BaseFragment写,因为有时候模块不同,使用的结构也不同,需要各自去实现,可以理解为可以在下面一种结构再进行一次抽取:这里使用MVVM结构来举例。
MVVMBaseFragment
由于MVVM的核心是完全解耦数据层与UI层,这就需要先具备一些mvvm的相关知识,比如说这行代码:
public class MVVMBaseFragment<VM extends ViewModel> extends BaseFragment {
}
这里简单理解就是子类在继承MVVMBaseFragment时需要指定一个泛型,且是需要继承ViewModel的,这样就可以在MVVMBaseFragment拿到这个泛型VM并且将其变成ViewModel,这样就不用再每个Fragment中自己去创建ViewModel了。
ok继续,接下来就是onCreateView了,这个方法在Fragment中是用来创建Fragment的,再通俗一点加载UI的
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}
这就很简单了,加载xml文件的肯定是可以抽出来的,加载之后需要初始化View,这个肯定也是可以抽出来的于是:
public abstract class MVVMBaseFragment<VM extends ViewModel> extends BaseFragment {
private View mRootView;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mRootView = inflater.inflate(setContentView(), container, false);
initView(mRootView);
return mRootView;
}
protected abstract void initView(View view);
protected abstract int setContentView();
}
那么下面就需要明确一个小点,ViewModel什么时候创建,这里思考到在initView之后就需要初始化数据了,所以ViewModel的建立应该在initView之前
于是:
public abstract class MVVMBaseFragment<VM extends ViewModel> extends BaseFragment {
private View mRootView;
private VM mViewModel;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mRootView = inflater.inflate(setContentView(), container, false);
CreateViewModel();
initView(mRootView);
return mRootView;
}
private void CreateViewModel() {
if (mViewModel == null) {
Class<VM> vmClass = null;
Type type = getClass().getGenericSuperclass();
if (type instanceof ParameterizedType) {
vmClass = (Class<VM>) ((ParameterizedType) type).getActualTypeArguments()[0];
} else {
//使用BaseViewModel
}
mViewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(vmClass);
getLifecycle().addObserver(mViewModel);
}
}
protected abstract void initView(View view);
protected abstract int setContentView();
}
上面有一句
getLifecycle().addObserver(mViewModel);
这个后面具体解释一下,另写一遍再附链接,很好用的无感化避免OOM
接着上面的,现在已经走完onCreateView了,接着的几个方法一般不会在基类中实现,有需要的子类可以重写自己处理,但是onDestroy这个方法还是需要的,可以直接在基类中释放掉viewmodel这种的资源。
还有,之前在最BaseFragment中添加了弹窗方法,于是还应该想到每一个Fragment加载数据时应该有结果的对吧。于是添加上:
protected void onLoadRetry() {
}
protected void showLoading() {
}
protected void showLoadSuccess() {
}
protected void showLoadFailed() {
}
protected void showEmpty() {
}
protected void showSearchEmpty() {
}
好了,到这里我们的基类Fragment大致就已经完成了,接着写基类ViewModel