欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android MVP模式浅析

程序员文章站 2022-03-09 16:15:07
...

 

1.什么是MVP?为什么使用MVP?

 

Android MVP模式浅析

 

View:负责界面刷新、布局等一切和UI界面相关的操作;

Presenter:连接Model和View的桥梁,属于逻辑实现层;

Model :负责数据相关的工作,比如去网络获取数据,去数据库读取数据等;

MVP模式中,Presenter(以下简称:P)层同时持有Model(以下简称:M)和View(以下简称:V)的对象引用,可以对M和V进行操作,这也是P层的职责所在,而V与M之间是没有任何关联的,完全解耦,M通过回调的方式返回值给P,而V只持有P的对象引用。

优点:高度解耦,方便项目维护,方便功能测试,维护起来更加的便利,简化Activity文件结构等,

缺点:需要编写大量的接口。

接下来我将写一个超级简单的登录Demo来演示MVP的使用,首先我们先来看看项目的基本结构

Android MVP模式浅析

接下来我们来看看如何分别实现各层之间的逻辑,先来看看Model层的实现。

2.Model层实现

由于model并未持有presenter层的引用,所以我们一般使用回调的方式将model层执行后的结果返回给presenter层,所以我们在这首先定义了一个ILoginCallback的回调函数,方便LoginModel将登录结果返回给LoginPresenter,我们来看看这个回调函数的定义:

public interface ILoginCallback<T> {
    //登录成功的回调方法
    void success(T t);
    //登录失败的回调方法
    void failed(String value);
}

 

接下来我们来看看ILoginModel接口的定义,首先既然我们的接口里面的方法是定义给LoginPresenter使用的,就需要先明白在登录时P层需要做什么操作,很明显登录的话最少需要一个进行登录请求的方法,所以ILoginModel接口的定义如下:

 

public interface ILoginModel {
    //进行网络操作:登录请求
    void loginRuquest(ILoginCallback<UserInfo> iLoginCallback, UserInfo userInfo);
}

既然接口已经定义了,接下来看看LoginModel的具体实现,逻辑很简单,就是做一个简单模拟登录的操作,匹配成功调用LoginCallback的success()方法,否则调用failed()方法。

public class LoginModel implements ILoginModel {
    @Override
    public void loginRuquest(final ILoginCallback<UserInfo> iLoginCallback, final UserInfo userInfo) {
        //模拟去服务器验证用户名和密码的操作
        new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    Thread.sleep(3000);
                    if (userInfo.getUserName().equals("123") && userInfo.getPassword().equals("123")) {
                        iLoginCallback.success(userInfo);
                    }else{
                        iLoginCallback.failed("登录失败");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
}

接下来我们来看看如何实现View层的逻辑。

 

3.View层实现

首先我们先来看看接口的实现,上面说过接口中的方法是用来给P层调用的,所以我们需要思考一下登录的过程中Activity需要哪些操作,在这里我就简单的定义了一下行为,如下:

public interface ILoginView {
    //提示登录信息
    void showMessage(String values);

    //清楚登录信息
    void clearText();

    //登录信息
    void loginInfo();

    //登陆成功的界面
    void startMain();
}

OK,接下来来看看LoginView对ILoginView接口的实现:

public class LoginActivity extends BaseActivity implements ILoginView {
    private Button btnLogin, btnClear;
    private EditText etName, etPswd;
    private ProgressBar pBar;
    private final static String TAG = "felix";

    private MyHandler myHandler = new MyHandler(this);

    public static class MyHandler extends Handler {

        WeakReference<Context> reference;

        public MyHandler(Context context) {
            reference = new WeakReference<>(context);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            LoginActivity activity = (LoginActivity) reference.get();
            if (msg.what == 1) {
                activity.pBar.setVisibility(View.GONE);
                Toast.makeText(activity, msg.obj.toString() ,Toast.LENGTH_SHORT).show();
            }
        }
    }

    //将Activity引用传递给presenter
    @Override
    protected ILoginPresenter getLoginPresenter() {
        return new LoginPresenter(this);
    }

    //加载布局
    @Override
    protected int initLayoutView() {
        return R.layout.activity_ilogin;
    }

    //初始化View
    @Override
    protected void initView() {
        etName = findViewById(R.id.et_name);
        etPswd = findViewById(R.id.et_pswd);
        btnLogin = findViewById(R.id.btn_login);
        btnClear = findViewById(R.id.btn_clear);
        pBar = findViewById(R.id.progress_bar);

        pBar.setVisibility(View.GONE);
        btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                loginInfo();
                pBar.setVisibility(View.VISIBLE);
            }
        });

        btnClear.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                loginPresenter.clear();
            }
        });
    }

    //提示登录结果
    @Override
    public void showMessage(String values) {
        Message message = Message.obtain();
        message.what = 1;
        message.obj = values;
        myHandler.sendMessage(message);
    }

    //清除EditText
    @Override
    public void clearText() {
        etName.setText("");
        etPswd.setText("");
    }

    //获取用户信息
    @Override
    public void loginInfo() {
        loginPresenter.loadData(etName.getText().toString(), etPswd.getText().toString());
    }

    @Override
    public void startMain() {
        //MainActivity只是一个空的activity
        startActivity(new Intent(this, MainActivity.class));
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        myHandler.removeCallbacksAndMessages(null);
    }
}

    //获取用户信息
    @Override
    public void loginInfo() {
        loginPresenter.loadData(etName.getText().toString(), etPswd.getText().toString());
    }

    @Override
    public void startMain() {
        //MainActivity只是一个空的activity
        startActivity(new Intent(this, MainActivity.class));
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        myHandler.removeCallbacksAndMessages(null);
    }
}

上面的逻辑很简单,关键地方都加了注释,就不解释了,在这贴一下BaseActivity的代码:

public abstract class BaseActivity extends Activity {

    public ILoginPresenter loginPresenter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //加载布局
        setContentView(initLayoutView());
        //初始化ILoginPresenter的引用
        initView();
        loginPresenter = getLoginPresenter();
    }


    //获取ILoginPresenter的引用
    protected abstract ILoginPresenter getLoginPresenter();

    //让子类实现并返回布局文件ID
    protected abstract int initLayoutView();

    //绑定控件
    protected abstract void initView();
}

接下来,我们来看看如何实现这个桥梁吧。

4.Presenter层实现

上面我们已经说过,P层需要同时持有Model和View的引用,所以,我们需要在构造方法中将Model层的引用new出来,而View层的引用因为需要调用调用P里面的方法(即P->V,V->P),所以我们在View层获取P层引用时将View自身传到了P层,以便于建立P与V之间的双向引用,我们先来看看P的构造方法:

public class LoginPresenter implements ILoginPresenter {
    private ILoginView iLoginView;
    private ILoginModel iLoginModel;

    public LoginPresenter(ILoginView iLoginView) {
        //获取V层传递过来的实例
        this.iLoginView = iLoginView;
        //new一个M层的实例
        iLoginModel = new LoginModel();
    }
} 

现在,需要考虑P层的接口如何定义,上面说过,P层接口中的方法是暴露给View层来调用的,而我们又是登录的逻辑,所以在接口中肯定需要定义一个登录的方法来给View层调用,同时上面也定义了一下clear方法,因此,接口定义如下:

public interface ILoginPresenter {
    void clear();
    void loadData(String name, String pswd);
}

接下来我们来看看完整的LoginPresenter的实现:

public class LoginPresenter implements ILoginPresenter {
    private ILoginView iLoginView;
    private ILoginModel iLoginModel;

    public LoginPresenter(ILoginView iLoginView) {
        this.iLoginView = iLoginView;
        iLoginModel = new LoginModel();
    }

    @Override
    public void clear() {
        iLoginView.clearText();
    }

    @Override
    public void loadData(String name, String pswd) {
        UserInfo userInfo = new UserInfo(name, pswd);
        //调用M层的loginRequest()方法进行登录操作
        iLoginModel.loginRuquest(new ILoginCallback<UserInfo>() {
            @Override
            public void success(UserInfo userInfo) {
                //调用V层的showMessage()方法提示用户
                iLoginView.showMessage(userInfo.getUserName()+"登录成功");
                //调用V层的startMain()方法进行跳转
                iLoginView.startMain();
            }

            @Override
            public void failed(String value) {
                iLoginView.showMessage("登录失败");
            }
        },userInfo);
    }
}

至此,整个MVP模式就简单的分析完了~

5.总结

现在网上的MVP流程图可能很多都和我上面的不太一样,大多数都是如下所示:

Android MVP模式浅析

其实也就是M层也持有P层的引用,区别就是需要在Model的构造方法中传入Presenter的引用,来让Presenter能调用到model的方法,但是本人不太喜欢这样使用,更喜欢通过回调的方式,感觉这样类与类之间的耦合更低!

呃呃,第一次写博客,如有理解不对的地方,请指正~