Android MVP模式浅析

 

1.什么是MVP?为什么使用MVP?

 

 

View:负责界面刷新、布局等一切和UI界面相关的操作;

Presenter:连接Model和View的桥梁,属于逻辑实现层;

Model :负责数据相关的工作,比如去网络获取数据,去数据库读取数据等;

MVP模式中,Presenter(以下简称:P)层同时持有Model(以下简称:M)和View(以下简称:V)的对象引用,可以对M和V进行操作,这也是P层的职责所在,而V与M之间是没有任何关联的,完全解耦,M通过回调的方式返回值给P,而V只持有P的对象引用。

优点:高度解耦,方便项目维护,方便功能测试,维护起来更加的便利,简化Activity文件结构等,

缺点:需要编写大量的接口。

接下来我将写一个超级简单的登录Demo来演示MVP的使用,首先我们先来看看项目的基本结构

接下来我们来看看如何分别实现各层之间的逻辑,先来看看Model层的实现。

2.Model层实现

由于model并未持有presenter层的引用,所以我们一般使用回调的方式将model层执行后的结果返回给presenter层,所以我们在这首先定义了一个ILoginCallback的回调函数,方便LoginModel将登录结果返回给LoginPresenter,我们来看看这个回调函数的定义:

public interface ILoginCallback<T> {
    //登录成功的回调方法
    void success(T t);
    //登录失败的回调方法
    void failed(String value);
}

 

接下来我们来看看ILoginModel接口的定义,首先既然我们的接口里面的方法是定义给LoginPresenter使用的,就需要先明白在登录时P层需要做什么操作,很明显登录的话最少需要一个进行登录请求的方法,所以ILoginModel接口的定义如下:

 

public interface ILoginModel {
    //进行网络操作:登录请求
    void loginRuquest(ILoginCallback<UserInfo> iLoginCallback, UserInfo userInfo);
}

既然接口已经定义了,接下来看看LoginModel的具体实现,逻辑很简单,就是做一个简单模拟登录的操作,匹配成功调用LoginCallback的success()方法,否则调用failed()方法。

public class LoginModel implements ILoginModel {
    @Override
    public void loginRuquest(final ILoginCallback<UserInfo> iLoginCallback, final UserInfo userInfo) {
        //模拟去服务器验证用户名和密码的操作
        new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    Thread.sleep(3000);
                    if (userInfo.getUserName().equals("123") && userInfo.getPassword().equals("123")) {
                        iLoginCallback.success(userInfo);
                    }else{
                        iLoginCallback.failed("登录失败");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
}

接下来我们来看看如何实现View层的逻辑。

 

3.View层实现

首先我们先来看看接口的实现,上面说过接口中的方法是用来给P层调用的,所以我们需要思考一下登录的过程中Activity需要哪些操作,在这里我就简单的定义了一下行为,如下:

public interface ILoginView {
    //提示登录信息
    void showMessage(String values);

    //清楚登录信息
    void clearText();

    //登录信息
    void loginInfo();

    //登陆成功的界面
    void startMain();
}

OK,接下来来看看LoginView对ILoginView接口的实现:

public class LoginActivity extends BaseActivity implements ILoginView {
    private Button btnLogin, btnClear;
    private EditText etName, etPswd;
    private ProgressBar pBar;
    private final static String TAG = "felix";

    private MyHandler myHandler = new MyHandler(this);

    public static class MyHandler extends Handler {

        WeakReference<Context> reference;

        public MyHandler(Context context) {
            reference = new WeakReference<>(context);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            LoginActivity activity = (LoginActivity) reference.get();
            if (msg.what == 1) {
                activity.pBar.setVisibility(View.GONE);
                Toast.makeText(activity, msg.obj.toString() ,Toast.LENGTH_SHORT).show();
            }
        }
    }

    //将Activity引用传递给presenter
    @Override
    protected ILoginPresenter getLoginPresenter() {
        return new LoginPresenter(this);
    }

    //加载布局
    @Override
    protected int initLayoutView() {
        return R.layout.activity_ilogin;
    }

    //初始化View
    @Override
    protected void initView() {
        etName = findViewById(R.id.et_name);
        etPswd = findViewById(R.id.et_pswd);
        btnLogin = findViewById(R.id.btn_login);
        btnClear = findViewById(R.id.btn_clear);
        pBar = findViewById(R.id.progress_bar);

        pBar.setVisibility(View.GONE);
        btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                loginInfo();
                pBar.setVisibility(View.VISIBLE);
            }
        });

        btnClear.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                loginPresenter.clear();
            }
        });
    }

    //提示登录结果
    @Override
    public void showMessage(String values) {
        Message message = Message.obtain();
        message.what = 1;
        message.obj = values;
        myHandler.sendMessage(message);
    }

    //清除EditText
    @Override
    public void clearText() {
        etName.setText("");
        etPswd.setText("");
    }

    //获取用户信息
    @Override
    public void loginInfo() {
        loginPresenter.loadData(etName.getText().toString(), etPswd.getText().toString());
    }

    @Override
    public void startMain() {
        //MainActivity只是一个空的activity
        startActivity(new Intent(this, MainActivity.class));
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        myHandler.removeCallbacksAndMessages(null);
    }
}

    //获取用户信息
    @Override
    public void loginInfo() {
        loginPresenter.loadData(etName.getText().toString(), etPswd.getText().toString());
    }

    @Override
    public void startMain() {
        //MainActivity只是一个空的activity
        startActivity(new Intent(this, MainActivity.class));
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        myHandler.removeCallbacksAndMessages(null);
    }
}

上面的逻辑很简单,关键地方都加了注释,就不解释了,在这贴一下BaseActivity的代码:

public abstract class BaseActivity extends Activity {

    public ILoginPresenter loginPresenter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //加载布局
        setContentView(initLayoutView());
        //初始化ILoginPresenter的引用
        initView();
        loginPresenter = getLoginPresenter();
    }


    //获取ILoginPresenter的引用
    protected abstract ILoginPresenter getLoginPresenter();

    //让子类实现并返回布局文件ID
    protected abstract int initLayoutView();

    //绑定控件
    protected abstract void initView();
}

接下来,我们来看看如何实现这个桥梁吧。

4.Presenter层实现

上面我们已经说过,P层需要同时持有Model和View的引用,所以,我们需要在构造方法中将Model层的引用new出来,而View层的引用因为需要调用调用P里面的方法(即P->V,V->P),所以我们在View层获取P层引用时将View自身传到了P层,以便于建立P与V之间的双向引用,我们先来看看P的构造方法:

public class LoginPresenter implements ILoginPresenter {
    private ILoginView iLoginView;
    private ILoginModel iLoginModel;

    public LoginPresenter(ILoginView iLoginView) {
        //获取V层传递过来的实例
        this.iLoginView = iLoginView;
        //new一个M层的实例
        iLoginModel = new LoginModel();
    }
} 

现在,需要考虑P层的接口如何定义,上面说过,P层接口中的方法是暴露给View层来调用的,而我们又是登录的逻辑,所以在接口中肯定需要定义一个登录的方法来给View层调用,同时上面也定义了一下clear方法,因此,接口定义如下:

public interface ILoginPresenter {
    void clear();
    void loadData(String name, String pswd);
}

接下来我们来看看完整的LoginPresenter的实现:

public class LoginPresenter implements ILoginPresenter {
    private ILoginView iLoginView;
    private ILoginModel iLoginModel;

    public LoginPresenter(ILoginView iLoginView) {
        this.iLoginView = iLoginView;
        iLoginModel = new LoginModel();
    }

    @Override
    public void clear() {
        iLoginView.clearText();
    }

    @Override
    public void loadData(String name, String pswd) {
        UserInfo userInfo = new UserInfo(name, pswd);
        //调用M层的loginRequest()方法进行登录操作
        iLoginModel.loginRuquest(new ILoginCallback<UserInfo>() {
            @Override
            public void success(UserInfo userInfo) {
                //调用V层的showMessage()方法提示用户
                iLoginView.showMessage(userInfo.getUserName()+"登录成功");
                //调用V层的startMain()方法进行跳转
                iLoginView.startMain();
            }

            @Override
            public void failed(String value) {
                iLoginView.showMessage("登录失败");
            }
        },userInfo);
    }
}

至此,整个MVP模式就简单的分析完了~

5.总结

现在网上的MVP流程图可能很多都和我上面的不太一样,大多数都是如下所示:

其实也就是M层也持有P层的引用,区别就是需要在Model的构造方法中传入Presenter的引用,来让Presenter能调用到model的方法,但是本人不太喜欢这样使用,更喜欢通过回调的方式,感觉这样类与类之间的耦合更低!

呃呃,第一次写博客,如有理解不对的地方,请指正~

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值