背景

看了好多android技术博客,写android分层架构的博客越来越多,有mvc、mvp、mvvm、clean等各式各样的,而mvp异常火热,然而每个人对mvp的定义又是不同,写法自然也是千紫万红。

目的

写一个实用分层清晰的mvp架构

主题

mvp无非 model(数据)、view(界面)、presenter(逻辑)。model对应本地持久化或远程服务端数据,而在笔者看来其实就是对应一个bean对象,然而这个bean对象由远程服务器或本地持久化而得到,因而此层需封装网络请求和本地持久化;view对应activity、fragment以及它们对应的xml布局文件,这层只负责做ui显示;presenter对应逻辑处理层,所做的事情很多,包括网络请求操作、读取缓存数据操作、算法计算等等。

接下来写代码来分析笔者认为优雅的mvp分层架构,开始看一下项目分组,如下图所示:

11235e7bcd8e?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

QQ图片20160908140950.png

从上图我们看到module下有四个分组,分别对应:contract、model、presenter、views。相信大部分童鞋对contract有点疑惑,这个分组是干啥用的呢?contract是作为契约,目的是将presenter、views等接口集中关联起来,便于统一管理。

11235e7bcd8e?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

QQ图片20160930154403.png

打开契约分组,我们看到四个接口和一个类,分别为IActivityLifeCycle(Activity生命周期接口类)、IBaseActivity(Activity接口基类 界面层的)、IBaseFragment(Fragment接口基类 界面层的)、IBasePresenter(逻辑层基类 逻辑层的)、UserInfoContract(用户信息契约类,关联view层与presenter层接口,方便统一管理)。

IActivityLifeCycle代码如下:

/**

* @className: IActivityLifeCycle

* @classDescription: 生命周期接口(为了实现Activity UI层生命周期映射到逻辑层)

* @author: leibing

* @createTime: 2016/8/11

*/

public interface IActivityLifeCycle {

void onCreate();

void onRestart();

void onStart();

void onResume();

void onPause();

void onStop();

void onDestroy();

}

IActivityLifeCycle 作为一个activity生命周期接口,为了将activity生命周期映射到对应presenter层,便于逻辑层能更好的处理跟activity生命周期有关的事件。

IBaseActivity代码如下:

package com.ym.mvpdemo.module.contract;

/**

* @className: IBaseActivity

* @classDescription: activity接口基类

* @author: leibing

* @createTime: 2016/8/11

*/

public interface IBaseActivity {

// 设置逻辑

void setPresenter(T mIActivityPresenter);

// 设置生命周期

void setILifeCycle(IActivityLifeCycle mIActivityLifeCycle);

}

IBaseActivity作为一个view层的activity接口基类,主要是设置逻辑层和设置生命周期,将逻辑层与界面层绑定起来,将activity生命周期映射到逻辑层去。

IBaseFragment代码如下:

package com.ym.mvpdemo.module.contract;

/**

* @className:IBaseFragment

* @classDescription:Fragment接口基类

* @author: leibing

* @createTime: 2016/8/12

*/

public interface IBaseFragment {

// 设置逻辑

void setPresenter(T mIFragmentPresenter);

}

IBaseFragment作为一个view层的fragment接口基类,主要是设置逻辑层,将逻辑层与界面层绑定起来。

IBasePresenter代码如下:

package com.ym.mvpdemo.module.contract;

/**

* @className: IBasePresenter

* @classDescription: 逻辑层基类

* @author: leibing

* @createTime: 2016/8/11

*/

public interface IBasePresenter {

// 逻辑层开始执行方法

void start();

}

IBasePresenter作为一个逻辑层基类,主要做界面层与逻辑层绑定之后,逻辑层初始化工作。

UserInfoContract比较重要,大家仔细看看,代码如下:

package com.ym.mvpdemo.module.contract;

import com.ym.mvpdemo.module.model.UserInfoModel;

/**

* @className: UserInfoContract

* @classDescription: 用户信息契约类

* @author: leibing

* @createTime: 2016/8/11

*/

public class UserInfoContract {

/**

* 用户信息activity中用于更新UI的方法集合

* @interfaceName: IUserInfoActivity

* @interfaceDescription: View接口

* @author: leibing

* @createTime: 2016/08/23

*/

public interface IUserInfoActivity extends IBaseActivity {

void showLoading();//展示加载框

void dismissLoading();//取消加载框展示

void showUserInfo(UserInfoModel userInfoModel);//将网络请求得到的用户信息回调

String loadUserId();//假设接口请求需要一个userId

}

/**

* 用户信息Fragment中用于更新UI的方法集合

* @interfaceName: IFragment

* @interfaceDescription: Fragment接口

* @author: leibing

* @createTime: 2016/08/23

*/

public interface IUserInfoFragment extends IBaseFragment {

void showData(); // 假定显示数据

}

/**

* 用户信息activity逻辑层需要使用的方法集合

* @interfaceName: IUserInfoActivityPresenter

* @interfaceDescription: 用户信息Activity逻辑层接口

* @author: leibing

* @createTime: 2016/08/23

*/

public interface IUserInfoActivityPresenter extends IBasePresenter {

void loadUserInfo();

}

/**

* 用户信息Fragment逻辑层需要使用的方法集合

* @interfaceName: IUserInfoFragmentPresenter

* @interfaceDescription: 用户信息Fragment逻辑层接口

* @author: leibing

* @createTime: 2016/08/23

*/

public interface IUserInfoFragmentPresenter extends IBasePresenter {

void loadData();

}

}

UserInfoContract作为一个契约类,将界面层与逻辑层接口进行集中管理,便于提高接口可读性。

契约分组分析完了,然后我们再看model,model结构图如下所示:

11235e7bcd8e?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

QQ图片20160930160414.png

至此,我们看到httprequest、data两个分组,这两分组分别对应网络请求封装和数据持久化封装,这两块封装看个人了,没有一个绝对的方案,我们还看到一个UserInfoModel,这里我就先放外面了,没放httprequest和data里面去了,这里具体封装具体放对应的位置,网络请求封装可以参考基于Retrofit、OkHttp、Gson封装通用网络框架、持久化数据封装可以参考android基于xml实现的对象缓存方案。

接下来我们分析View层,View分组如下图所示:

11235e7bcd8e?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

QQ图片20160930161058.png

view分组就一个activity和一个fragment,其实这层很简单,主要做更新ui的工作,代码结构也比较清晰,笔者在activity里面将对应生命周期映射到了其逻辑层上面去了,这样省去了在生命周期上面 view层往逻辑层写方法的麻烦。

activity代码如下:

package com.ym.mvpdemo.module.views.userinfo;

import android.os.Bundle;

import android.support.v4.app.Fragment;

import android.support.v4.view.ViewPager;

import android.support.v7.app.AppCompatActivity;

import android.widget.TextView;

import android.widget.Toast;

import com.ym.mvpdemo.R;

import com.ym.mvpdemo.adapter.ViewpagerAdapter;

import com.ym.mvpdemo.module.contract.IActivityLifeCycle;

import com.ym.mvpdemo.module.contract.UserInfoContract;

import com.ym.mvpdemo.module.model.UserInfoModel;

import com.ym.mvpdemo.module.presenter.userinfo.UserInfoActivityPresenter;

import java.util.ArrayList;

import java.util.List;

import butterknife.BindView;

import butterknife.ButterKnife;

import butterknife.OnClick;

/**

* @className: UserInfoActivity

* @classDescription: UI层(Activity)

* @author: leibing

* @createTime: 2016/8/11

*/

public class UserInfoActivity extends AppCompatActivity implements UserInfoContract.IUserInfoActivity{

// 切换Tab常量

public final static int HOME_INDEX = 0;

public final static int CZH_INDEX = 1;

public final static int ME_INDEX = 2;

// Activity逻辑层接口

private UserInfoContract.IUserInfoActivityPresenter mIActivityPresenter;

// 生命周期接口

private IActivityLifeCycle mIActivityLifeCycle;

// Fragment

private UserInfoFragment mHomeFragment;

private UserInfoFragment mCzhFragment;

private UserInfoFragment mMineFragment;

// Fragement列表

private List mFragmentList;

// 标题列表

private List mTitleList;

@BindView(R.id.tv_name) TextView nameTv;

@BindView(R.id.tv_age) TextView ageTv;

@BindView(R.id.tv_address) TextView addressTv;

@BindView(R.id.vpg_main) ViewPager mainPager;

@BindView(R.id.tv_main_home) TextView mainHomeTv;

@BindView(R.id.tv_main_czh) TextView mainCzhTv;

@BindView(R.id.tv_main_me) TextView mainMeTv;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

// 绑定ButterKnife

ButterKnife.bind(this);

// 初始化list

initList();

// 初始化Fragment

initFragment();

// 初始化逻辑

new UserInfoActivityPresenter(this);

mIActivityPresenter.start();

// View映射onCreate生命周期到Presenter

mIActivityLifeCycle.onCreate();

}

/**

* 初始化列表

* @author leibing

* @createTime 2016/8/11

* @lastModify 2016/8/11

* @return

*/

private void initList() {

mFragmentList = new ArrayList<>();

mTitleList = new ArrayList<>();

}

/**

* 初始化Fragment

* @author leibing

* @createTime 2016/8/11

* @lastModify 2016/8/11

* @param

* @return

*/

private void initFragment() {

// 首页

mHomeFragment = new UserInfoFragment();

Bundle bundle = new Bundle();

bundle.putSerializable(UserInfoFragment.PAGE_INDEX, HOME_INDEX);

mHomeFragment.setArguments(bundle);

mFragmentList.add(mHomeFragment);

// 车智汇

mCzhFragment = new UserInfoFragment();

bundle = new Bundle();

bundle.putSerializable(UserInfoFragment.PAGE_INDEX, CZH_INDEX);

mCzhFragment.setArguments(bundle);

mFragmentList.add(mCzhFragment);

// 我的

mMineFragment = new UserInfoFragment();

bundle = new Bundle();

bundle.putSerializable(UserInfoFragment.PAGE_INDEX, ME_INDEX);

mMineFragment.setArguments(bundle);

mFragmentList.add(mMineFragment);

// ViewPager适配

ViewpagerAdapter mAdapter = new ViewpagerAdapter(

getSupportFragmentManager(), mFragmentList, mTitleList);

mainPager.setAdapter(mAdapter);

mainPager.setOffscreenPageLimit(3);

mainPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {

@Override

public void onPageSelected(int position) {

}

@Override

public void onPageScrollStateChanged(int state) {

}

@Override

public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

if (positionOffset == 0.0)

switchTab(position);

}

});

mainPager.setCurrentItem(0);

}

/**

* 切换Tab页

* @author leibing

* @createTime 2016/5/6

* @lastModify 2016/5/6

* @param index

* @return

*/

private void switchTab(int index){

switch (index){

case HOME_INDEX:

mainHomeTv.setTextColor(getResources().getColor(R.color.main_home_text_blue));

mainCzhTv.setTextColor(getResources().getColor(R.color.main_home_text_gray));

mainMeTv.setTextColor(getResources().getColor(R.color.main_home_text_gray));

break;

case CZH_INDEX:

mainHomeTv.setTextColor(getResources().getColor(R.color.main_home_text_gray));

mainCzhTv.setTextColor(getResources().getColor(R.color.main_home_text_blue));

mainMeTv.setTextColor(getResources().getColor(R.color.main_home_text_gray));

break;

case ME_INDEX:

mainHomeTv.setTextColor(getResources().getColor(R.color.main_home_text_gray));

mainCzhTv.setTextColor(getResources().getColor(R.color.main_home_text_gray));

mainMeTv.setTextColor(getResources().getColor(R.color.main_home_text_blue));

break;

}

}

@Override

protected void onRestart() {

// View映射onRestart生命周期到Presenter

mIActivityLifeCycle.onRestart();

super.onRestart();

}

@Override

protected void onStart() {

super.onStart();

// View映射onStart生命周期到Presenter

mIActivityLifeCycle.onStart();

}

@Override

protected void onResume() {

super.onResume();

// View映射onResume生命周期到Presenter

mIActivityLifeCycle.onResume();

}

@Override

protected void onPause() {

// View映射onPause生命周期到Presenter

mIActivityLifeCycle.onPause();

super.onPause();

}

@Override

protected void onStop() {

// View映射onStop生命周期到Presenter

mIActivityLifeCycle.onStop();

super.onStop();

}

@Override

protected void onDestroy() {

// View映射onDestroy生命周期到Presenter

mIActivityLifeCycle.onDestroy();

super.onDestroy();

}

@Override

public void showLoading() {

Toast.makeText(this, "正在加载", Toast.LENGTH_SHORT).show();

}

@Override

public void dismissLoading() {

Toast.makeText(this, "加载完成", Toast.LENGTH_SHORT).show();

}

@Override

public void showUserInfo(UserInfoModel userInfoModel) {

if (userInfoModel != null) {

nameTv.setText(userInfoModel.getName());

ageTv.setText(String.valueOf(userInfoModel.getAge()));

addressTv.setText(userInfoModel.getAddress());

}

}

@Override

public String loadUserId() {

return "1000";//假设需要查询的用户信息的userId是1000

}

@Override

public void setPresenter(UserInfoContract.IUserInfoActivityPresenter mIActivityPresenter) {

this.mIActivityPresenter = mIActivityPresenter;

}

public void setILifeCycle(IActivityLifeCycle mIActivityLifeCycle) {

this.mIActivityLifeCycle = mIActivityLifeCycle;

}

@OnClick(R.id.ly_main_home) void mainOnClick() {

mainPager.setCurrentItem(HOME_INDEX, false);

}

@OnClick(R.id.ly_main_czh) void czhOnClick() {

mainPager.setCurrentItem(CZH_INDEX, false);

}

@OnClick(R.id.ly_main_me) void meOnClick() {

mainPager.setCurrentItem(ME_INDEX, false);

}

}

fragment代码如下:

package com.ym.mvpdemo.module.views.userinfo;

import android.app.Activity;

import android.os.Bundle;

import android.support.annotation.Nullable;

import android.support.v4.app.Fragment;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import android.widget.TextView;

import android.widget.Toast;

import com.ym.mvpdemo.R;

import com.ym.mvpdemo.module.contract.UserInfoContract;

import com.ym.mvpdemo.module.presenter.userinfo.UserInfoFragmentPresenter;

import butterknife.BindView;

import butterknife.ButterKnife;

/**

* @className: UserInfoFragment

* @classDescription: Ui层(IFragment)

* @author: leibing

* @createTime: 2016/8/11

*/

public class UserInfoFragment extends Fragment implements UserInfoContract.IUserInfoFragment {

// 页面常量

public final static String PAGE_INDEX = "page_index";

// 页面数字

private int pageIndex;

// UI回调

private UserInfoContract.IUserInfoFragmentPresenter mIFragmentPresenter;

// 判断是否当前Fragment

private boolean isVisibleToUser = false;

@BindView(R.id.tv_fgm)

TextView fgmTv;

@Override

public void onAttach(Activity activity) {

super.onAttach(activity);

pageIndex = (int) getArguments().getSerializable(PAGE_INDEX) + 1;

}

@Nullable

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

View view = inflater.inflate(R.layout.fragment_main, null);

// 绑定ButterKnife

ButterKnife.bind(this, view);

fgmTv.setText("第"+ pageIndex + "页");

if (isVisibleToUser) {

new UserInfoFragmentPresenter(this);

mIFragmentPresenter.start();

}

return view;

}

@Override

public void showData() {

Toast.makeText(getActivity(), "这是第" + pageIndex + "个页面", Toast.LENGTH_SHORT).show();

}

@Override

public void setPresenter(UserInfoContract.IUserInfoFragmentPresenter mIFragmentPresenter) {

this.mIFragmentPresenter = mIFragmentPresenter;

}

@Override

public void setUserVisibleHint(boolean isVisibleToUser) {

super.setUserVisibleHint(isVisibleToUser);

this.isVisibleToUser = isVisibleToUser;

}

}

view层分析完后,接下来我们来分析压轴戏:presenter(逻辑层),逻辑层分组情况如下图所示:

11235e7bcd8e?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

QQ图片20160930161532.png

我们可以看到逻辑层分组里面包含一个activity逻辑层类、fragment逻辑层类。

接下来我们看下activity逻辑层类,代码如下:

package com.ym.mvpdemo.module.presenter.userinfo;

import android.os.Handler;

import com.ym.mvpdemo.module.contract.IActivityLifeCycle;

import com.ym.mvpdemo.module.contract.UserInfoContract;

import com.ym.mvpdemo.module.model.UserInfoModel;

/**

* @className: UserInfoActivityPresenter

* @classDescription: 用户信息activity逻辑层

* @author: leibing

* @createTime: 2016/8/11

*/

public class UserInfoActivityPresenter implements UserInfoContract.IUserInfoActivityPresenter, IActivityLifeCycle {

// 用户信息activity接口

private UserInfoContract.IUserInfoActivity mIUserInfoActivity;

/**

* 构造函数

* @author leibing

* @createTime 2016/08/23

* @lastModify 2016/08/23

* @param mIUserInfoActivity 用户信息activity接口

* @return

*/

public UserInfoActivityPresenter(UserInfoContract.IUserInfoActivity mIUserInfoActivity) {

this.mIUserInfoActivity = mIUserInfoActivity;

// 设置逻辑

mIUserInfoActivity.setPresenter(this);

// 设置生命周期

mIUserInfoActivity.setILifeCycle(this);

}

@Override

public void loadUserInfo() {

String userId = mIUserInfoActivity.loadUserId();

System.out.println("ddddddddddddddddddddddddddd userId = " + userId);

mIUserInfoActivity.showLoading();//接口请求前显示loading

//这里模拟接口请求回调-

new Handler().postDelayed(new Runnable() {

@Override

public void run() {

//模拟接口返回的json,并转换为javaBean

UserInfoModel userInfoModel = new UserInfoModel("小宝", 1, "杭州");

mIUserInfoActivity.showUserInfo(userInfoModel);

mIUserInfoActivity.dismissLoading();

}

}, 3000);

}

@Override

public void start() {

loadUserInfo();

}

@Override

public void onRestart() {

System.out.println("ddddddddddddddddddddd present onRestart");

}

@Override

public void onCreate() {

System.out.println("ddddddddddddddddddddd present onCreate");

}

@Override

public void onStart() {

System.out.println("ddddddddddddddddddddd present onStart");

}

@Override

public void onResume() {

System.out.println("ddddddddddddddddddddd present onResume");

}

@Override

public void onPause() {

System.out.println("ddddddddddddddddddddd present onPause");

}

@Override

public void onStop() {

System.out.println("ddddddddddddddddddddd present onStop");

}

@Override

public void onDestroy() {

System.out.println("ddddddddddddddddddddd present onDestroy");

}

}

上面代码主要是将activity 界面层与逻辑层关联起来、实现生命周期映射接口、实现逻辑层接口,有不懂得童鞋可以结合view层多看看就明白了,fragment逻辑层类似activity,就不多做分析了。

童鞋们,笔者写的这个mvp是不是很简单?大家可以尝试去写下。

效果图如下:

11235e7bcd8e?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

MvpDemo.gif

关于作者

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐