欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

关于mvvm简易封装(一)

程序员文章站 2022-03-16 13:01:27
...

前言

关于mvvm的出现已经很长一段时间了,但是博主一直没有太过于关注,但是由于最近接触的和新出的很多框架都是基于mvvm模式去开发的,于是花了点时间看了下。
关于学习mvvm前,可能需要首先了解databing,请自行百度了,介绍databing的博客一堆,由于道行不够高深就不带大家解读源码了。有了databing的加入你的项目再也不需要findViewById了,也不需要butterknife插件了,而且databing功能不仅如此,他还可以绑定事件、和数据转换,在网上找来这张图可以先了解下
关于mvvm简易封装(一)
那么接下来开始我们的简易封装之旅

  1. BaseActivity封装
  2. BaseFragment封装
  3. BaseView统一接口方法封装
  4. BaseViewModel封装
  5. BaseException错误异常定义类
  6. BaseModelEntity接口返回数据统一处理类
  7. 封装网络请求框架,这里后续会以Rxjava2+Retrofit 为例
  8. 统一工具类封装和api相关配置封装
    此篇文章我们先从BaseActivity、BaseFragment、BaseView、BaseViewModel讲起
    需要引入的库有
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    //放着没有及时回收造成RxJava内存泄漏
    implementation 'com.trello.rxlifecycle2:rxlifecycle-components:2.2.2'
    //Room的依赖引用
    implementation 'androidx.room:room-runtime:2.2.5'
    annotationProcessor 'androidx.room:room-compiler:2.2.5'

BaseActivity

一说到BaseActivity封装那么我们第一时间想到的就是不用重复findViewById和不用重复的setContentView,那么我们可以定义一个抽象方法

    /**
     * 初始化布局
     * @return 布局id
     */
    protected abstract int getLayoutId();

当然我们还可以封装统一的toolbar剩余每个页面都要引入toolbar,当然在引入toolbar之前肯定是先去掉原生的导航栏

    <style name="AppBaseTheme" parent="@style/Theme.AppCompat.Light.NoActionBar">
        <!--
            Theme customizations available in newer API levels can go in
            res/values-vXX/styles.xml, while customizations related to
            backward-compatibility can go here.
        -->
        <!--<item name="android:statusBarColor">@color/theme_backgroung_color</item>-->
        <!--<item name="android:windowBackground">@color/window_background</item>-->
        <item name="android:colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/themeColor</item>
    </style>
    <style name="AppTheme" parent="AppBaseTheme"/>

然后我们在application节点下引入theme主题即可
这时我们可以尝试引用一下databing的双向绑定特性了,我们新建一个toolbar样式类文件

/**
 * Create by CherishTang on 2020/3/25 0025
 * describe:toolbar配置
 */
public class ToolbarConfig extends BaseObservable {
    private String title;
    private @DrawableRes int backIconRes = R.mipmap.icon_fh_black;//toolbar返回按钮资源样式
    private boolean defaultTheme = true;//toolbar的menu主题,默认主题为黑色
    private int textColor = R.color.black;//标题字体颜色
    private int bgColor = R.color.white;//标题背景色
    private boolean isShowBackButton = true;//是否显示返回按钮

    public ToolbarConfig() {
    }
    
    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getBackIconRes() {
        return backIconRes;
    }

    public void setBackIconRes(int backIconRes) {
        this.backIconRes = backIconRes;
    }

    public boolean isDefaultTheme() {
        return defaultTheme;
    }

    public void setDefaultTheme(boolean defaultTheme) {
        this.defaultTheme = defaultTheme;
    }

    public int getTextColor() {
        return textColor;
    }

    public void setTextColor(int textColor) {
        this.textColor = textColor;
    }

    public int getBgColor() {
        return bgColor;
    }

    public void setBgColor(int bgColor) {
        this.bgColor = bgColor;
    }

    public boolean isShowBackButton() {
        return isShowBackButton;
    }

    public void setShowBackButton(boolean showBackButton) {
        isShowBackButton = showBackButton;
    }
}

然后新建一个base_activity.xml布局文件,引入统一的toolbar

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <variable
            name="toolbarConfig"
            type="com.jcloudsoft.demo.bean.base.ToolbarConfig" />

        <import type="android.view.View" />

        <import type="androidx.core.content.ContextCompat" />

        <import type="androidx.annotation.LayoutRes" />

        <import type="androidx.annotation.ColorRes" />
        <variable
            name="context"
            type="android.content.Context" />
    </data>

    <LinearLayout
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/main_bar"
            android:layout_width="match_parent"
            android:layout_height="@dimen/y96"
            android:background="@{ContextCompat.getColor(context,toolbarConfig.bgColor)}"
            android:minHeight="?attr/actionBarSize"
            app:navigationIcon="@{ContextCompat.getDrawable(context,toolbarConfig.backIconRes)}"
            android:theme="@style/ToolBarStyle_black">

            <TextView
                android:id="@+id/tv_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:ellipsize="end"
                android:maxLength="12"
                android:maxLines="1"
                android:text="@{toolbarConfig.title}"
                android:textColor="@{ContextCompat.getColor(context,toolbarConfig.textColor)}"
                android:textSize="@dimen/font_18"/>
        </androidx.appcompat.widget.Toolbar>

        <FrameLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </LinearLayout>
</layout>

关于上面的布局文件大概会有几个疑问:
1、context对象,在xml布局中我们是无法回去context的对象,但是我们可以利用databing双向绑定把this对象传递到xml布局中,或者你可以直接自定义一个方法return相同的返回数据,效果一样

bind.setVariable(BR.context, this);

2、样式@style/ToolBarStyle_black的问题,如果不懂的可以看下自定义toolbar的博文,直接贴代码,不做过多解释了,如果你想把toolbar样式更加炫酷,当然 你可以在此思路上增加更多的相关配置

    <style name="ToolBarStyle_black" parent="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
        <item name="actionMenuTextColor">@android:color/black</item> <!-- menu 敲定颜色-->
        <item name="android:textSize">@dimen/font_14</item> <!--  搞掂字体大小-->
        <item name="android:textStyle">normal</item>
        <item name="colorControlNormal">@color/black</item><!--图标颜色-->
        <item name="overlapAnchor">false</item>
    </style>

在activity中引入布局,但是问题来了,如果我们部分页面不需要toolbar怎么办?总不能setVisibility吧,这也太low了,我们可以先定义一个抽象方法,供子activity去控制toolbar的显示与隐藏

    /**
     * 是否引用toolbar
     * @return 默认显示
     */
    protected boolean hasToolBar() {
        return true;
    }

再定义一个标题文字方法

    /**
     * 设置toolbar的title
     * @return 标题
     */
    public abstract String setTitleBar();

然后我们如何去引用这些方法呢?当然是利用databing的双向绑定特性啦,

//toolbarConfig是xml定义的name,setToolbarStyle()是我们刚才新建的toolbar配置类
bind.setVariable(BR.toolbarConfig, setToolbarStyle())

当然我们需要暴露一个接口供子Activity去修改toolbar样式

    /**
     * 设置toolbar默认样式
     *
     * @return toolbar配置
     */
    public ToolbarConfig setToolbarStyle() {
        return new ToolBarSet().build();
    }

自定义toolbar样式,可以根据需求添加自己的方法

   /**
     * 自定义toolbar样式类
     */
    public class ToolBarSet {
        private ToolbarConfig toolbarConfig;

        public ToolBarSet() {
            if (toolbarConfig == null) {
                this.toolbarConfig = new ToolbarConfig();
            }
            toolbarConfig.setTitle(setTitleBar());
        }

        public ToolbarConfig getConfig(){
            return toolbarConfig;
        }

        public ToolbarConfig build() {
            return toolbarConfig;
        }

        public ToolBarSet setTitleTextColor(@ColorRes int colorRes) {
            toolbarConfig.setTextColor(colorRes);
            return this;
        }

        public ToolBarSet setBackIconRes(@DrawableRes int imgRes) {
            toolbarConfig.setBackIconRes(imgRes);
            return this;
        }

        public ToolBarSet setTitle(String title) {
            toolbarConfig.setTitle(title);
            return this;
        }

        public ToolBarSet setBgColor(@ColorRes int colorRes) {
            toolbarConfig.setBgColor(colorRes);
            return this;
        }

        public ToolBarSet setDefaultTheme(boolean defaultTheme) {
            toolbarConfig.setDefaultTheme(defaultTheme);
            return this;
        }

        public ToolBarSet setShowBackButton(boolean isShow) {
            toolbarConfig.setShowBackButton(isShow);
            return this;
        }
    }

那么toolbar的自定义样式绑定好了,可以我们引入和不引入toolbar的问题还没有解决呢?在databing中绑定布局文件写法如下,

ViewDataBinding VDB = DataBindingUtil.setContentView(this, R.layout.base_activity)

那么看这个,灵感是否有了呢,我们需要做到以下几点
1、在UI页面中我们绑定的布局不能是base_activity,需要是我们getLayoutId()返回的布局id
2、通过hasToolBar()方法去判断toolbar的隐藏显示
在databing中绑定布局,与原来的有些区别,但是他们都是要走系统的setContentView方法的,那么我们可以重写系统的setContentView方法去实现不同的绑定关系

    @Override
    public void setContentView(int layoutResID) {
        if (hasToolBar()) {//如果是引用toolbar布局的话我们根布局重写一下,需要引入base_activity作为根布局文件,然后把各ui页面的getLayoutId()定义的布局资源添加到根布局文件中去
            super.setContentView(R.layout.base_activity);
            FrameLayout container = findViewById(R.id.container);
            mToolbar = findViewById(R.id.main_bar);
            binding = DataBindingUtil.inflate(LayoutInflater.from(this), getLayoutId(), container, true);
            mToolbar.setNavigationOnClickListener(v -> finish());
        } else {//如果不需要toolbar的话,我们直接就以getLayoutId()的布局资源id作为根布局
            super.setContentView(layoutResID);
        }
    }

做好这个后我们就可以在onCreate中是去实现它啦

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (hasToolBar()) {
            toolbarBind = DataBindingUtil.setContentView(this, R.layout.base_activity);//我们需要定义一个属于toolbar的bind来控制toolbar的样式
            toolbarBind.setVariable(BR.context, this);//给view传递context对象
            toolbarBind.setVariable(BR.toolbarConfig, setToolbarStyle());
        } else {
            binding = DataBindingUtil.setContentView(this, getLayoutId());
        }
    }

那么到这里我们的toolbar样式就封装好了,当然我们Activity封装还没有完,我们可以添加一些公用方法

    /**
     * 初始化布局
     */
    public abstract void initView();

    /**
     * 设置数据
     */
    public abstract void initData(Bundle bundle);

但是说了半天我们还没有引入ViewModel呢,别急!我们先介绍下fragment封装,activity的源码下面我会贴在文章中

BaseFragment

有了activity封装的先例,我们fragment封装就简单多了,首先同样的定义一些我们需要的公用方法

    /**
     * 该抽象方法就是 onCreateView中需要的layoutID
     *
     * @return
     */
    protected abstract int getLayoutId();

    /**
     * 该抽象方法就是 初始化view
     *
     * @param view
     * @param savedInstanceState
     */
    protected abstract void initView(View view, Bundle savedInstanceState);

    /**
     * 执行数据的加载
     */
    protected abstract void initData(Bundle bundle);

在fragment中绑定资源文件要与activity有些许区别

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container
            , Bundle savedInstanceState) {
        binding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
        return binding.getRoot();
    }

在fragment中绑定的View布局我们后面可能还有用,所以我们定义一个View去接受一下它

    protected View mContentView;
        @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container
            , Bundle savedInstanceState) {
        binding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
        mContentView = binding.getRoot();
        return mContentView;
    }

然后去实现我们刚才自定义的抽象方法

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        initData(getArguments() == null ? new Bundle() : getArguments());
    }
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container
            , Bundle savedInstanceState) {
        binding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
        mContentView = binding.getRoot();
        initView(mContentView, savedInstanceState);
        return mContentView;
    }

那么fragment的简易封装要简单很多啦。

BaseView

在封装完了Activity和Fragment后发现少了很多公用方法,比如:Toast、加载框等等因为这些方法是fragment和Activity共有的,我们可以定义一个接口,然后让activity和fragment去实现他的方法体即可

public interface BaseView {
    /**
     * 显示dialog
     */
    void showLoading(String dialogMessage);
    /**
     * 更新dialog
     */
    void refreshLoading(String dialogMessage);

    /**
     * 隐藏 dialog
     */

    void hideLoading();

    /**
     * 显示错误信息
     *
     * @param msg
     */
    void showToast(String msg);

    /**
     * 错误码
     */
    void onErrorCode(BaseModelEntity model);

}

然后我们只需要在BaseActivity和BaseFragment中去实现他们的方法体即可

BaseViewModel

讲了这么多,要真正讲到正主了ViewModel。
在说这个前,可能有人说要引入LifeCycle。我可以回答:不需要!!!!!在androidx中已经默认实现了LifeCycle的接口
如果不是特殊需求的话上面的

    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'

这个库是可以不引入的,因为google已经给你做好了

public class BaseViewModel extends AndroidViewModel {
    public BaseViewModel(@NonNull Application application) {
        super(application);
    }
}

当然我们需要引入刚才的接口baseView

public class BaseViewModel<V extends BaseView> extends AndroidViewModel {
    protected V baseView;
    public BaseViewModel(@NonNull Application application) {
        super(application);
    }
    protected void setBaseView(V baseView) {
        this.baseView = baseView;
    }
    public V getBaseView() {
        return baseView;
    }
}

这个时候我们还不知道在ViewModel中需要什么,不要盲目的写,用到自然就知道了,先不如我们viewModel就写这么多,我们现在需要做的就是在BaseActivity中引入BaseViewModel,当然我们不同的Activity会有不同的ViewModel,所以我们不如让所有的ViewModel都继承BaseViewModel,在BaseActivity中以泛型引入;

public abstract class BaseActivity<VM extends BaseViewModel, VDB extends ViewDataBinding> extends AppCompatActivity implements BaseView {
    public Context context;
    Toolbar mToolbar;
    public VM mViewModel;
    protected VDB binding;
    private BaseActivityBinding toolbarBind;
    private CustomProgressDialog customProgressDialog;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        context = this;
        if (hasToolBar()) {
            toolbarBind = DataBindingUtil.setContentView(this, R.layout.base_activity);
            toolbarBind.setVariable(BR.context, this);//给view传递context对象
            toolbarBind.setVariable(BR.toolbarConfig, setToolbarStyle());
        } else {
            binding = DataBindingUtil.setContentView(this, getLayoutId());
        }
        createViewModel();
        initView();
        initData((getIntent() == null || getIntent().getExtras() == null) ? new Bundle() : getIntent().getExtras());
    }
    
    private void closeLoadingDialog() {
        if (customProgressDialog != null && customProgressDialog.isShowing()) {
            customProgressDialog.dismiss();
        }
    }

    private void showLoadingDialog(String dialogMessage) {
        if (customProgressDialog == null || !customProgressDialog.isShowing()) {
            customProgressDialog = new CustomProgressDialog(context);
            customProgressDialog.isShowBg(StringUtil.isEmpty(dialogMessage));
            customProgressDialog.setMessage(dialogMessage);
            customProgressDialog.show();
        }
    }

    @Override
    public void showLoading(String dialogMessage) {
        runOnUiThread(() -> showLoadingDialog(dialogMessage));
    }

    @Override
    public void refreshLoading(String dialogMessage) {
        runOnUiThread(() -> {
            if (customProgressDialog != null && customProgressDialog.isShowing()) {
                customProgressDialog.setMessage(dialogMessage);
            }
        });
    }

    @Override
    public void hideLoading() {
        runOnUiThread(this::closeLoadingDialog);
    }

    @Override
    public void showToast(String msg) {
        runOnUiThread(() -> {
            if (StringUtil.isEmpty(msg)) return;
            ToastUtils.showShort(this, msg);
        });
    }

    @Override
    public void onErrorCode(BaseModelEntity model) {
        if (model != null && (UserAccountHelper.isLoginPast(model.getCode()) || UserAccountHelper.isNoPermission(model.getCode()))) {
            LoginActivity.show(this, new Bundle());
        }
    }

}

我们现在对viewModel进行创建,如果你想给ViewModel添加构造方法可以通过他的工厂类去实现ViewModelProvider.Factory

    /**
     * 创建viewModel
     */
    public void createViewModel() {
        if (mViewModel == null) {
            Class modelClass;
            Type type = getClass().getGenericSuperclass();
            if (type instanceof ParameterizedType) {
                modelClass = (Class) ((ParameterizedType) type).getActualTypeArguments()[0];
            } else {
                //如果没有指定泛型参数,则默认使用BaseViewModel
                modelClass = BaseViewModel.class;
            }
            mViewModel = (VM) new ViewModelProvider(this).get(modelClass);
            mViewModel.setBaseView(createBaseView());
        }
    }

在这里我们setBaseView方法绑定activity与ViewModel

    protected BaseView createBaseView() {
        return this;
    }

这里的CustomProgressDialog.java可以自己去写,一个全局加载弹框dialog
到这里我们BaseActivity基本封装好了,下面我们继续在BaseFragment中去引用ViewModel;同样的我们以泛型形式引入

public abstract class BaseFragment<VM extends BaseViewModel, VDB extends ViewDataBinding> extends Fragment{
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container
            , Bundle savedInstanceState) {
        binding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
        mContentView = binding.getRoot();
        createViewModel();
        initView(mContentView, savedInstanceState);
        return mContentView;
    }
}

这里创建ViewModel对象与activity中是一样的

    public void createViewModel() {
        if (mViewModel == null) {
            Class modelClass;
            Type type = getClass().getGenericSuperclass();
            if (type instanceof ParameterizedType) {
                modelClass = (Class) ((ParameterizedType) type).getActualTypeArguments()[0];
            } else {
                //如果没有指定泛型参数,则默认使用BaseViewModel
                modelClass = BaseViewModel.class;
            }
            mViewModel = (VM) new ViewModelProvider(this).get(modelClass);
            mViewModel.setBaseView(createBaseView());
        }
    }

    protected BaseView createBaseView(){
        return this;
    }

然后我们在BaseFragment中去实现BaseView 接口类

public abstract class BaseFragment<VM extends BaseViewModel, VDB extends ViewDataBinding> extends Fragment implements BaseView {
    protected Activity mActivity;

    public CustomProgressDialog customProgressDialog;
    protected VM mViewModel;
    protected View mContentView;
    protected VDB binding;
    /**
     * 获得全局的,防止使用getActivity()为空
     *
     * @param context
     */
    @Override
    public void onAttach(@NotNull Context context) {
        super.onAttach(context);
        this.mActivity = (Activity) context;
    }
    
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container
            , Bundle savedInstanceState) {
        binding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
        mContentView = binding.getRoot();
        createViewModel();
        initView(mContentView, savedInstanceState);
        return mContentView;
    }

    public void createViewModel() {
        if (mViewModel == null) {
            Class modelClass;
            Type type = getClass().getGenericSuperclass();
            if (type instanceof ParameterizedType) {
                modelClass = (Class) ((ParameterizedType) type).getActualTypeArguments()[0];
            } else {
                //如果没有指定泛型参数,则默认使用BaseViewModel
                modelClass = BaseViewModel.class;
            }
            mViewModel = (VM) new ViewModelProvider(this).get(modelClass);
            mViewModel.setBaseView(createBaseView());
        }
    }

    protected BaseView createBaseView(){
        return this;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        initData(getArguments() == null ? new Bundle() : getArguments());
    }

    /**
     * 该抽象方法就是 onCreateView中需要的layoutID
     *
     * @return
     */
    protected abstract int getLayoutId();

    /**
     * 该抽象方法就是 初始化view
     *
     * @param view
     * @param savedInstanceState
     */
    protected abstract void initView(View view, Bundle savedInstanceState);

    /**
     * 执行数据的加载
     */
    protected abstract void initData(Bundle bundle);

    /**
     * 关闭弹框
     */
    private void closeLoadingDialog() {
        if (customProgressDialog != null && customProgressDialog.isShowing()) {
            customProgressDialog.dismiss();
        }
    }

    /**
     * 显示加载弹框
     *
     * @param dialogMessage 弹框内容,如果内容为空则不展示文字部分
     */
    private void showLoadingDialog(String dialogMessage) {
        if (customProgressDialog == null || !customProgressDialog.isShowing()) {
            customProgressDialog = new CustomProgressDialog(getActivity());
            customProgressDialog.isShowBg(StringUtil.isEmpty(dialogMessage));
            customProgressDialog.setMessage(dialogMessage);
            customProgressDialog.show();
        }
    }

    @Override
    public void showLoading(String dialogMessage) {
        if (getActivity() != null && !getActivity().isFinishing()) {
            getActivity().runOnUiThread(() -> showLoadingDialog(dialogMessage));
        }
    }

    @Override
    public void hideLoading() {
        if (getActivity() != null && !getActivity().isFinishing()) {
            getActivity().runOnUiThread(this::closeLoadingDialog);
        }
    }

    @Override
    public void refreshLoading(String dialogMessage) {
        if (getActivity() != null && !getActivity().isFinishing()) {
            getActivity().runOnUiThread(() -> {
                if (customProgressDialog != null && customProgressDialog.isShowing()) {
                    customProgressDialog.setMessage(dialogMessage);
                }
            });
        }
    }

    @Override
    public void showToast(String msg) {
        ToastUtils.showShort(getActivity(), msg);
    }

    @Override
    public void onErrorCode(BaseModelEntity model) {
        if (model != null && (UserAccountHelper.isLoginPast(model.getCode())||UserAccountHelper.isNoPermission(model.getCode()))) {
            Bundle bundle = new Bundle();
            LoginActivity.show(this, bundle);
        }
    }
}

这样我们可以简易的书写代码了

public class TestActivity extends BaseActivity<MainViewModel, BaseFragmentContainerBinding> {

    @Override
    protected int getLayoutId() {
        return R.layout.base_fragment_container;
    }

    @Override
    public String setTitleBar() {
        return "测试";
    }

    @Override
    public void initView() {
        
    }

    @Override
    public void initData(Bundle bundle) {

    }

}

fragment

public class TestFragment extends BaseFragment<TestViewModel, ActivityTestBinding> {
    @Override
    protected int getLayoutId() {
        return R.layout.activity_test;
    }

    @Override
    protected void initView(View view, Bundle savedInstanceState) {
    }

    @Override
    protected void initData(Bundle bundle) {
        binding.tvTest.setText("这是"+bundle.getInt(MainViewModel.ARG_PAGE)+"个页面");
    }
}

写在最后,后续会上传demo
这里注意踩坑!!!如果在基类中引入其他泛型的话,需要把泛型写在最后不能写在ViewDataBinding、BaseViewModel前面,错误写法

BaseActivity<T extends BeseBean,VM extends BaseViewModel, VDB extends ViewDataBinding>

正确写法

BaseActivity<VM extends BaseViewModel, VDB extends ViewDataBinding,T extends BeseBean>

后续我们继续封装

  1. 统一异常错误处理BaseException;
  2. 网络框架的封装Rxjava2+Retrofit
  3. 接口数据统一数据
  4. application与相关api参数配置
  5. 工具类封装
    关于网络请求和统一异常处理可以查看这篇博文关于mvvm简易封装(二)
相关标签: MVVM