Android中的MVP模式(1)

说说MVC


View:对应于布局文件

Model:业务逻辑和实体模型
Controllor:对应于Activity

上面可以看到View和Controllor的界限很模糊,View对应于布局文件,其实能做的事情特别少,另外,布局文件中的数据绑定的操作,事件处理的代码都在Activity中,造成了Activity既像View又像Controller。所以给人的感觉就好像只是View-Model模式一样。


说说MVP

View:负责显示数据以及与用户交互,可以是Activity, Fragment, android.view.View或者Dialog。
Model:数据访问层,例如:数据库API,远程服务器API
Presenter:负责View与Model间的交互,主要处理后台任务以及提供Model中的数据给View。

下面详细说说各层:
(1)model层主要负责:
从网络,数据库,文件,传感器,第三方等数据源读写数据。
对外部的数据类型进行解析转换为APP内部数据交由上层处理。
对数据的临时存储,管理,协调上层数据请求。

(2)view 层主要负责:
提供UI交互
在presenter的控制下修改UI。
将业务事件交由presenter处理。

注意: View层不存储数据,不与Model层交互。
在Android中View层一般是Activity、Fragment、View(控件)、ViewGroup(布局等)等。

(3)Presenter作为View与Model交互的中间纽带,处理与用户交互的处理逻辑。Presenter根据用户在视图的中交互来完成一系列的处理逻辑,包括对用户行为的处理,将视图数据发送给模型,将模型数据发生给视图。

在MVP模式里通常包含4个要素:
(1) View :负责绘制UI元素、与用户进行交互(在Android中体现为Activity);
(2) View interface :需要View实现的接口,View通过View interface与Presenter进行交互,降低耦合,方便进行单元测试;
(3) Model :负责存储、检索、操纵数据(有时也实现一个Model interface用来降低耦合);
(4) Presenter :作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。

下面我们来结合代码分析,代码使用的是github上的一个开源例子,源码地址:androidmvp

我们以其中的Login为例
(1)定义一个View层接口,主要是为了让View层进行实现,例如Activity,它包含了Presenter层对View的回调操作,用于UI的更新。

public interface LoginView {
    void showProgress();

    void hideProgress();

    void setUsernameError();

    void setPasswordError();

    void navigateToHome();
}
可以看到上面定义了一个LoginView接口,接口中声明了一些操作,这些操作都是View层的更新操作。

(2)定义View层代码,下面对应的就是Activity。
public class LoginActivity extends Activity implements LoginView, View.OnClickListener {

    private ProgressBar progressBar;
    private EditText username;
    private EditText password;
    private LoginPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        progressBar = (ProgressBar) findViewById(R.id.progress);
        username = (EditText) findViewById(R.id.username);
        password = (EditText) findViewById(R.id.password);
        findViewById(R.id.button).setOnClickListener(this);

        presenter = new LoginPresenterImpl(this);
    }

    @Override protected void onDestroy() {
        presenter.onDestroy();
        super.onDestroy();
    }

    @Override public void showProgress() {
        progressBar.setVisibility(View.VISIBLE);
    }

    @Override public void hideProgress() {
        progressBar.setVisibility(View.GONE);
    }

    @Override public void setUsernameError() {
        username.setError(getString(R.string.username_error));
    }

    @Override public void setPasswordError() {
        password.setError(getString(R.string.password_error));
    }

    @Override public void navigateToHome() {
        startActivity(new Intent(this, MainActivity.class));
        finish();
    }

    @Override public void onClick(View v) {
        presenter.validateCredentials(username.getText().toString(), password.getText().toString());
    }
}
可以看到,Activity实现了LoginView接口,用于对UI界面进行更新。那么这些更新操作是谁来调用,什么时候会被调用,怎样调用,这是我们关心的问题。我们可以知道,这些更新操作是对处理逻辑在界面上的一种反馈,例如,当用户点击登录,这时就开始登录,处理逻辑会对用户名和密码进行校验,并且这时应该调用showProgress来显示进度,所以可以看到,这些操作应该由处理逻辑进行回调,也就是由Presenter层代码来回调。下面我们来看看它是如何调用的。

(3)定义Presenter层代码,主要用来对View层与Model层进行交互处理操作。
public interface LoginPresenter {
    void validateCredentials(String username, String password);

    void onDestroy();
}
上面定义了一个接口,抽象了对处理方法。

public class LoginPresenterImpl implements LoginPresenter, LoginInteractor.OnLoginFinishedListener {

    private LoginView loginView;
    private LoginInteractor loginInteractor;

    public LoginPresenterImpl(LoginView loginView) {
        this.loginView = loginView;
        this.loginInteractor = new LoginInteractorImpl();
    }

    @Override public void validateCredentials(String username, String password) {
        if (loginView != null) {
            loginView.showProgress();
        }

        loginInteractor.login(username, password, this);
    }

    @Override public void onDestroy() {
        loginView = null;
    }

    @Override public void onUsernameError() {
        if (loginView != null) {
            loginView.setUsernameError();
            loginView.hideProgress();
        }
    }

    @Override public void onPasswordError() {
        if (loginView != null) {
            loginView.setPasswordError();
            loginView.hideProgress();
        }
    }

    @Override public void onSuccess() {
        if (loginView != null) {
            loginView.navigateToHome();
        }
    }
}
从上面代码我们可以看到,在LoginPresenterImpl的构造函数里面传入了一个LoginView类型的对象,也就是LoginView里面包含了一个LoginView类型引用,这就解决了UI更新操作如何调的问题,我们可以看到在LoginPresenterImpl里面实现了对LoginView方法的回调。

下面我们再回到LoginActivity里面,我们可以看到在onCreate方法里面创建了一个LoginPresenterImpl,它的传入参数为this,也就是Activity本身,因为Activity实现了LoginView,也相当于传入了一个LoginView类型的对象。这样在Activity里面通过对LoginPresenterImpl对象的方法调用,将业务的处理操作都放在LoginPresenterImpl里面,这就保证了Activity只是起到View的作用,只做用户交互和UI更新操作,在LoginPresenterImpl里面会根据业务的处理情况,回调LoginView方法进行UI更新,这样就将业务处理操作与UI进行了同步。
presenter = new LoginPresenterImpl(this);
(4)实现Model层进行数据访问。在LoginPresenterImpl里面,我们需要对View层带过来的数据进行校验,这里需要通过服务数据库或者远程服务器来查看用户名或者密码是否正确,这里对数据库或者远程服务器的操作就应该在Model中进行,Model层的数据访问结果会返回给LoginPresenterImpl对象,然后由LoginPresenterImpl对象来回调进行UI更新。


参考文章:

用MVP架构开发Android应用

Android MVP 详解(上)

Android MVP 详解(下)
Introduction to Model View Presenter on Android
MVP 模式在 GankDaily 中的应用

欢迎关注我的公众号:DroidMind

精品内容,独家发布


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值