Android MVP的简单实现

一个好的好的设计模式可以帮我们很好的管理我们的代码,也会方便于我们的后期的扩展。特别是针对我们这样的新手,学好一个好的代码管理是很有必要的。所以今天,和大家一起学习一下MVP模式。

一、MVP设计模式概述

什么是MVP设计模式

MVP,分别是Model-View-Presenter,即模型-视图-提出者。
Model:模型,实现业务逻辑和实例生成。
View:视图,对应的界面布局,以及界面布局的方法。
Presenter:一个活动中,主要的业务交互,作为view和model的中间传达者。

什么是MVC设计模式

MVP,分别是Model-View-Presenter,即模型-视图-控制器。
Model:模型,实现业务逻辑和实例生成。
View:视图,对应的界面布局,以及界面布局的方法。
Controllor:作为页面的控制器,响应view的交互,对应于Android中的Activity

对比

这么说可能不是很清晰,我们用两张图片来描述一下。


MVP与MVC对比

但是在Android我们的可以知道,其实Avtivity不仅要处理view的呈现,还要负责处理页面的业务逻辑,显得Activity不仅像view,又像controllor的结合体,导致Acitivity整体臃肿,超过1000行都是常事。

所以,为了更好的解耦这样的情况,让Activity更好的只关心页面的呈现,将主要的业务交互放置在presenter中实现,让Presenter作为view与model的中间传递者。减少Activity的体积。降低耦合度。

在MVP模式中,我们要做到,view层和model不能直接通信,要想通信,必须通过Persenter这一中间件。并且,view,model,presenter都是接口,之间的通讯也都是通过接口实现的。

二、 MVP的简单实现

接下来我们通过一个基础的实例,让我们来实现一个简单的MVP模式的登录界面。从实例的角度,我们来分析一个具体的MVP模式。看看他是怎样实现解耦,以及明显的内容划分。

Model层:

model层是用来实现某一层里面的业务逻辑。
那么,在一个登录界面中,我的model层就是实现登录密码检验的功能。
首先,我们需要有一个对象类,来表达我们需要检验的对象。

public class User {
    private String name;
    private String password;

    public User(String name,String password){
        this.name = name;
        this.password = password;
    }
    ...省略getter和setter
}

然后我们需要一个LoginModel的接口函数,来申明我们的登录所需要的函数。

public interface LoginModel {
    void login(User user);
}

其次是model的实现类,实现我们刚才在LoginModel中申明的login函数。
这里我们延时2秒钟,用于模拟登陆效果。

public class LoginModelImpl implements LoginModel{
    @Override
    public void login(User user) {
        final String name = user.getName();
        final String password = user.getPassword();

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                Boolean error =false;
                if (TextUtils.isEmpty(name)){
                    error = true;
                    //输入错误
                    System.out.println("输入错误");
                }
                if (TextUtils.isEmpty(password)){
                    error = true;
                    //输入错误
                    System.out.println("输入错误");
                }
                if(!error){
                    //登录成功
                    System.out.println("登录成功");
                }
            }
        },2000);
    }
}

View层:

对应的界面布局,以及界面布局的方法。
这里我们需要实现的waitDialog的显示与消失,以及登录成功和失败的提醒,所以建立我们的view接口类,LoginView.

public interface LoginView {
    void loginSuccess();
    void ErrorPass();
    void ErrorEnter();

    void showDialog();
    void hideDialog();
}

然后我们的实例就是Activity,使Acitivity继承我们的接口,实现我们接口中的方法。

    //申明persenter实例
    LoginPresenter presenter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        ButterKnife.bind(this);

        btn.setOnClickListener(this);
        //通过Presenter实例类申明presenter
        presenter = new LoginPresenterImpl(this);
    }
    

    @Override
    public void loginSuccess() {
        Toast.makeText(this,"登录成功",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void ErrorPass() {
        Toast.makeText(this,"密码错误",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void ErrorEnter() {
        Toast.makeText(this,"输入错误",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showDialog() {
        loginProgress.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideDialog() {
        loginProgress.setVisibility(View.GONE);
    }
    
    @Override
    public void onClick(View v) {
        User user = new User(name.getText().toString(), password.getText().toString());
        //将检验事件回调给presenter,让他通知model进行检验
        presenter.checkLogin(user);
    }
}

Presenter层:

接下来是重点了,我们的Presenter类。
传递
作为view和model的中间传达者。view告诉presenter需要检验了,然后presenter在告诉model,并把得到的user值给model,让model进行检验。
申明presenter的接口类。

public interface LoginPresenter {
    void checkLogin(User user);
}

接口实现类LoginPresenterImpl,我们在presenter的实例中,获取到view和model的实例,然后在里面对其进行信息传递。

public class LoginPresenterImpl implements LoginPresenter,LoginModelListener{
    LoginView view;
    LoginModel model;

    public LoginPresenterImpl(LoginView loginView){
        //从Activity中获取到对view的引用
        this.view = loginView;
        //申明一个新的model
        model = new LoginModelImpl();
    }

    @Override
    public void checkLogin(User user) {
        //保证视图是存在的
        if (view == null){
            return;
        }
        //显示view中的dialog,然后对user信息进行验证
        view.showDialog();
        model.login(user,this);
    }
}

这样我们就实现了将view的事件,通过presenter传递给了model层,让他去处理这一次的检验事件,但是大家有没有发现,我们是将这个事件传递给了model,但是model在处理完了之后,他又怎么将得到的结果通知给presenter呢?因为我们要避免view和model直接接触。所以在这里,我们还需要一个listenter的接口类,让presenter继承这个接口,在传递给model的时候,携带接口一起传过去。然后model通过这个接口告诉presenter,presenter再去通知view。

public interface LoginModelListener {
    void loginSuccess();
    void loginError();
}
public interface LoginModel {
    void login(User user, LoginModelListener listener);
}
public class LoginModelImpl implements LoginModel{
    @Override
    public void login(User user, final LoginModelListener listener) {
        final String name = user.getName();
        final String password = user.getPassword();

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                Boolean error =false;
                if (TextUtils.isEmpty(name)){
                    error = true;
                    //输入错误
                    System.out.println("输入错误");
                    listener.loginError();
                }
                if (TextUtils.isEmpty(password)){
                    error = true;
                    //输入错误
                    System.out.println("输入错误");
                    listener.loginError();
                }
                if(!error){
                    //登录成功
                    System.out.println("登录成功");
                    listener.loginSuccess();
                }
            }
        },2000);
    }
}

至此,我们就实现了一个简单的MVP的demo,是不是觉得Acitivity里面的逻辑很简单了,看着一下字就轻松了很多。

三、总结

那么我们现在结合刚才的案例。我们再来总结一下。

因为我们要保证view和model直接不能直接通信,所有之间的交互,我们都要通过presenter这一个中间类来进行传递。persenter中拥有view和model的引用,然后,view中有事件产生时,将事件以及需要的参数传给presenter,presenter再交给model,待model处理完了之后,将反馈信息传给presenter,presenter再将返回结果告诉view,最后呈现给用户。

流程图

github代码

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

推荐阅读更多精彩内容