用一个简单的登录例子了解MVP思想

相信大家都或多或少的了解过MVP(全称:Model View Presenter)架构,MVP是从MVC演变过来的。MVC模式中,Activity即充当VIew又充当了Controller,使得Activity内的代码十分臃肿,难以进行维护,而MVP模式就很好的解决了这个问题。学习MVP架构对于自身的面向对象的理解也能更加深刻。
MVP
上面这张图片就可以很清晰的看出MVP各层之间的交互,其中:

  1. Model层:主要用来提供数据,比如从网上获取数据
  2. View层:即视图层,主要用来展示视图
  3. Presenter层:即逻辑层,View层与Model层的桥梁,处理一些逻辑操作

MVP与MVC最大的区别在与,你无法在View层看到任何逻辑处理,MVP中所有的逻辑处理都交由Presenter层来处理,View层只需要负责展示视图即可,有Presenter层的存在,实现了View层与Model层的解耦

下面我们就动手搭建一个MVP架构的登录功能吧,下面所用到的登录API是wanAndroid网站所提供的开放API

1. 项目结构

先来看看这个登录Demo的项目结构
项目结构

2. 创建契约类

因为MVP有个很明显的缺点就是,接口数量太多,而契约类就很好的帮我们整理好这些接口

契约类的代码如下:
public interface IContract {
	//View层
    public interface ILoginView {
        String getUserName();
        String getPassword();
        void onLoginSuccess(String msg);
        void onLoginFails(String msg);
    }

	//Presenter层
    public interface ILoginPresenter {
        void requestMsg(String userName, String password);
        void destroyView();
    }

	//Model层
    public interface ILoginModel {
        public interface CallBack{
            void onSuccess(String msg);
            void onFailed();
        }
        void requestData(String userName, String password, IContract.ILoginModel.CallBack callBack);
    }
}

很明显的可以看出来,IContract接口托管了MVP的三层接口,其中CallBack接口用于数据获取完成之后的回调

3. View层

View层,即我们的LoginActivity,主要负责的是view的操作,所以我们的LoginActivity需要实现ILoginView接口,重写其中的方法

public class LoginActivity  implements IContract.ILoginView {
    @Override
    public String getUserName() {
        return null;
    }

    @Override
    public String getPassword() {
        return null;
    }

    @Override
    public void onLoginSuccess(String msg) {

    }

    @Override
    public void onLoginFails(String msg) {

    }
}

之后我们的View层应该与Presenter层进行关联,所以我们的LoginActivity中应该持有Presenter的引用。

Activity中所有的代码如下:
public class LoginActivity extends AppCompatActivity implements IContract.ILoginView {

    private EditText userNameET;
    private EditText passwordET;
    private Button loginButton;
    private IContract.ILoginPresenter loginPresenter;

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

        loginPresenter = new LoginPresenter(this);

        userNameET = findViewById(R.id.username_et);
        passwordET = findViewById(R.id.password_et);
        loginButton = findViewById(R.id.login_button);
        loginButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                loginPresenter.requestMsg(getUserName(), getPassword());
            }
        });
    }

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

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

    @Override
    public void onLoginSuccess(final String msg) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(LoginActivity.this, "用户 : " + msg + " 登录成功", Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public void onLoginFails(final String msg) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(LoginActivity.this, msg, Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (loginPresenter != null) {
            loginPresenter.destroyView();
        }
    }
}

可以看到我们没有在Activity中做任何逻辑相关的处理,我们把获取到的用户名和密码传到Presenter层,然后在重写的方法中更行UI就可以了,这里只是简单的给了一个Toast

4. Presenter层

同样的,首先先实现P层的接口,重写里面的方法。

public class LoginPresenter implements IContract.ILoginPresenter {
    @Override
    public void requestMsg(String userName, String password) {
        
    }

    @Override
    public void destroyView() {

    }
}

因为P层是V层与M层的桥梁,所以P层需要持有V层和M层的引用,为了防止内存泄漏,我们一个弱引用,这些引用赋值操作我们把它放在构造方法中

 private IContract.ILoginView iLoginView;
    private IContract.ILoginModel iLoginModel;
    private WeakReference<IContract.ILoginView> iLoginViewWeakReference;
    private WeakReference<IContract.ILoginModel> iLoginModelWeakReference;
    private User user;

    public LoginPresenter(IContract.ILoginView iLoginView) {
        this.iLoginView = iLoginView;
        iLoginModel = new LoginModel();
        iLoginViewWeakReference = new WeakReference<>(iLoginView);
        iLoginModelWeakReference = new WeakReference<>(iLoginModel);
    }

我们看看重写的方法做了哪些操作,先判断View层传过来的用户名和密码是否为空,如果为空,就通知View层做相应的处理,在这里我们是传一个错误信息过去给View层做显示。如果用户民和密码都不为空,那么我们就把数据传到Model层去请求数据,然后通过CallBack这个接口回调把结果返回给View层

    @Override
    public void requestMsg(String userName, String password) {
        if (userName.equals("") || userName.isEmpty()) {
            iLoginView.onLoginFails("用户名不能为空");
        } else if (password.equals("") || password.isEmpty()) {
            iLoginView.onLoginFails("密码不能为空");
        } else {
            iLoginModel.requestData(userName, password, new IContract.ILoginModel.CallBack() {
                @Override
                public void onSuccess(String msg) {
                    parseJSONByGson(msg);
                    if (user.getErrorCode() == -1) {
                        iLoginView.onLoginFails(user.getErrorMsg());
                    } else {
                        iLoginView.onLoginSuccess(user.getData().getUsername());
                    }
                }

                @Override
                public void onFailed() {
                    iLoginView.onLoginFails("网络错误");
                }
            });
        }
    }

destroyView就是销毁View的方法

@Override
    public void destroyView() {
        iLoginViewWeakReference.clear();
        iLoginModelWeakReference.clear();
    }
Presenter层所有代码如下:
public class LoginPresenter implements IContract.ILoginPresenter {

    private IContract.ILoginView iLoginView;
    private IContract.ILoginModel iLoginModel;
    private WeakReference<IContract.ILoginView> iLoginViewWeakReference;
    private WeakReference<IContract.ILoginModel> iLoginModelWeakReference;
    private User user;

    public LoginPresenter(IContract.ILoginView iLoginView) {
        this.iLoginView = iLoginView;
        iLoginModel = new LoginModel();
        iLoginViewWeakReference = new WeakReference<>(iLoginView);
        iLoginModelWeakReference = new WeakReference<>(iLoginModel);
    }

    @Override
    public void requestMsg(String userName, String password) {
        if (userName.equals("") || userName.isEmpty()) {
            iLoginView.onLoginFails("用户名不能为空");
        } else if (password.equals("") || password.isEmpty()) {
            iLoginView.onLoginFails("密码不能为空");
        } else {
            iLoginModel.requestData(userName, password, new IContract.ILoginModel.CallBack() {
                @Override
                public void onSuccess(String msg) {
                    parseJSONByGson(msg);
                    if (user.getErrorCode() == -1) {
                        iLoginView.onLoginFails(user.getErrorMsg());
                    } else {
                        iLoginView.onLoginSuccess(user.getData().getUsername());
                    }
                }

                @Override
                public void onFailed() {
                    iLoginView.onLoginFails("网络错误");
                }
            });
        }
    }

    @Override
    public void destroyView() {
        iLoginViewWeakReference.clear();
        iLoginModelWeakReference.clear();
    }

    public void parseJSONByGson(String jsonData) {
        Gson gson = new Gson();
        user = gson.fromJson(jsonData, User.class);
    }
}

5. Model层

Model层,也就是负责请求数据的,同样先实现Model层接口,重写里面的方法

public class Model implements IContract.ILoginModel {
    @Override
    public void requestData(String userName, String password, CallBack callBack) {
        
    }
}

之后我们通过OKHttp进行登录请求,通过post请求,把用户名和密码传到网上进行查询,并将结果返回

Model层的所有代码如下:
public class LoginModel implements IContract.ILoginModel {

    @Override
    public void requestData(String userName, String password, final IContract.ILoginModel.CallBack callBack) {
        String url = "https://www.wanandroid.com/user/login";
        RequestBody requestBody = new FormBody.Builder()
                .add("username", userName)
                .add("password", password)
                .build();
        OkHttpClient okHttpClient = new OkHttpClient();
        final Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                callBack.onFailed();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String msg = response.body().string();
                callBack.onSuccess(msg);
            }
        });
    }
}

6. 总结

  1. 首先创建一个契约类来管理MVP三层接口
  2. 让Activity继承View层接口,重写方法,并持有Presenter层的引用
  3. 编写Presenter层实现类,重写方法做相关逻辑处理,将处理后的数据传到Model层,需持有View层和Model层的引用
  4. 编写Model层实现类,重写方法进行数据获取,然后将结果返回Presenter层
  5. Presenter层对Model层返回的结果再进行相关逻辑处理
  6. 没有问题后,把结果返回View层展示
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为你解答关于Android MVP模式和使用Kotlin模拟登录的问题。 首先,MVP模式是一种架构模式,它将应用程序分为三个部分:模型(Model),视图(View)和控制器(Presenter)。MVP模式的主要目的是将业务逻辑和用户界面分离,从而实现更好的代码可读性和可维护性。 在Android开发中,MVP模式通常用于处理复杂的UI和业务逻辑。下面是一个使用Kotlin模拟登录Android MVP模式示例: 1. 创建一个包含登录逻辑的模型 ```kotlin interface LoginModel { fun login(username: String, password: String, callback: LoginCallback) } interface LoginCallback { fun onSuccess() fun onFailure(error: String) } class LoginModelImpl : LoginModel { override fun login(username: String, password: String, callback: LoginCallback) { // 模拟登录 if (username == "admin" && password == "123456") { callback.onSuccess() } else { callback.onFailure("用户名或密码错误") } } } ``` 2. 创建一个视图接口和一个实现视图接口的Activity ```kotlin interface LoginView { fun showLoading() fun hideLoading() fun showToast(msg: String) } class LoginActivity : AppCompatActivity(), LoginView { private lateinit var presenter: LoginPresenter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_login) presenter = LoginPresenterImpl(this, LoginModelImpl()) btn_login.setOnClickListener { presenter.login(et_username.text.toString(), et_password.text.toString()) } } override fun showLoading() { // 显示加载中 } override fun hideLoading() { // 隐藏加载中 } override fun showToast(msg: String) { // 显示Toast } } ``` 3. 创建一个控制器(Presenter)来连接模型和视图 ```kotlin interface LoginPresenter { fun login(username: String, password: String) } class LoginPresenterImpl(private val view: LoginView, private val model: LoginModel) : LoginPresenter { override fun login(username: String, password: String) { view.showLoading() model.login(username, password, object : LoginCallback { override fun onSuccess() { view.hideLoading() view.showToast("登录成功") } override fun onFailure(error: String) { view.hideLoading() view.showToast(error) } }) } } ``` 在此示例中,LoginActivity是视图,LoginPresenterImpl是控制器(Presenter),LoginModelImpl是模型。当用户单击“登录”按钮时,LoginActivity将调用LoginPresenterImpl的login方法,该方法将调用LoginModelImpl的login方法进行模拟登录。如果登录成功,LoginPresenterImpl将通知LoginActivity以显示“登录成功”消息;否则,它将通知LoginActivity显示错误消息。 希望这个示例可以帮助你理解Android MVP模式和使用Kotlin模拟登录的实现。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值