zoukankan      html  css  js  c++  java
  • 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也好,要基本要解决的问题都是数据和视图的交互问题:获取视图提供的数据,比如输入,响应视图事件,然后反馈给视图。为什么会提供这两种解决方案呢?就是要解决耦合,重用。

    
    


  • 相关阅读:
    css样式的六种选择器
    css 颜色表示法
    css 文本设置
    “http”和“https”的区别是什么?优缺点是什么?
    Httpclient
    接口认证:Bearer Token(Token 令牌)
    哪个参数用来区分请求来自客户(手机)端还是服务器(PC)端?
    常用的HTTP响应头
    Http 请求头包含哪些信息?
    单例集合的体系
  • 原文地址:https://www.cnblogs.com/hbolin/p/11019973.html
Copyright © 2011-2022 走看看