android mvp分层架构优雅写代码
来源:互联网 发布:贵州省网络直报 编辑:程序博客网 时间:2024/05/29 07:20
背景
看了好多android技术博客,写android分层架构的博客越来越多,有mvc、mvp、mvvm、clean等各式各样的,而mvp异常火热,然而每个人对mvp的定义又是不同,写法自然也是千紫万红。
目的
写一个实用分层清晰的mvp架构
主题
mvp无非 model(数据)、view(界面)、presenter(逻辑)。model对应本地持久化或远程服务端数据,而在笔者看来其实就是对应一个bean对象,然而这个bean对象由远程服务器或本地持久化而得到,因而此层需封装网络请求和本地持久化;view对应activity、fragment以及它们对应的xml布局文件,这层只负责做ui显示;presenter对应逻辑处理层,所做的事情很多,包括网络请求操作、读取缓存数据操作、算法计算等等。
接下来写代码来分析笔者认为优雅的mvp分层架构,开始看一下项目分组,如下图所示:
从上图我们看到module下有四个分组,分别对应:contract、model、presenter、views。相信大部分童鞋对contract有点疑惑,这个分组是干啥用的呢?contract是作为契约,目的是将presenter、views等接口集中关联起来,便于统一管理。
打开契约分组,我们看到四个接口和一个类,分别为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<T> { // 设置逻辑 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<T> { // 设置逻辑 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<IUserInfoActivityPresenter> { 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<IUserInfoFragmentPresenter> { 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结构图如下所示:
至此,我们看到httprequest、data两个分组,这两分组分别对应网络请求封装和数据持久化封装,这两块封装看个人了,没有一个绝对的方案,我们还看到一个UserInfoModel,这里我就先放外面了,没放httprequest和data里面去了,这里具体封装具体放对应的位置,网络请求封装可以参考基于Retrofit、OkHttp、Gson封装通用网络框架、持久化数据封装可以参考android基于xml实现的对象缓存方案。
接下来我们分析View层,View分组如下图所示:
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<Fragment> mFragmentList; // 标题列表 private List<String> 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(逻辑层),逻辑层分组情况如下图所示:
我们可以看到逻辑层分组里面包含一个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是不是很简单?大家可以尝试去写下。
项目地址: MvpDemo
效果图如下:
关于作者
- QQ:872721111
- Email:leibing1989@126.com
- Github:leibing@github
- android mvp分层架构优雅写代码
- Android代码架构之MVP
- android 之mvp架构,直接上代码
- Android MVP架构和MVC架构比较(有代码示例)
- Android分层架构笔记
- Android Camera架构分层及代码结构(MTK version)
- 关于单片机代码架构分层
- 关于单片机代码架构分层
- Android MVP架构浅析
- android MVP架构
- Android MVC、MVP架构
- android MVP架构
- Android MVP 架构示例
- android mvp架构 浅尝辄止
- android mvp架构
- android MVP架构学习
- Android MVP架构
- Android架构之MVP
- OpenCV矩阵的归一化和欧式距离详解
- 解决maven 下载jar包慢,修改maven仓库地址
- 如何解决UltraEdit不高亮的问题
- 什么是管道模式以及管道模式的优点
- Sublime Text 2 JS 格式化插件 JsFormat的配置使用
- android mvp分层架构优雅写代码
- Android系统 应用图标显示未读消息数(BadgeNumber) 桌面app图标的角标显示
- 算法导论 第九章 中位数和顺序统计量
- IIS添加MIME扩展类型及常用的MIME类型列表
- AndroidStudio 将 module 设置为 Libeary
- javascript深入理解js闭包
- IKAnaylzer分词,词库中包括特殊字符的处理
- 搭建可调试的php-fpm生产环境
- JAVA与C++的基本语法区别