Android之 MVC到MVVM架构发展和封装

article/2025/9/30 8:34:06

一  简介

1.1 软件架构发展趋势是解耦,即分离数据层和视图层,使得数据层专注于业务的数据和逻辑处理。从而提高代码的可读可编辑效率,提高团队协作能力,项目的生产能力,降低后期维护成本。

1.2 Android架构发展MVC -> MVP -> MVVM,目前最流程的MVVM,配合google的jetpack开发工具可以轻松实现MVVM架构

二 MVC

2.1 概念MVC (Model-View-Controller, 模型-视图-控制器)

  • 模型层 (Model):业务逻辑对应的数据模型,无View无关,而与业务相关;
  • 视图层 (View):一般使用XML或者Java对界面进行描述;
  • 控制层 (Controllor):在Android中通常指Activity和Fragment,或者由其控制的业务类

2.2 基类封装BaseActivity

title.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/rl_title_layout"android:layout_width="match_parent"android:layout_height="44dp"android:background="@color/white"><ImageViewandroid:id="@+id/iv_back"android:layout_width="wrap_content"android:layout_height="match_parent"android:paddingLeft="15dp"android:paddingRight="15dp"android:src="@mipmap/back" /><TextViewandroid:id="@+id/tv_head_title"style="@style/Font_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:gravity="center"android:includeFontPadding="false"android:singleLine="true" /><TextViewandroid:id="@+id/tv_right_text"style="@style/Font_title"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:layout_marginRight="10dp"android:gravity="center"android:includeFontPadding="false"android:paddingLeft="5dp"android:paddingRight="5dp"android:singleLine="true"android:textColor="@color/color_3e"android:textSize="15sp" /><ImageViewandroid:id="@+id/iv_right"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:layout_marginRight="10dp"android:gravity="center"android:paddingLeft="5dp"android:paddingRight="5dp" />
</RelativeLayout>

activity_base.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><include layout="@layout/title"/><FrameLayoutandroid:id="@+id/root_layout"android:layout_width="match_parent"android:layout_height="match_parent"/>
</LinearLayout>

BaseActivity.java

public abstract class BaseActivity extends AppCompatActivity {public Activity mContext;private RelativeLayout rlTitleLayout;private ImageView ivBack;private TextView tvHeadTitle;private TextView tvRightText;private ImageView ivRight;private FrameLayout rootLayout;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);mContext = this;setContentView(R.layout.activity_base);initRootView();}private void initRootView() {rlTitleLayout = (RelativeLayout) findViewById(R.id.rl_title_layout);ivBack = (ImageView) findViewById(R.id.iv_back);tvHeadTitle = (TextView) findViewById(R.id.tv_head_title);tvRightText = (TextView) findViewById(R.id.tv_right_text);ivRight = (ImageView) findViewById(R.id.iv_right);rootLayout = (FrameLayout) findViewById(R.id.root_layout);ivBack.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {finish();}});rootLayout.addView(View.inflate(this, getLayoutResId(), null));initView();initData();}public void hideTitleLayout() {rlTitleLayout.setVisibility(View.GONE);}public void setTitleText(String text) {tvHeadTitle.setText(text);}public void setTitleText(int resId) {tvHeadTitle.setText(getResources().getText(resId));}public void setRightText(int resId, View.OnClickListener onClickListener) {tvRightText.setText(getResources().getText(resId));tvRightText.setOnClickListener(onClickListener);}public void setRightImage(int resId, View.OnClickListener onClickListener) {ivRight.setImageResource(resId);ivRight.setOnClickListener(onClickListener);}protected abstract int getLayoutResId();public abstract void initView();/*** 初始化数据*/public abstract void initData();
}

使用MainActivity.java

public class MainActivity extends BaseActivity implements View.OnClickListener {private FrameLayout flMainFragment;private LinearLayout llBottom;private LinearLayout llFirst;private ImageView ivFirst;private TextView tvFirst;private LinearLayout llTwo;private ImageView ivTwo;private TextView tvTwo;private LinearLayout llThree;private ImageView ivThree;private TextView tvThree;private FragmentTransaction transaction;private HomeFragment homeFragment;private CommunityFragment communityFragment;private MeFragment meFragment;@Overrideprotected int getLayoutResId() {return R.layout.activity_main;}@Overridepublic void initView() {//UltimateBarXUtils.setNoAppBar(this, true);hideTitleLayout();flMainFragment = (FrameLayout) findViewById(R.id.fl_main_fragment);llBottom = (LinearLayout) findViewById(R.id.ll_bottom);llFirst = (LinearLayout) findViewById(R.id.ll_first);ivFirst = (ImageView) findViewById(R.id.iv_first);tvFirst = (TextView) findViewById(R.id.tv_first);llTwo = (LinearLayout) findViewById(R.id.ll_two);ivTwo = (ImageView) findViewById(R.id.iv_two);tvTwo = (TextView) findViewById(R.id.tv_two);llThree = (LinearLayout) findViewById(R.id.ll_three);ivThree = (ImageView) findViewById(R.id.iv_three);tvThree = (TextView) findViewById(R.id.tv_three);}@Overridepublic void initData() {llFirst.setOnClickListener(this);llTwo.setOnClickListener(this);llThree.setOnClickListener(this);setTabSelection(1);}@Overridepublic void onClick(View view) {switch (view.getId()){case R.id.ll_first:setTabSelection(1);break;case R.id.ll_two:setTabSelection(2);break;case R.id.ll_three:setTabSelection(3);break;}}public void setTabSelection(int index) {transaction = getSupportFragmentManager().beginTransaction();hideFragments(transaction);resetBtn();switch (index) {case 1:if (homeFragment == null) {homeFragment = new HomeFragment();transaction.add(R.id.fl_main_fragment, homeFragment);}transaction.show(homeFragment);ivFirst.setImageResource(R.mipmap.tab1_se);tvFirst.setTextColor(getResources().getColor(R.color.color_blue_tab));break;case 2:if (communityFragment == null) {communityFragment = new CommunityFragment();transaction.add(R.id.fl_main_fragment, communityFragment);}transaction.show(communityFragment);ivTwo.setImageResource(R.mipmap.tab2_se);tvTwo.setTextColor(getResources().getColor(R.color.color_blue_tab));break;case 3:if (meFragment == null) {meFragment = new MeFragment();transaction.add(R.id.fl_main_fragment, meFragment);}transaction.show(meFragment);ivThree.setImageResource(R.mipmap.tab3_se);tvThree.setTextColor(getResources().getColor(R.color.color_blue_tab));break;}transaction.commitAllowingStateLoss();}private void resetBtn() {ivFirst.setImageResource(R.mipmap.tab1);ivTwo.setImageResource(R.mipmap.tab2);ivThree.setImageResource(R.mipmap.tab3);tvFirst.setTextColor(getResources().getColor(R.color.color_d8d8d8));tvTwo.setTextColor(getResources().getColor(R.color.color_d8d8d8));tvThree.setTextColor(getResources().getColor(R.color.color_d8d8d8));}private void hideFragments(FragmentTransaction transaction) {if (homeFragment != null) {transaction.hide(homeFragment);}if (communityFragment != null) {transaction.hide(communityFragment);}if (meFragment != null) {transaction.hide(meFragment);}}
}

2.3 基类封装BaseFragment

BaseFragment.java

public abstract class BaseFragment extends Fragment {public Activity mContext;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mContext = getActivity();}@Nullable@Overridepublic View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {View view = inflater.inflate(getLayoutId(), container, false);initView(view, savedInstanceState);initData();return view;}/*** Fragment数据的懒加载.*/protected boolean isVisible;@Overridepublic void setUserVisibleHint(boolean isVisibleToUser) {super.setUserVisibleHint(isVisibleToUser);if (getUserVisibleHint()) {isVisible = true;lazyLoad();} else {isVisible = false;}}protected void lazyLoad() {}public abstract int getLayoutId();public abstract void initView(View view, @Nullable Bundle savedInstanceState);/*** 初始化数据*/public abstract void initData();
}

fragment_home.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><Viewandroid:id="@+id/view_statues"android:layout_width="match_parent"android:layout_height="0dp"/><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="40dp"><TextViewstyle="@style/Font_black"android:layout_height="24dp"android:layout_centerVertical="true"android:layout_marginLeft="15dp"android:background="@drawable/shape_bg_color_f0eff5_30"android:gravity="center"android:paddingLeft="12dp"android:paddingRight="12dp"android:text="@string/home_property"android:textColor="@color/color_3e"android:textSize="15sp"android:textStyle="bold" /><ImageViewandroid:id="@+id/iv_search"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_alignParentRight="true"android:layout_marginRight="10dp"android:paddingLeft="5dp"android:paddingRight="5dp"android:src="@mipmap/home_search" /></RelativeLayout><androidx.core.widget.NestedScrollViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:overScrollMode="never"android:scrollbars="none"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"></LinearLayout></androidx.core.widget.NestedScrollView>
</LinearLayout>

FragmentHome.java

public class HomeFragment extends BaseFragment{private View viewStatues;@Overridepublic int getLayoutId() {return R.layout.fragment_home;}@Overridepublic void initView(View view, @Nullable Bundle savedInstanceState) {viewStatues = (View) view.findViewById(R.id.view_statues);LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) viewStatues.getLayoutParams();layoutParams.height = ScreenUtils.getStatusHeight(mContext);viewStatues.setLayoutParams(layoutParams);}@Overridepublic void initData() {}}

三 MVP

3.1 概念MVP (Model-View-Presenter) 

  • 模型层 (Model):主要提供数据存取功能。
  • 视图层 (View):处理用户事件和视图。在Android中,可能是指Activity、Fragment或者View。
  • 展示层 (Presenter):负责通过Model存取书数据,连接View和Model,从Model中取出数据交给View。

3.2 封装基类

新建Presenter接口,Presenter.java

public interface Presenter<V extends BaseView> {void attachView(V view);void detachView();
}

新建Presenter基类BasePresenter.java

public class BasePresenter<V> implements IPresenter<V> {protected WeakReference<V> mViewRef; //View接口类型的弱引用/*** 建立关联* @param view*/public void attachView(V view){mViewRef=new WeakReference<V>(view);}/*** 解除关联*/public void detachView(){if (mViewRef!=null){mViewRef.clear();mViewRef=null;}}/*** 判断是否与View建立关联* @return*/public boolean isViewAttach(){return mViewRef != null && mViewRef.get() != null;}/*** 获取View* @return*/protected V getView(){return mViewRef.get();}}

新建Activity基类BaseActivity.java

public abstract class BaseActivity<T extends BasePresenter> extends AppCompatActivity {protected T mPresenter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(getLayoutId());mPresenter=createPresneter();//关联viewif (mPresenter != null) {mPresenter.attachView(this);}init();}protected abstract T createPresneter();protected abstract void init();protected abstract int getLayoutId();@Overrideprotected void onDestroy() {super.onDestroy();//解除if (mPresenter != null) {mPresenter.detachView();}}
}

新建Fragment基类BaseFragment.java

public abstract class BaseFragment<T extends BasePresenter> extends Fragment {protected T mPresenter;@Nullable@Overridepublic View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {View mView = inflater.inflate(getLayoutId(), null);mPresenter = createPresneter();//关联viewif (mPresenter != null) {mPresenter.attachView(this);}init(mView);return mView;}protected abstract T createPresneter();/*** 初始化*/protected abstract void init(View view);/*** 布局ID** @return*/protected abstract int getLayoutId();@Overridepublic void onDestroy() {super.onDestroy();//解除if (mPresenter != null) {mPresenter.detachView();}}
}

3.3 实战,登录功能

新建登录视图操作接口,LoginView.java

public interface LoginView {String getUserName();String getPassWord();void toMainActivity();void showToast(String message);void finish();void showWaitDialog(String message);void hideWaitDialog();
}

 新建数据操作接口,LoginModel.java

public interface LoginModel {void login();
}

新建桥梁连接Model合View, LoginPresenter.java

public class LoginPresenter extends BasePresenter<LoginView> implements LoginModel {@Overridepublic void login() {getView().showWaitDialog("正在登录...");String userName=getView().getUserName();String password=getView().getPassWord();String result="";if(StringUtils.isEmpty(userName)){result="用户名不能为空";getView().hideWaitDialog();getView().showToast(result);return;}if(StringUtils.isEmpty(password)){result="密码不能为空";getView().hideWaitDialog();getView().showToast(result);return;}RetrofitClient.getmInstance().postMapLogin(userName, password, new BaseSubscriber<HttpResponse<UserInfo>>() {@Overridepublic void onError(ExceptionHandle.ResponeThrowable e) {getView().hideWaitDialog();getView().showToast(e.message);}@Overridepublic void onNext(HttpResponse<UserInfo> userInfoHttpResponse) {if (userInfoHttpResponse.getStatus() == 1) {UserInfo userInfo = userInfoHttpResponse.getReturnX();getView().hideWaitDialog();getView().showToast("登录成功");getView().toMainActivity();getView().finish();} else {getView().hideWaitDialog();getView().showToast(userInfoHttpResponse.getInfo());}}});}
}

 新建登录页面LoginActivity.java

public class LoginActivity extends BaseActivity implements LoginView {private MaterialEditText metName;private MaterialEditText metPassword;private Button btnLogin;LoginPresenter loginPresenter;@Overrideprotected int getLayoutId() {return R.layout.activity_login;}@Overrideprotected BasePresenter createPresneter() {loginPresenter = new LoginPresenter();return loginPresenter;}@Overrideprotected void init() {metName = (MaterialEditText) findViewById(R.id.met_name);metPassword = (MaterialEditText) findViewById(R.id.met_password);btnLogin = (Button) findViewById(R.id.btn_login);btnLogin.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {loginPresenter.login();}});}@Overridepublic String getUserName() {return  metName.getText().toString();}@Overridepublic String getPassWord() {return metPassword.getText().toString();}@Overridepublic void toMainActivity() {startActivity(new Intent(this,MainActivity.class));}@Overridepublic void showToast(String message) {ToastUtils.show(this,message);}@Overridepublic void finish() {}@Overridepublic void showWaitDialog(String message) {}@Overridepublic void hideWaitDialog() {}@Overrideprotected void onDestroy() {super.onDestroy();}
}

四 MVVM

4.1 概念MVVM(Model-View-ViewModel)

  • 模型层 (Model):负责从各种数据源中获取数据;
  • 视图层 (View):在 Android 中对应于 Activity 和 Fragment,用于展示给用户和处理用户交互,会驱动 ViewModel 从 Model 中获取数据;
  • ViewModel 层:用于将 Model 和 View 进行关联,我们可以在 View 中通过 ViewModel 从 Model 中获取数据;当获取到了数据之后,会通过自动绑定,比如 DataBinding,来将结果自动刷新到界面上。

4.2 Android中使用ViewModel + LiveData + DataBinding来快速实现MVVM架构的搭建,

ViewModel:① 绑定Activity ,② 页面布局绘制,③ 实现业务逻辑如登录功能
LiveData:① 可修改数据 ,② 数据观察
DataBinding:① 单向绑定 ,② 双向绑定

4.2 封装MVVM基类

在Model级build.gradle文件里开启dataBinding支持

android {compileSdk 32defaultConfig {applicationId "com.bob.diary"minSdk 21targetSdk 32versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}dataBinding {enabled = true}
}

创建ViewModel基类,绑定Activity,BaseViewModel.java,BaseActivity.java 

public abstract class BaseViewModel extends ViewModel implements DefaultLifecycleObserver {public Activity activity;public void setActivity(Activity activity) {this.activity = activity;}}

创建Activity基类,activity_base.xml

<?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"xmlns:tools="http://schemas.android.com/tools"><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><include layout="@layout/layout_title_bar"android:id="@+id/layout_title_root"/><FrameLayoutandroid:id="@+id/fl_content_container"android:layout_width="0dp"android:layout_height="0dp"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintTop_toBottomOf="@id/layout_title_root"/></androidx.constraintlayout.widget.ConstraintLayout>
</layout>
public abstract class BaseActivity<VM extends BaseViewModel, DB extends ViewDataBinding>extends AppCompatActivity {public DB mDataBinding;protected VM mViewModel;protected ActivityBaseBinding activityBaseBinding;protected Activity mContext;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {mContext=this;super.onCreate(savedInstanceState);activityBaseBinding = DataBindingUtil.setContentView(this, R.layout.activity_base);mDataBinding = DataBindingUtil.inflate(getLayoutInflater(), getLayoutResId(),activityBaseBinding.flContentContainer, true);initViewModel();bindViewModel();if (mDataBinding != null) {mDataBinding.setLifecycleOwner(this);}//ViewModel订阅生命周期事件if (mViewModel != null) {getLifecycle().addObserver(mViewModel);mViewModel.setActivity(this);}init();}/*** 获取当前页面的布局资源ID** @return 布局资源ID*/protected abstract int getLayoutResId();/*** 初始化ViewModel*/protected abstract void initViewModel();/*** 绑定ViewModel*/protected abstract void bindViewModel();/*** 初始化*/protected abstract void init();/*** 设置标题*/public void setTitle(String title) {UltimateBarXUtils.setAppBar(this, true, R.color.white);ConstraintLayout layoutTitleRoot = findViewById(R.id.layout_title_root);layoutTitleRoot.setVisibility(View.VISIBLE);ImageView ivBack = findViewById(R.id.iv_back);TextView tvTitleMiddle = findViewById(R.id.tv_title_middle);ivBack.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {finish();}});tvTitleMiddle.setText(title);}}

使用,以登录为例,新建登录model,LoginModel.java

public class LoginModel extends BaseViewModel {//LiveData监听器public MutableLiveData<List<User>> userLiveData;public LoginModel() {userLiveData = new MutableLiveData<>();}//查询用户public void daoQueryAllUser() {//通知数据变化List<User> userList = DaoUserUtils.getInstance().daoQueryAllUser();userLiveData.postValue(userList);}
}

新建登录页面,activity_login.xml和LoginActivity.java

<?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></data><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/color_faf1ed"android:gravity="center_vertical"android:orientation="vertical"><LinearLayoutandroid:id="@+id/ll_login_layout"android:layout_width="match_parent"android:layout_height="0dp"android:gravity="center_vertical"android:orientation="vertical"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintBottom_toTopOf="@id/ll_policy_layout"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="-42dp"android:orientation="vertical"android:paddingLeft="@dimen/dp_32"android:paddingRight="@dimen/dp_32"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/login_welcome_login"android:textColor="#E9756E"android:textSize="@dimen/sp_24"android:textStyle="bold" /><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="@dimen/dp_40"android:layout_marginTop="@dimen/dp_30"><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:src="@mipmap/login_account"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintTop_toTopOf="parent" /><EditTextandroid:id="@+id/et_account"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginLeft="@dimen/dp_30"android:background="@drawable/bg_orange_border_bottom"android:hint="@string/register_enter_account"android:maxLength="15"android:textColor="@color/color_e9756e"android:textColorHint="@color/color_e9756e"android:textSize="@dimen/sp_16" /></RelativeLayout><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="@dimen/dp_40"android:layout_marginTop="@dimen/dp_30"><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:src="@mipmap/login_password"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintTop_toTopOf="parent" /><EditTextandroid:id="@+id/et_password"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginLeft="@dimen/dp_30"android:background="@drawable/bg_orange_border_bottom"android:hint="@string/register_enter_password"android:inputType="textPassword"android:maxLength="15"android:textColor="@color/color_e9756e"android:textColorHint="@color/color_e9756e"android:textSize="@dimen/sp_16" /><ImageViewandroid:id="@+id/iv_see"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:paddingLeft="@dimen/dp_10"android:paddingRight="@dimen/dp_10"android:src="@mipmap/login_look_off" /></RelativeLayout><TextViewandroid:id="@+id/tv_register"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="right"android:padding="@dimen/dp_10"android:text="@string/login_register_account"android:textColor="#E9756E"android:textSize="@dimen/sp_12" /></LinearLayout><TextViewandroid:id="@+id/tv_confirm"android:layout_width="match_parent"android:layout_height="@dimen/dp_48"android:layout_marginLeft="@dimen/dp_15"android:layout_marginTop="@dimen/dp_70"android:layout_marginRight="@dimen/dp_15"android:background="@drawable/bg_orange_10"android:gravity="center"android:text="@string/login_to"android:textColor="@color/white"android:textSize="@dimen/sp_18"android:textStyle="bold"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toBottomOf="@id/ll_login_layout" /></LinearLayout><LinearLayoutandroid:id="@+id/ll_policy_layout"android:layout_width="0dp"android:layout_height="@dimen/dp_60"android:layout_marginLeft="@dimen/dp_15"android:layout_marginRight="@dimen/dp_15"android:orientation="horizontal"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toBottomOf="@id/ll_login_layout"><CheckBoxandroid:id="@+id/cb_check"android:layout_width="wrap_content"android:layout_height="wrap_content"android:button="@drawable/selector_checked"android:paddingTop="@dimen/dp_5"android:paddingBottom="@dimen/dp_5"android:paddingLeft="@dimen/dp_5"android:paddingRight="@dimen/dp_3"android:textColor="@color/color_3d3d3d"android:textSize="@dimen/sp_12" /><TextViewandroid:id="@+id/tv_policy"android:layout_width="wrap_content"android:layout_height="wrap_content"android:gravity="left|top"android:orientation="horizontal"android:text="@string/login_agreement_agree"android:textColor="@color/color_3d3d3d"android:textSize="@dimen/sp_12" /></LinearLayout></androidx.constraintlayout.widget.ConstraintLayout></layout>
public class LoginActivity extends BaseActivity<LoginModel, ActivityLoginBinding> {private boolean canSee;@Overrideprotected int getLayoutResId() {return R.layout.activity_login;}@Overrideprotected void initViewModel() {mViewModel = ViewModelProviders.of(this).get(LoginModel.class);}@Overrideprotected void bindViewModel() {//监听数据变化mViewModel.userLiveData.observe(this, new Observer<List<User>>() {@Overridepublic void onChanged(List<User> users) {User user = users.get(0);if (user == null) {ToastHelp.showToast("用户不存在");return;}startActivity(MainActivity.class);finish();}});}@Overrideprotected void init() {//登录点击事件mDataBinding.tvConfirm.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {String account = mDataBinding.etAccount.getText().toString();String password = mDataBinding.etPassword.getText().toString();if (TextUtils.isEmpty(account)) {ToastHelp.showToast(getResources().getString(R.string.register_enter_account));return;}if (TextUtils.isEmpty(password)) {ToastHelp.showToast(getResources().getString(R.string.register_enter_password));return;}mViewModel.daoQueryAllUser();}});}
}

4.3 BaseFragment封装

public abstract class BaseFragment<VM extends BaseViewModel, DB extends ViewDataBinding>extends Fragment {protected DB mDataBinding;protected VM mViewModel;private FragmentBaseBinding fragmentBaseBinding;protected Activity mContext;@Overridepublic void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);mContext=getActivity();initViewModel();// ViewModel订阅生命周期事件if (mViewModel != null) {getLifecycle().addObserver(mViewModel);} }@Nullable@Overridepublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {fragmentBaseBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_base, container, false);mDataBinding = DataBindingUtil.inflate(inflater, getLayoutResId(),fragmentBaseBinding.flContentContainer, true);bindViewModel();mDataBinding.setLifecycleOwner(this);init();return fragmentBaseBinding.getRoot();}/*** 获取当前页面的布局资源ID** @return 布局资源ID*/protected abstract int getLayoutResId();/*** 初始化ViewModel*/protected abstract void initViewModel();/*** 绑定ViewModel*/protected abstract void bindViewModel();/*** 初始化*/protected abstract void init();protected boolean isVisible;/*** 在这里实现Fragment数据的缓加载.** @param isVisibleToUser*/@Overridepublic void setUserVisibleHint(boolean isVisibleToUser) {super.setUserVisibleHint(isVisibleToUser);if (getUserVisibleHint()) {isVisible = true;onVisible();} else {isVisible = false;onInvisible();}}protected void onVisible() {lazyLoad();}protected void lazyLoad() {}protected void onInvisible() {} 
}

BaseFragment的使用,以首页为例,新建HomeModel.java

public class HomeModel extends BaseViewModel {//LiveData监听器public MutableLiveData<List<User>> userLiveData;public LoginModel() {userLiveData = new MutableLiveData<>();}//查询用户public void daoQueryAllUser() {//通知数据变化List<User> userList = DaoUserUtils.getInstance().daoQueryAllUser();userLiveData.postValue(userList);}}

新建HomeFragment.java

public class HomeFragment extends BaseFragment<HomeModel, FragmentHomeBinding> implements View.OnClickListener {@Overrideprotected boolean isEventBus() {return true;}@Overrideprotected int getLayoutResId() {return R.layout.fragment_home;}@Overrideprotected void initViewModel() {mViewModel = ViewModelProviders.of(getActivity()).get(HomeModel.class);}@Overrideprotected void bindViewModel() {mDataBinding.setModel(mViewModel);}@Overrideprotected void init() {mViewModel.daoQueryAllUser();//监听数据变化mViewModel.userLiveData.observe(this, new Observer<List<User>>() {@Overridepublic void onChanged(List<User> users) {//加载用户信息if(users.size()>0){User user=users.get(0);GlideUtil.loadImageView(mContext,user.getAvatar(),mDataBinding.ivUser,R.mipmap.default_user);mDataBinding.tvNickName.setText(user.getName());}}});}
}

五 总结,三种架构各自的特点

5.1 MVC特点

  • 优势:简单易用,View接收用户操作,通过Controller去处理业务逻辑,并通过Model去获取/更新数据,然后Model层又将最新的数据传回View层进行页面展示。
  • 劣势:由于XML布局能力弱,我们的View层的很多操作都是写在Activity/Fragment中,同时,Controller、Model层的代码也大都写在Activity/Fragment中,这就会导致一个问题,当业务逻辑比较复杂时,Activity/Fragment中的代码量会很大,其违背了类单一职责,不利于后续扩展及维护

5.2 MVP特点

  • View层接收用户操作,并通过持有的Presenter去处理业务逻辑,请求数据;接着Presenter层通过Model去获取数据,然后Model又将最新的数据传回Presenter层,Presenter层又持有View层的引用,进而将数据传给View层进行展示
  • 与MCVC相比,View层与Model层不再交互,而是通过Presenter去进行联系
  • MVP是面向接口编程,Model/View/Presenter每层的职责分工明确,当业务复杂时,整个流程逻辑也是很清晰的
  • Presenter会被抽象成IPresenter接口及其一些列方法,每当实现一个功能时,都需要编写多个接口及其对应的方法,实现起来相对比较繁琐,而且每次有改动时,对应的接口方法也基本都会再去改动
  • View层与Presenter层相互持有,当View层关闭时,由于Presenter层不是生命周期感知的,可能会导致内存泄漏甚至是崩溃。如果你的项目中使用了RxJava,可以使用 配合Rxjava自动解绑

 5.3 MVVM特点

  • View层接收用户操作,并通过持有的ViewModel去处理业务逻辑,请求数据
  • ViewModel层通过Model去获取数据,然后Model又将最新的数据传回ViewModel层,这里ViewModel与Presenter所做的事基本是一样的
  • View层会通过观察者模式监听ViewModel层的数据变化,当有新数据时,View层能自动收到新数据并刷新界面,所以ViewModel不会也不能持有View层的引用。

http://chatgpt.dhexx.cn/article/KgWvaXKR.shtml

相关文章

安卓mvvm

AndroidX的意思是android extension libraries, 也就是安卓扩展包 AndroidX其实是Jetpack类库的命名空间 (190条消息) AndroidX初识_Neda Wang的博客-CSDN博客https://blog.csdn.net/weixin_38261570/article/details/111500044 viewmodel Android 面试总结 - ViewModel - 知…

Android MVVM的实现

Android MVVM的实现 前言&#xff1a; 在我们写一些项目的时候&#xff0c;通常会对一些常用的一些常用功能进行抽象封装&#xff0c;简单例子&#xff1a;比如BaseActivity&#xff0c;BaseFragment等等…一般这些Base会去承载一些比如标题栏&#xff0c;主题之类的工作&…

Android MVI框架搭建与使用

MVI框架搭建与使用 前言正文一、创建项目① 配置AndroidManifest.xml② 配置app的build.gradle 二、网络请求① 生成数据类② 接口类③ 网络请求工具类 三、意图与状态① 创建意图② 创建状态 四、ViewModel① 创建存储库② 创建ViewModel③ 创建ViewModel工厂 五、UI① 列表适…

Android MVVN 使用入门

MVVM&#xff08;Model-View-ViewModel&#xff09;是一种基于数据绑定的设计模式&#xff0c;它与传统的 MVC 和 MVP 模式相比&#xff0c;更加适合处理复杂的 UI 逻辑和数据展示。在 Android 开发中&#xff0c;MVVM 通常使用 Data Binding 和 ViewModel 实现。 下面是一个简…

mvnw的使用

1、什么是mvnw mvnw是Maven Wrapper的缩写。我们安装Maven时&#xff0c;默认系统所有项目都会使用全局安装的这个Maven版本。但对于某些项目来说&#xff0c;它可能必须使用某个特定的Maven版本&#xff0c;这时就可以使用Maven Wrapper&#xff0c;它可以负责给这个特定的项…

快速查找参考文献影响因子——ScholarScope

前言&#xff1a; 最初看到的关于查看影响因子的插件有&#xff1a;pubmedy, pubmed plus 和 scholar scope. 试了pubmedy&#xff0c;找到的版本没有用。 PubMed 是一个提供生物医学方面的论文搜寻以及摘要&#xff0c;并且免费搜寻的数据库。它的数据库来源为MEDLINE。其核心…

新手刚学js遇到的ie6问题

2019独角兽企业重金招聘Python工程师标准>>> 1.前段时间遇到一个需求&#xff0c;需要让图片在点击tab的时候加载。如果那个tab是由a标签组成的&#xff0c;这时候你就需要在click之后return false。不然坑爹的ie6是没法显示图片的。 2.有个需求是&#xff0c;做一…

查看文章影响因子的插件_查询文献可实时显示影响因子与分区排名的2个强大浏览器插件...

首先,看下我们普通的PubMed文献查找页面,是下图这样子的: 可是装了两个国产神器之后,文献的检索结果列表是下图这样的,可以实时查看文章的影响因子、研究领域的排名,以及全文下载链接、引文格式等。 而这两个神器其实只是两款非常小的浏览器插件:PubMedy和Scholarscope。…

python 贪吃蛇游戏代码

第一步&#xff1a;蛇形 运行IDLE&#xff0c;打开一个新的文本编辑窗口。输入以下的代码&#xff1a; # -*- coding: UTF-8 -*- # 1 - 引入模块 import pygame from pygame.locals import * import sys,random,time,math# 2 - 初始化pygame pygame.init() fpsClock pygame.…

简单的贪吃蛇

最近都在忙着复习考试&#xff0c;忙里偷闲&#xff0c;抽出时间写了个贪吃蛇&#xff0c;没时间写详细的思路了&#xff0c;代码里有比较详细的注释&#xff0c;有兴趣的同学可以自己看看。&#xff08;感觉写的相对来说还是比较简短的&#xff0c;如果有什么写的不好或是不对…

简单的贪吃蛇代码,可上机运行

贪吃蛇无敌版&#xff0c;可穿墙&#xff0c;英文输入法小写字母wasd操作。 #include<stdio.h> #include<string.h> #include<windows.h> #include<time.h> #include<conio.h>#define up w #define down s #define left a #define right d #def…

cmd贪吃蛇(cmd贪吃蛇怎么做)

贪吃蛇代码-贪吃蛇的围墙代码怎么&#xff1f;贪吃蛇的围墙代码怎么写 哈哈……避邪[哈哈] 贪吃蛇在哪下载啊 我的工享里有 在dos环境下c语言编程编一个贪吃蛇游戏 程序设计及说明 该类规定游戏的范围大小。 Snake 用该类生成一个实例蛇 snake 该类用于实现对蛇的操作控制&…

C++实现cmd界面简单贪吃蛇游戏

贪吃蛇的玩法我想应该大家都是耳熟能详了。但是这游戏虽然简单&#xff0c;但是编写的难度对一个刚刚学完c,准备考研的苦逼大学生来说却是一件非常艰难的事情。 date:10月3日&#xff0c;国庆节的头3天&#xff0c;大家在外玩耍我却苦逼的在这里写代码痛苦ing&#xff0c;不知…

手敲最基础C语言代码----“贪吃蛇”

C语言创作游戏----第二弹----贪吃蛇&#xff08;无限吃&#xff09; 主函数系列&#xff1a; 创建引入头文件----方便查看代码&#xff01;&#xff01; #include<stdio.h> #include<Windows.h> #include<stdlib.h> #include<conio.h> #include<tim…

创建链表和遍历链表算法演示

#include <stdio.h> #include <malloc.h> #include <string.h> #include <stdlib.h>typedef struct Node {int data; //数据域struct Node * pNext; //指针域}Node, *pNode;//函数声明 pNode create_list(); void traverse_list(pNode pHead); int…

C++ 创建链表

本文旨在解决两个问题&#xff1a; 1、如何写一个创建链表函数 2、为什么对于单个节点必须要new&#xff0c;而不能使用& 1、如何写一个创建链表函数 代码如下 ListNode* createListNode(vector<int> input) {ListNode dummy ListNode(-1);ListNode* pre &d…

单链表创建

单链表的创建与操作 链表作为基本的数据结构&#xff0c;学习好链表的创建与操作是数据结构入门的基础。 &#xff08;小白make for myself&#xff09; 单链表的创建 typedef struct Node {int data;struct Node* next; }Node;//结构体创建&#xff0c;也可以使用*Node取址…

动态链表的创建

#include <stdio.h> //List结构样式 typedef struct node { int data; struct node *next; }Node; //创建head的空链 Node *createList() { Node *head (Node *)malloc(sizeof(Node)); if(NULL head) exit(-1); head->next NULL; return head; } Node *insertList(…

C++创建一个链表

这个是在参加面试的时候遇到的题目&#xff0c;说句实话&#xff0c;我当时不懂。 后面查了资料&#xff0c;里面写的比较仔细就不多说了。 #include <iostream> using namespace std; struct node {int data;node* next;node(int data, node* next NULL) {this->d…

如何在Python中创建与使用链表(单链表)

如何在Python中创建与使用链表&#xff08;单链表&#xff09; 最近用Python语言在Leetcode中刷题&#xff0c;接触到不少关于链表的题&#xff0c;学校目前还没有开设数据结构的课程&#xff08;开设的话应该也是以C/C语言写的&#xff09;。 因为不太了解链表使用方式&#…