Android Mvp实践

Android Mvp实践

简介

本文是参考google官方发布的MVP架构demo以及前人对MVP实现方式的一些总结做的一个简单demo,在这里记录一下一点心得,希望能给想用MVP的人一点帮助。


MVP框图

总体框架

工程目录结构

首先先看下整个工程的目录结构:



目录的代码组织方式是按照功能来组织的,功能内部分为xactivity、xcontract、xfragment、xpresenter四个类文件(x代表业务名称)。base文件夹存放一些公用的基类文件,data文件夹存放业务逻辑相关的代码,utils文件夹则放一些公用的工具类。本demo实现的功能为:通过点击界面上的按钮,获取手机相关信息,获取过程中加入延时及等待提示(模拟网络),最终将信息显示于界面上(简单演示,只是显示了系统时间)。

基类

先看下BasePresenter与BaseView这两个接口类,它们分别是所有Presenter与View的基类。

public interface BasePresenter {
    void start();
}

BasePresenter中含有方法start(),该方法的作用是presenter开始获取数据并调用view中方法改变界面显示,其调用时机是在Fragment类的onResume方法中。

public interface BaseView<T> {
    void setPresenter(T presenter);
}

BaseView中含方法setPresenter,该方法作用是将presenter实例传入view中,其调用时机是在activity的presenter实现类的构造函数中。

契约类

public interface GetPhoneInfoContract {

    interface View extends BaseView<Presenter> {

        void setTime(String time);

        void showLoading();

        void hideLoading();
    }

    interface Presenter extends BasePresenter {

        void getTime();
    }

}

与比较常见的mvp实现不同,官方的实现中加入了契约类来统一管理view与presenter的所有的接口,这种方式使得view与presenter中有哪些功能,一目了然,维护起来也方便,该实例中presenter的接口实现获取系统时间,view的接口实现时间的显示以及提示对话框的显示及隐藏。

MVP的组织方式

activity的作用

activity作为全局的控制者,负责创建view以及presenter实例,并将二者联系起来,具体的view交由fragment来实现,两者各司其职。

@EActivity(R.layout.get_phone_info_act)
public class GetPhoneInfoActivity extends ActionBarActivity {

    private FragmentManager fm;
    private GetPhoneInfoFragment mGetPhoneInfoFragment = new GetPhoneInfoFragment_();
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setDefaultFragment();
        new GetPhoneInfoPresenter(mGetPhoneInfoFragment);
    }

    private void setDefaultFragment() {
        fm = getFragmentManager();
        FragmentTransaction transaction = fm.beginTransaction();
        transaction.add(R.id.fragcontent, mGetPhoneInfoFragment);
        transaction.commit();
    }

}

本例中,activity中通过setDefaultFragment()设置了fragment,之后实例化GetPhoneInfoPresenter,并将frament传递进去,实现在presenter中通过fragment的接口对view进行操作展示。

presenter的实现

public class GetPhoneInfoPresenter implements GetPhoneInfoContract.Presenter{

    private final GetPhoneInfoContract.View mGetPhoneInfoView;
    private PhoneInfoBiz phoneInfoBiz;

    public GetPhoneInfoPresenter(GetPhoneInfoContract.View getPhoneInfoView) {
        mGetPhoneInfoView = getPhoneInfoView;
        mGetPhoneInfoView.setPresenter(this);
        phoneInfoBiz = new PhoneInfoBizIml();
    }

    @Override
    public void start() {
        getTime();
    }

    @Override
    public void getTime() {
        mGetPhoneInfoView.showLoading();
        phoneInfoBiz.getPhoneInfo(new PhoneInfoBiz.GetPhoneInfoCallback() {
            @Override
            public void onGetPhoneInfo(PhoneInfo phoneInfo) {
                mGetPhoneInfoView.setTime(phoneInfo.getTime());
                mGetPhoneInfoView.hideLoading();
            }
        });
    }
}

presenter构造函数中调用了view的setPresenter方法将自身实例传入,start方法中处理了数据加载与展示。如果需要界面做对应的变化,直接调用view层的方法即可,这样view层与presenter层就能够很好的被划分。

view的实现

@EFragment(R.layout.get_phone_info_frag)
public class GetPhoneInfoFragment extends Fragment implements GetPhoneInfoContract.View {

    private GetPhoneInfoContract.Presenter mPresenter;
    ProgressDialog dialog;

    @ViewById
    TextView tv_time;

    @ViewById
    Button btn_get_time;

    @Click
    void btn_get_time() {
        mPresenter.getTime();
    }

    @AfterViews
    void initView() {
        dialog = new ProgressDialog(getActivity());
    }

    @Override
    public void onResume() {
        super.onResume();
        mPresenter.start();
    }

    @Override
    public void setPresenter(GetPhoneInfoContract.Presenter presenter) {
        if (presenter != null)
            mPresenter = presenter;
    }

    @Override
    @UiThread
    public void setTime(String time) {
        tv_time.setText(time);
    }

    @Override
    public void showLoading() {
        dialog.setTitle("请稍候");
        dialog.setMessage("loading!");
        dialog.show();
    }

    @Override
    public void hideLoading() {
        dialog.dismiss();
    }

}

setPresenter方法继承于父类,通过该方法,view获得了presenter得实例,从而可以调用presenter代码来处理业务逻辑。在onResume中还调用了presenter得start方法,处理数据的加载与展示。

简单归纳

  • activity创建fragment及实例化presenter,在实例化的同时,将fragment作为参数传递进去,这样一来在presenter中就可调用view的接口对界面进行更新、展示
  • presenter实例化时,还调用了view的setPresenter方法,将自身传递进去,这样一来fragment获得了presenter的实例,便可在view中调用presenter进行业务逻辑的操作
  • view及presenter拥有彼此的实例,实现了在view中调用presenter处理业务,处理完后再presenter中更新view。

model层

简单介绍下model层,PhoneInfo对象存储手机相关信息,PhoneInfoBiz为借口类实现该业务所需要的接口及回调接口,PhoneInfoBizIml为接口的实现类。直接贴代码:

public interface PhoneInfoBiz {

    interface GetPhoneInfoCallback {

        void onGetPhoneInfo(PhoneInfo phoneInfo);
    }

    void getPhoneInfo(GetPhoneInfoCallback getPhoneInfoCallback);
}

public class PhoneInfoBizIml implements PhoneInfoBiz{

    @Override
    public void getPhoneInfo(final GetPhoneInfoCallback getPhoneInfoCallback) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    PhoneInfo phoneInfo = new PhoneInfo();
                    phoneInfo.setTime(System.currentTimeMillis() + "");
                    phoneInfo.setMobileType(Build.MODEL);
                    phoneInfo.setMobileVer(Build.VERSION.RELEASE);
                    Thread.sleep(1000);
                    getPhoneInfoCallback.onGetPhoneInfo(phoneInfo);
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

总结

至此,一个简单的mvp框架到此结束,对于mvp的使用目前也还在探索中,上例是结合官方发布的demo做的一个简化工程,有不足之处欢迎一起探讨交流!

最后附上本文demo及官方demo的地址:

本文demo链接
官方demo链接

本文首发地址链接:http://www.codeceo.com/article/android-mvp-learn.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,919评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,567评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,316评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,294评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,318评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,245评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,120评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,964评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,376评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,592评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,764评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,460评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,070评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,697评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,846评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,819评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,665评论 2 354

推荐阅读更多精彩内容