Android MVP 实战——MVP 在 Android 中的简单应用

2015-08-12 15:50:18 +08:00
 Bison

导读:上一章我们初探了Android MVP,但是只涉及到一些概念性的东西,这一章,我们将来一起来一步步实现一个简单的MVP的Demo

回顾

上章我们说到如何抽离MVP的层?还记得大致的步骤么?

好了,我们就先按照这个步骤来吧。首先:

需求

想来想去,最后决定还是用登陆这个比较典型的粟子。如下:

需求很简单,那么下面开始实战吧!

实现

创建项目MVPDemo,这里我选择的开发环境是AndroidStudio.<br>下面先看一下包结构:

按照MVP的通用结构,将其分为了三层,那么接下来是布局文件,这没什么好说的,大家大致过一下就好:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

<EditText
    android:layout_width="match_parent"
    android:id="@+id/userName"
    android:hint="userName"
    android:layout_height="wrap_content" />
<EditText
    android:layout_width="match_parent"
    android:id="@+id/passWord"
    android:hint="passWord"
    android:layout_height="wrap_content" />


<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:onClick="onAction"
    android:text="登录"/>

</LinearLayout>

就是简单的两个输入框,一个用于登录的Button.接下来,我们再来按照昨天说的步骤,开始MVP的应用。

以下,贴出ViewInterface的抽离代码,取名为LoginViewInterface.

public interface LoginViewInterface {
            String getUserName();

            String getPassword();

            void showProgress();

            void dismissProgress();

            void showLoginFail(String error);

            void goToActivity();
        }

再次回顾一下,我们可以发现,在LoginViewInterface中,出现的接口都是与交互,与UI,与界面元素有关的。

因为这里登录是属于耗时操作,所以我们在Presenter层定义了一个内部回调接口,用于在异步线程中回调。代码如下

public interface CallBack{

          void onSuccess(LoginInfo info);

          void onFail(Throwable error);
    }

这里说明一下,有些朋友可能会疑惑,你不是说ModelInterface只需要提供需要的数据就行了吗?登录业务的话,那这里只需要在登录成功的时候,返回一个成功的状态码,再加上一个LoginInfo给Presenter层,登录失败返回错误异常就行了,不需要进行逻辑判断。但你这里明显是用if else进行了逻辑判断啊?

好吧,这是因为我们这里是模拟登录。也就是说在实际开发中,判断登录成功与失败是在服务器中进行的,我们这儿的逻辑也是模拟的服务器校验逻辑。而且,MVP的根本目的在于减轻Activity或Fragment的当量,即使有一些简单的逻辑处理混在里面倒也无需计较。

最后还有我们的Activity:

public class LoginActivity extends AppCompatActivity implements LoginViewInterface {

        private EditText userNameEdit;
        private EditText pwdEdit;
        private ProgressDialog dialog;

        private LoginPresenter loginPresenter = new LoginPresenter(this);

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

        private void initViews() {

            userNameEdit = (EditText) findViewById(R.id.userName);
            pwdEdit = (EditText) findViewById(R.id.passWord);
        }

        public void onAction(View v){
            loginPresenter.login();
        }


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

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

        @Override
        public void showProgress() {
            if(dialog==null){
                dialog = ProgressDialog.show(this,"","loading...");
            }else{
                if(!dialog.isShowing()){
                    dialog.show();
                }
            }
        }

        @Override
        public void dismissProgress() {
            if(dialog!=null&&dialog.isShowing()){
                dialog.dismiss();
            }
        }

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

        }

        @Override
        public void goToActivity() {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(LoginActivity.this, "登录成功,跳转HomeActivity", Toast.LENGTH_SHORT).show();
                }
            });

        }
    }

这里跳转与错误信息提示,我都偷懒直接用的Toast了。另外为防止子线程刷新UI崩溃,这里作了一下处理。当然也可以Activity中不做处理,而放在Presenter层通过Handler来实现异步UI更新,我这里是只是图个便捷。

好了,到这里这么一个简单的Demo便完成了,功能比较单一,也就不贴效果图了。回顾一下,有的朋友可能会说,这么一个简单的登录业务就给我搞了这么多类,而实际开发中一个界面涉及的业务何其之多,使用MVP真的现实吗?其实,这是可以的,并不是虚妄,而且实质上,在熟练以后,它并不会给你增加多少工作量,反而会让你在以后的维护工作中轻松自如。有兴趣的同学,可以看一下 chrisbanes的开源项目philm,其整体架构就是一套MVP的实现,另外下面附上本文中的Demo源码

源码下载

12434 次点击
所在节点    Android
5 条回复
lugeek
2015-08-12 19:11:48 +08:00
项目结构复杂了,耦合降低了。
21grams
2015-08-12 19:45:25 +08:00
搞这么多接口有必要吗
F1ReKing
2015-08-12 23:33:45 +08:00
有点复杂
ybjaychou
2015-08-13 11:21:05 +08:00
正在学习MVP,这篇文章写的也不错http://zhengxiaopeng.com/2015/02/06/Android%E4%B8%AD%E7%9A%84MVP/
kaient
2015-08-19 10:58:34 +08:00
我觉得 LoginPresenter 里的 callback 接口应该在 LoginBusinessImp 里定义。一般就是谁调用谁定义。如果某一天你把 LoginPresenter 干掉了,那 LoginBusinessImp 里的代码就得改。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/212695

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX