Android找工作系列之MVP架构

有关MVC架构的详见:

Android找工作系列之MVC模式

MVP架构主要是要解决什么问题呢?主要是为了解决MVC架构中C的冗余问题,简单的说就是在Android中解决Activity的这个Controller层太重的问题,换句话说就是代码太多的问题。

在MVC架构中,Model层和View层的交互发生在Controller中,即是发生在Activity中,Activity即要从View中获取事件,又要反馈事件,那么Model层和View层就和Activity耦合程度过多,而且代码臃肿。为了解决这个问题,引入了Presenter层,那么Activity做什么用呢?Activity此时反而单纯的充当View层,只View层的工作:即使提供View数据,更新View数据等只和View层相关的工作。

详见代码解释:

用户登录MVC做法:

public class RegisterActivity extends AppCompatActivity {

    private EditText mUsername;
    private EditText mPassword;
    private String mUsername1;
    private String mPassword1;
    private ProgressDialog mProgressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_register);
        mUsername = (EditText) findViewById(R.id.username);
        mPassword = (EditText) findViewById(R.id.password);
    }

    public void register(View view) {
        mUsername1 = mUsername.getText().toString().trim();
        mPassword1 = mPassword.getText().toString().trim();

        User user = new User();
        user.setUserName(mUsername1);
        user.setPassword(mPassword1);
        mProgressDialog = ProgressDialog.show(this, "注册", "玩命儿注册中.....");
        RetrofitClient.getInstance().getAPI().postRequest(user).enqueue(new Callback<UserResult>() {


            @Override
            public void onResponse(Call<UserResult> call, Response<UserResult> response) {
                mProgressDialog.dismiss();
                if (response.isSuccessful()){
                    UserResult userResult = response.body();
                    if (userResult!=null){
                        if (userResult.getErrcode()==1){
                            Toast.makeText(RegisterActivity.this,"注册成功!!",Toast.LENGTH_SHORT).show();
                            return;
                        }
                        Toast.makeText(RegisterActivity.this,userResult.getErrmsg(),Toast.LENGTH_SHORT).show();
                        return;
                    }
                    Toast.makeText(RegisterActivity.this,"未知错误!",Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onFailure(Call<UserResult> call, Throwable t) {
                mProgressDialog.dismiss();
                Toast.makeText(RegisterActivity.this,"注册失败",Toast.LENGTH_SHORT).show();
            }
        });

    }
}

很明显Activity即要获取视图数据,mUsername1,mPassword1,响应视图事件 register(View view),还要从Model层中获取数据,并且根据成功还是失败来更新视图。

如果只是一个登录事件需要触发,那以上代码没什么问题,代码少,一言就能看出来。但是如果这个Activity的按钮多,即使视图事件多,那么这个Activity就会膨胀起来,代码多得烦。

来看下MVP的解决代码:

 View层:

public interface LoginView {
    /**
     * 登录时,需要账号
     */
    String getUserName();

    /**
     * 登录时,需要密码
     */
    String getPassword();

    /**
     * 用户登录成功的处理
     */
    void loginSuccess();

    /**
     * 用户登录成功的处理
     */
    void loginFailed();
}

Presenter层:

import android.os.Handler;

public class LoginPresenter {

    private LoginView loginView;

    LoginPresenter(LoginView loginView) {
        this.loginView = loginView;
    }

    void UserLogin() {

        // 模拟网络请求 -- 这层就是model层,我一般称之为数据来源层,我这里懒得写了,直接用Handler替代
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {

                // 逻辑处理
                if (loginView.getUserName().equals("111") && loginView.getPassword().equals("000")) {
                    loginView.loginSuccess();
                } else {
                    loginView.loginFailed();
                }
            }
        }, 2000);
    }

}

Mode层,我就不写了,直接在Presenter层上用Handler模拟。

附上Activity:

public class LoginActivity extends AppCompatActivity implements LoginView {

    LoginPresenter mLoginPresenter;

    EditText editText, editText2;
    Button button;

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

        editText = findViewById(R.id.editText);
        editText2 = findViewById(R.id.editText2);
        button = findViewById(R.id.button);

        mLoginPresenter = new LoginPresenter(this);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mLoginPresenter.UserLogin();
            }
        });
    }


    @Override
    public String getUserName() {
        return editText.getText().toString().trim();
    }

    @Override
    public String getPassword() {
        return editText2.getText().toString().trim();
    }

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

    @Override
    public void loginFailed() {
        Toast.makeText(this, "账号或者密码错误", Toast.LENGTH_LONG).show();
    }
}

很好,一个完整的简单的MVP模式就写好了。

详解一下:

在上面的Model层的用户Login接口需要什么?需要账号和密码2个参数,那么View层的接口就需要提供getUserName和getPassword接口,另外登录会成功,也可能会失败,则需要View层去处理,那么View层就提供了2个接口loginSuccess和loginFailed。简单的说来,Model层需要参数,并且响应的结果,即是View层处理的,View层都需要提供接口,需要强调的是这个View层是个Interface,并非具体的实现,具体的实现交由Activity去提供,或者其他视图去提供。

Presenter层需要做什么事情呢?就是链接View层和Model层,即是提供View层和Model层交互的地方,在Android MVC中,View层和Model层的交互发生在什么地方呢?发生在Activity中,而MVP模式则是发生在Presenter中,那么Activity的耦合性就下降了。那么Activity要做的事情是什么呢?就是实现View层接口。提供相关的数据和响应的结果。

MVP对于MVC的优点在哪里?就是解放了Activity,耦合性降低。缺点是啥?代码变多了,还得多写Presenter层。

其实以上代码是有风险的,如果Activity被销毁了,loginView.loginSuccess();中的LoginView可能就是个空指针了。所以我们写个基类来优化下。

BaseView:

public interface MvpBaseView {
    /**
     * 显示正在加载view
     */
    void showLoading();

    /**
     * 关闭正在加载view
     */
    void hideLoading();

    /**
     * 显示提示
     *
     * @param msg
     */
    void showToast(String msg);

    /**
     * 获取上下文
     *
     * @return 上下文
     */
    Context getContext();
}

BasePresenter:

public class MvpBasePresenter<T extends MvpBaseView> {
    /**
     * 绑定的view
     */
    protected T mvpView;

    /**
     * 绑定view,一般在初始化中调用该方法
     */
    public void attachView(T mvpView) {
        this.mvpView = mvpView;
    }

    /**
     * 断开view,一般在onDestroy中调用
     */
    public void detachView() {
        this.mvpView = null;
    }

    /**
     * 是否与View建立连接
     * 每次调用业务请求的时候都要出先调用方法检查是否与View建立连接
     */
    public boolean isViewAttached() {
        return mvpView != null;
    }

    /**
     * 获取连接的view
     */
    public T getView() {
        return mvpView;
    }
}

BaseMvpActivity:

public abstract class MvpBaseActivity extends AppCompatActivity implements MvpBaseView {

    private ProgressDialog mProgressDialog;

    private MvpBasePresenter mMvpBasePresenter;


    public abstract MvpBasePresenter setPresenter();


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mProgressDialog = new ProgressDialog(this);
        mProgressDialog.setCancelable(false);
        mMvpBasePresenter = setPresenter();
        mMvpBasePresenter.attachView(this);
    }

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

    @Override
    public void showLoading() {
        if (!mProgressDialog.isShowing()) {
            mProgressDialog.show();
        }
    }

    @Override
    public void hideLoading() {
        if (mProgressDialog.isShowing()) {
            mProgressDialog.dismiss();
        }
    }

    @Override
    public void showToast(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }

    @Override
    public Context getContext() {
        return MvpBaseActivity.this;
    }
}

实际使用:

/**
 * 接口继承
 */
public interface LoginView extends MvpBaseView {

    /**
     * 用户登录成功的处理
     */
    void loginSuccess();

    String getUserName();

    String getPassword();
}
public class LoginPresenter extends MvpBasePresenter<LoginView> {

    void UserLogin() {
        // 视图层的显示
        mvpView.showLoading();

        // 如果当前视图已经被销毁了,就不要进行Model层的数据请求了
        if (!this.isViewAttached()) {
            return;
        }

        // 模拟网络请求 -- 这层就是model层,我一般称之为数据来源层
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                // 视图层的显示
                mvpView.hideLoading();

                // 逻辑处理
                if (mvpView.getUserName().equals("111") && mvpView.getPassword().equals("000")) {
                    // 如果当前视图已经被销毁了,就不要进行View层的数据更新了
                    if (!LoginPresenter.this.isViewAttached()) {
                        return;
                    }
                    mvpView.loginSuccess();
                } else {
                    // 如果当前视图已经被销毁了,就不要进行View层的数据更新了
                    if (!LoginPresenter.this.isViewAttached()) {
                        return;
                    }
                    mvpView.showToast("账号或者密码错误");
                }
            }
        }, 2000);
    }
}
public class MainActivity extends MvpBaseActivity implements LoginView {

    LoginPresenter mLoginPresenter;

    EditText editText, editText2;
    Button button, button2;

    @Override
    public MvpBasePresenter setPresenter() {
        mLoginPresenter = new LoginPresenter();
        return mLoginPresenter;
    }

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

        editText = findViewById(R.id.editText);
        editText2 = findViewById(R.id.editText2);
        button = findViewById(R.id.button);
        button2 = findViewById(R.id.button2);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mLoginPresenter.UserLogin();
            }
        });

        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainActivity.this, LoginActivity.class));
            }
        });
    }

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

    @Override
    public String getUserName() {
        return editText.getText().toString().trim();
    }

    @Override
    public String getPassword() {
        return editText2.getText().toString().trim();
    }
}

一样Model层,我任然没写。懒得写...

代码:

https://github.com/hbolin/android_mvp_demo

总之,不管是MVC也好,还是MVP也好,要基本要解决的问题都是数据和视图的交互问题:获取视图提供的数据,比如输入,响应视图事件,然后反馈给视图。为什么会提供这两种解决方案呢?就是要解决耦合,重用。




原文地址:https://www.cnblogs.com/hbolin/p/11019973.html