一 简介

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

activity_base.xml

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

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 {void attachView(V view);void detachView();}

新建Presenter基类BasePresenter.java

public class BasePresenter implements IPresenter {protected WeakReference mViewRef; //View接口类型的弱引用/** * 建立关联 * @param view */public void attachView(V view){mViewRef=new WeakReference(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 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 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 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>() {@Overridepublic void onError(ExceptionHandle.ResponeThrowable e) {getView().hideWaitDialog();getView().showToast(e.message);}@Overridepublic void onNext(HttpResponse 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() {returnmetName.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

public abstract class BaseActivityextends 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> userLiveData;public LoginModel() {userLiveData = new MutableLiveData();}//查询用户public void daoQueryAllUser() {//通知数据变化List userList = DaoUserUtils.getInstance().daoQueryAllUser();userLiveData.postValue(userList);}}

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

public class LoginActivity extends BaseActivity {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>() {@Overridepublic void onChanged(List 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 BaseFragmentextends 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> userLiveData;public LoginModel() {userLiveData = new MutableLiveData();}//查询用户public void daoQueryAllUser() {//通知数据变化List userList = DaoUserUtils.getInstance().daoQueryAllUser();userLiveData.postValue(userList);}}

新建HomeFragment.java

public class HomeFragment extends BaseFragment 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>() {@Overridepublic void onChanged(List 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.1MVC特点

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

5.2MVP特点

  • 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层的引用。