Android-MVP入门

本文介绍了MVP(Model-View-Presenter)模式,对比了MVP与MVC的区别。在MVP中,View不直接使用Model,而是通过Presenter进行通信,降低了耦合。文中以登录为例,详细解释了Model、View和Presenter的角色和交互方式,并讨论了在Android中使用MVP模式可能遇到的内存泄漏问题及其解决方案。
摘要由CSDN通过智能技术生成

目录

 

1.什么是MVP

1.1MVP

1.2MVC

1.3MVP和MVC区别

3.如何使用MVP(以登录为示例)

3.1Model

3.2View

3.3Presenter


1.什么是MVP

1.1MVP

mvp的全称为Model-View-Presenter,Model提供数据,View负责显示,Controller/Presenter负责逻辑的处理。MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会直接从Model中读取数据而不是通过 Controller。

它降低了View与Model之间的耦合。彻底将View与Model分离。MVP不是一种标准化的模式,它由很多种实现。

Model:模型,实现业务逻辑和实例生成。
View:视图,对应的界面布局,以及界面布局的方法。
Presenter:一个活动中,主要的业务交互,作为view和model的中间桥梁。

1.2MVC

全称为Model-View-Controller,也就是模型-视图-控制器。MVC结构如下图所示:

在Android中对MVC的应用很经典,我们的布局文件如main.xml就是对应View层,本地的数据库数据或者是网络下载的数据就是对应Model层,而Activity对应Controller层。

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

1.3MVP和MVC区别

MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是通过 Controller。

在MVC里,View是可以直接访问Model的!从而,View里会包含Model信息,不可避免的还要包括一些业务逻辑。 在MVC模型里,更关注的Model的不变,而同时有多个对Model的不同显示,及View。所以,在MVC模型里,Model不依赖于View,但是View是依赖于Model的。不仅如此,因为有一些业务逻辑在View里实现了,导致要更改View也是比较困难的,至少那些业务逻辑是无法重用的。

2.为什么使用MVP

      在MVP里,Presenter完全把Model和View进行了分离,主要的程序逻辑在Presenter里实现。而且,Presenter与具体的View是没有直接关联的,而是通过定义好的接口进行交互,从而使得在变更View时候可以保持Presenter的不变,即重用! 不仅如此,我们还可以编写测试用的View,模拟用户的各种操作,从而实现对Presenter的测试--而不需要使用自动化的测试工具。 我们甚至可以在Model和View都没有完成时候,就可以通过编写Mock Object(即实现了Model和View的接口,但没有具体的内容的)来测试Presenter的逻辑。 在MVP里,应用程序的逻辑主要在Presenter来实现,其中的View是很薄的一层。因此就有人提出了Presenter First的设计模式,就是根据User Story来首先设计和开发Presenter。在这个过程中,View是很简单的,能够把信息显示清楚就可以了。在后面,根据需要再随便更改View,而对Presenter没有任何的影响了。 如果要实现的UI比较复杂,而且相关的显示逻辑还跟Model有关系,就可以在View和Presenter之间放置一个Adapter。由这个 Adapter来访问Model和View,避免两者之间的关联。而同时,因为Adapter实现了View的接口,从而可以保证与Presenter之间接口的不变。这样就可以保证View和Presenter之间接口的简洁,又不失去UI的灵活性。 在MVP模式里,View只应该有简单的Set/Get的方法,用户输入和设置界面显示的内容,除此就不应该有更多的内容,绝不容许直接访问Model--这就是与MVC很大的不同之处。

3.如何使用MVP(以登录为示例)

3.1Model

实现具体业务逻辑例如访问web,访问数据检索数据等相关数据处理;

package com.antonioleiva.mvpexample.app.login;

import android.os.Handler;
import android.text.TextUtils;

public class LoginInteractor {

    interface OnLoginFinishedListener {
        void onUsernameError();

        void onPasswordError();

        void onSuccess();
    }
    //实现登录业务逻辑
    public void login(final String username, final String password, final OnLoginFinishedListener listener) {
        // Mock login. I'm creating a handler to delay the answer a couple of seconds
        new Handler().postDelayed(() -> {
            if (TextUtils.isEmpty(username)) {
                listener.onUsernameError();
                return;
            }
            if (TextUtils.isEmpty(password)) {
                listener.onPasswordError();
                return;
            }
            listener.onSuccess();
        }, 2000);
    }
}

3.2View

视图,通常由一个Activity实现(它可能是一个Fragment,一个View……取决于应用程序的结构),将包含对presenter的引用。理想情况下,presenter将由像Dagger这样的依赖注入器提供,但是如果您不使用这样的东西,它将负责创建presenter对象。视图将做的唯一一件事是在每次有用户操作(例如单击按钮)时调用presenter方法。

由于presenter必须是视图无关的,所以它使用了一个需要实现的接口实现Presenter和View通信。下面是例子使用的接口:

定义通知View显示进度条,隐藏进度条等操作方法 ;

public interface LoginView {
    void showProgress();

    void hideProgress();

    void setUsernameError();

    void setPasswordError();

    void navigateToHome();
}

在Activity中实现Presenter和View通信接口:

public class LoginActivity extends AppCompatActivity implements LoginView {

    private LoginPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        //将LoginActivity(View)和Model注入到LoginPresenter,LoginPresenter可以直接调用实现方法通知View更行UI显示
        presenter = new LoginPresenter(this, new LoginInteractor());
    }

    @Override
    protected void onDestroy() {
        presenter.onDestroy();
        super.onDestroy();
    }
    //Presenter和View通信接口
    @Override
    public void showProgress() {//实现UI显示变化
        progressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideProgress() {//实现UI显示变化
        progressBar.setVisibility(View.GONE);
    }

    @Override
    public void setUsernameError() {//实现UI显示变化
        username.setError(getString(R.string.username_error));
    }

    @Override
    public void setPasswordError() {//实现UI显示变化
        password.setError(getString(R.string.password_error));
    }

    @Override
    public void navigateToHome() {//实现UI显示变化或页面跳转
        startActivity(new Intent(this, MainActivity.class));
        finish();
    }
    //调用Presenter实现验证用户名密码
    private void validateCredentials() {
        presenter.validateCredentials(username.getText().toString(), password.getText().toString());
    }
}

当点击登录按钮登录时,会调用Presenter登录认证方法presenter.validateCredentials(username.getText().toString(), password.getText().toString());

当执行onDestroy时调用presenter.onDestroy(),通知View已销毁不需要接受新的UI更新通知;

3.3Presenter

Presenter负责充当视图和模型之间的通信桥梁。它从模型中检索数据并将其格式化后返回给视图;

另外,与典型的MVC不同,它决定与视图交互时会发生什么。因此,对于用户可以执行的每个操作,它都有一个方法。我们在视图中看到了它,但这里是实现:

public class LoginPresenter implements LoginInteractor.OnLoginFinishedListener {

    private LoginView loginView;
    private LoginInteractor loginInteractor;
    //将View注入到Presenter中
    LoginPresenter(LoginView loginView, LoginInteractor loginInteractor) {
        this.loginView = loginView;
        this.loginInteractor = loginInteractor;
    }
    //在View中调用LoginPresenter的登录认证方法
    public void validateCredentials(String username, String password) {
        if (loginView != null) {
            loginView.showProgress();//通知View更新
        }
        //调用Model执行登录处理
        loginInteractor.login(username, password, this);
    }

    public void onDestroy() {
        loginView = null;
    }
    
    ......

}

注意事项:

MVP可能有一些风险,我们用来忘记的最重要的一点是,Presenter是永远依附于View的。视图是一个activity,这意味着:

a.我们可能使用长时间运行的任务引用Activity导致内存泄漏;

b.我们可以尝试更新已经销毁的Activity;

对于第一点,如果你能确保你的后台任务在合理的时间内完成,我不会太担心。泄露5-10秒的activity不会让你的应用程序变得更糟,解决这个问题的方法通常很复杂。

第二点更令人担忧。假设向服务器发送请求需要10秒,但用户在5秒后关闭活动。在调用回调并更新UI时,它将崩溃,因为activity已经完成。

解决泄漏问题的办法:

public void onDestroy() {
        loginView = null;//activity销毁时设置Presenter对View的引用为null
    }

 

在Android中,将接口与逻辑分离并不容易,但是MVP模式使我们能够更容易地防止我们的activities由数百甚至数千行组成的耦合类。在大型应用程序中,组织好我们的代码是很重要的。否则,就不可能维护和扩展。

 

参考:

https://antonioleiva.com/mvp-android/

https://github.com/antoniolg/androidmvp/blob/master/app/src/main/java/com/antonioleiva/mvpexample/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值