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分层架构,开始看一下项目分组,如下图所示:

QQ图片20160908140950.png

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

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<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结构图如下所示:

QQ图片20160930160414.png

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

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

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<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(逻辑层),逻辑层分组情况如下图所示:

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是不是很简单?大家可以尝试去写下。

项目地址: MvpDemo

效果图如下:

MvpDemo.gif

关于作者

  • QQ:872721111
  • Email:leibing1989@126.com
  • Github:leibing@github
0 0
原创粉丝点击