前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android MVC、MVP、MVVM、MVP-databinding 架构单元示例

Android MVC、MVP、MVVM、MVP-databinding 架构单元示例

原创
作者头像
Jingbin
发布2019-03-18 17:50:36
1.2K0
发布2019-03-18 17:50:36
举报
文章被收录于专栏:Android 技术栈

GitHub地址:ProjectPatternStudy

基本Android项目都采用MVC、MVP、MVVM架构,个人认为软件架构没有绝对的优劣之分,大家都各有利弊。

  • 如果页面比较单一,采用MVC也未尝不可;
  • 如果需要稳定性高,解耦性强就可以选用MVP,使M层与V层分离,结构更清晰;
  • 如果想尝鲜(其实已经有段时间了),少写接口,高效,也可以使用MVVM;

阮一峰《MVC,MVP 和 MVVM 的图示》总结的非常简练,这里相当于扩展了一下,对于不太懂的人可能会用处更大。

MVP-databinding:是使用MVP架构,但是布局使用databinding设置值,也是行之有效的一种,也可以满足你的需求。

MVC

Model-View-Controller,最常见的软件架构之一。

  • 视图(View):用户界面。
  • 控制器(Controller):业务逻辑
  • 模型(Model):数据保存
来自MVC----MVC,MVP 和 MVVM 的图示
来自MVC----MVC,MVP 和 MVVM 的图示

Avtivity里的一个点击事件:

代码语言:txt
复制
/\*\*

 \* 将业务逻辑封装在Model里, 但C(Activity)层可以和Model直接交互, 交互完后根据显示结果来调整V层(如 显示数据)

 \*/

EssayModel essayModel = new EssayModel(LoadDataActivity.this);

essayModel.getEssay(3, new EssayModel.OnEssayListener() {

    @Override

    public void onSuccess(List<Essay> list) {

        /\*\*

         \* 直接使用list,得到List的逻辑都放在mode层

         \*/

        if (list != null && list.get(0) != null) {

            tvViewUpdata.setText("MVC 更新数据: " + list.get(0).getTitle());

        }

    }



    @Override

    public void onError() {



    }

});

如果一个页面比较简单,只有简单的几个操作,也不会经常去改可以使用此方式;如果页面逻辑比较复杂,接口请求都有好几个,那么不建议使用MVC,因为代码会全部堆积在一个Activity里面,会显得非常之冗余。

MVP

MVP 模式将 Controller 改名为 Presenter,同时改变了通信方向。

来自MVP----MVC,MVP 和 MVVM 的图示
来自MVP----MVC,MVP 和 MVVM 的图示

通过P层将Model层与View层解耦,同时P与V、P与M可以相互通信。

下面举个登录的例子:

代码语言:txt
复制
public class UserLoginActivity extends AppCompatActivity implements IUserLoginView {





    @BindView(R.id.et\_username)

    EditText etUsername;

    @BindView(R.id.et\_password)

    EditText etPassword;

    @BindView(R.id.bt\_login)

    Button btLogin;

    @BindView(R.id.bt\_clear)

    Button btClear;

    @BindView(R.id.progress)

    ProgressBar progress;

    @BindView(R.id.activity\_user\_login)

    RelativeLayout activityUserLogin;



    private UserLoginPresenter userLoginPresenter = new UserLoginPresenter(this);



    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity\_user\_login);

        ButterKnife.bind(this);

        setTitle("用户登录(MVP)");



        initListener();



    }



    private void initListener() {

        btLogin.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                userLoginPresenter.login();

            }

        });

        btClear.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                userLoginPresenter.clear();

            }

        });

    }



    @Override

    public String getUserName() {

        return etUsername.getText().toString().trim();

    }



    @Override

    public String getPassword() {

        return etPassword.getText().toString().trim();

    }



    @Override

    public void clearUserName() {

        etUsername.setText("");

    }



    @Override

    public void clearPassword() {

        etPassword.setText("");

    }



    @Override

    public void showLoading() {

        progress.setVisibility(View.VISIBLE);

    }



    @Override

    public void hindLoading() {

        progress.setVisibility(View.GONE);

    }



    @Override

    public void toMainActivity() {

        Toast.makeText(this, "login success , to MainActivity!", Toast.LENGTH\_SHORT).show();

    }



    @Override

    public void showFailedError() {

        Toast.makeText(this, "login failed!", Toast.LENGTH\_SHORT).show();

    }

}
代码语言:txt
复制
/\*\*

 \* Created by jingbin on 2016/11/3.

 \* Presenter是用作Model和View之间交互的桥梁,那么应该有什么方法呢?

 \* 其实也是主要看该功能有什么操作,比如本例,两个操作:login和clear。

 \*/



public class UserLoginPresenter {



    // view

    private IUserLoginView iUserLoginView;

    // model

    private UserBiz mUserBiz;

    private Handler mHandler = new Handler();



    public UserLoginPresenter(IUserLoginView iUserLoginView) {

        this.iUserLoginView = iUserLoginView;

        this.mUserBiz = new UserBiz();

    }



    public void login() {

        //view

        iUserLoginView.showLoading();

        // model

        mUserBiz.login(iUserLoginView.getUserName(), iUserLoginView.getPassword(), new OnLoginListener() {

            @Override

            public void loginSuccess(User user) {



                // 需要在UI线程中执行

                mHandler.post(new Runnable() {

                    @Override

                    public void run() {

                        iUserLoginView.toMainActivity();

                        iUserLoginView.hindLoading();

                    }

                });

            }



            @Override

            public void loginFailed() {

                // 需要在UI线程中执行

                mHandler.post(new Runnable() {

                    @Override

                    public void run() {

                        iUserLoginView.hindLoading();

                        iUserLoginView.showFailedError();

                    }

                });

            }

        });

    }



    public void clear() {

        iUserLoginView.clearUserName();

        iUserLoginView.clearPassword();

    }



}
代码语言:txt
复制
public interface IUserLoginView {



    // login说明了要有用户名、密码,那么对应两个方法:

    String getUserName();



    String getPassword();



    void clearUserName();



    void clearPassword();



    // 再者login是个耗时操作,我们需要给用户一个友好的提示,一般就是操作ProgressBar,所以再两个:

    void showLoading();



    void hindLoading();





    // login当然存在登录成功与失败的处理,我们主要看成功我们是跳转Activity,而失败可能是去给个提醒:

    void toMainActivity();



    void showFailedError();

}

用户点击登录,触发点击事件,然后通过P层userLoginPresenter,调用登录的方法login(),方法里面会通过Model层mUserBiz.login()去做一些数据请求操作的处理,然后得到相应的数据返回。这里看到Model层的数据处理操作放在P层里,是不与V层直接交互的。

然后M层得到数据后回调,P层根据相应的数据,显示不同的UI,如toMainActivity,showFailedError等,这样V层只会出现一些基本的显示逻辑的处理。

MVVM

MVVM 模式将 Presenter 改名为 ViewModel,基本上与 MVP 模式完全一致。

来自MVVM----MVC,MVP 和 MVVM 的图示
来自MVVM----MVC,MVP 和 MVVM 的图示

唯一的区别是,它采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然。

代码语言:txt
复制
/\*\*

 \* 简单的MVVM + data-binding案例:

 \* 以点击一下按钮然后年龄会+2 为例

 \*

 \* @author jingbin

 \*/

public class ChangeAgeActivity extends AppCompatActivity {



    private ChangeAgeViewModel viewModel;



    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        ActivityChangeAgeBinding binding = DataBindingUtil.setContentView(this, R.layout.activity\_change\_age);

        setTitle("MVVM + data-binding");



        viewModel = ViewModelProviders.of(this).get(ChangeAgeViewModel.class);

        binding.setViewModel(viewModel);

        binding.setButtonname("年龄+2");



        /\*\*监听年龄的变化\*/

        viewModel.desc.observe(this, new Observer<String>() {

            @Override

            public void onChanged(@Nullable String desc) {

                Log.e("desc", desc);

            }

        });



        binding.btAge.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                viewModel.change();

            }

        });



    }

}
代码语言:txt
复制
/\*\*

 \* @author jingbin

 \*/

public class ChangeAgeViewModel extends AndroidViewModel {



    final MutableLiveData<String> desc = new MutableLiveData<>();

    public final ObservableField<String> age = new ObservableField<>();



    public ChangeAgeViewModel(@NonNull Application application) {

        super(application);

        age.set(String.valueOf(23));

    }



    void change() {

        String value = age.get();

        if (!TextUtils.isEmpty(value)) {

            Integer integer = Integer.valueOf(value);

            // 改变age的值 布局里的值直接改变

            age.set(String.valueOf(integer + 2));



            desc.setValue("年龄改变:" + age.get());

        }

    }



}
代码语言:txt
复制
<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools">



    <!-- View + ViewModel-->

    <data>



        <variable

            name="viewModel"

            type="com.example.jingbin.projectstru.mvvm.ChangeAgeViewModel" />



        <variable

            name="buttonname"

            type="String" />



    </data>





    <LinearLayout

        android:layout\_width="match\_parent"

        android:layout\_height="match\_parent"

        android:orientation="vertical"

        android:padding="25dp">





        <TextView

            android:id="@+id/tv\_mvvm"

            android:layout\_width="wrap\_content"

            android:layout\_height="wrap\_content"

            android:text="年龄"

            android:textColor="@color/colorPrimary" />



        <TextView

            android:id="@+id/tv\_age"

            android:layout\_width="wrap\_content"

            android:layout\_height="wrap\_content"

            android:layout\_marginTop="10dp"

            android:layout\_marginBottom="10dp"

            android:text="@{viewModel.age}" />



        <Button

            android:id="@+id/bt\_age"

            android:layout\_width="wrap\_content"

            android:layout\_height="wrap\_content"

            android:text="@{buttonname}" />



    </LinearLayout>

</layout>

可以看出,MVVM比MVP少了对应View的接口文件,这样更简洁了,而且,改变ViewModel里的值,则xml文件对应的值也会对应改变。如果通过手动setText(),则ViewModel里的值也会得到改变。通过这一层关系,我们可以通过数据去操控View里的显示,所以才可以去除掉对应View的接口文件。

MVP-databinding

基本实现了MVC,MVP,MVVM后,我发现它们各自有各自的优缺点。

MVC:简单,单一页面可以实现。但是不利于复杂页面。

MVP:解耦,结构清晰。但文件较多,每一个页面基本要新建P层和V层的文件,同时还会有findViewById操作。

MVVM:解耦,结构相对清晰,文件相对MVP较少。但如果页面显示比较复杂,需要通过多个值去控制页面的显示,或者页面一个值的显示 要通过多种逻辑去处理得到结果,个人感觉还是不太适用。(其中的ViewModel与对应宿主的生命周期相同,从而内存泄漏问题比MVP处理较好这里先不做讨论)

**MVP-databinding**:

处理方式与MVP相同,只是使用了databinding的优势,databinding节省了类似findViewById和数据绑定的时间,从此代码里就没有findViewById和ButterKnife之类的代码了,而且也不会有通过多个值去控制页面的显示这样不好操作的情况了。当然文件还是会多一些。

代码语言:txt
复制
/\*\*

 \* MVP + data-binding

 \*

 \* @author jingbin

 \*/

public class MvpDataBindingActivity extends AppCompatActivity implements ChangeAgeView {



    private ActivityMvpDataBindingBinding binding;

    private ChangeAgePresenter presenter;



    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        binding = DataBindingUtil.setContentView(this, R.layout.activity\_mvp\_data\_binding);



        setTitle("MVP + data-binding");

        presenter = new ChangeAgePresenter(this);

        binding.setButtonname("年龄+2");



        binding.btAge.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                presenter.changeAge(binding.getUser());

            }

        });

    }



    @Override

    public void showContentView(UserBean user) {

        binding.setUser(user);

    }



    @Override

    protected void onDestroy() {

        super.onDestroy();

        presenter.clear();

    }

}
代码语言:txt
复制
/\*\*

 \* @author jingbin

 \* @date 2019/02/26

 \*/



public class ChangeAgePresenter {



    private ChangeAgeView changeInterface;

    private UserModel userModel;



    public ChangeAgePresenter(ChangeAgeView changeInterface) {

        this.changeInterface = changeInterface;

        // 初始化

        changeInterface.showContentView(new UserBean("小白", 23));

    }



    /\*\*

     \* 改变年龄

     \*/

    public void changeAge(UserBean myUser) {

        if (userModel == null) {

            userModel = new UserModel();

        }

        userModel.changeAge(myUser, 2, new UserModel.ChangeInterface() {

            @Override

            public void success(UserBean user) {

                changeInterface.showContentView(user);

            }

        });

    }



    public void clear() {

        userModel = null;

    }

}

参考资料

End

对应项目:**ProjectPatternStudy** 😁

此文仅个人总结,如有不当之处,请留言告知。

代码语言:txt
复制
projectstru

├─ MainActivity.java

├─ mvc

│    ├─ LoadDataActivity.java

│    ├─ bean

│    │    └─ Essay.java

│    └─ model

│           ├─ EssayModel.java

│           └─ MainModel.java

├─ mvp

│    ├─ UserLoginActivity.java

│    ├─ bean

│    │    └─ User.java

│    ├─ model

│    │    ├─ IUserBiz.java

│    │    ├─ OnLoginListener.java

│    │    └─ UserBiz.java

│    ├─ presente

│    │    └─ UserLoginPresenter.java

│    └─ view

│           └─ IUserLoginView.java

├─ mvpdatabindind

│    ├─ MvpDataBindingActivity.java

│    ├─ bean

│    │    └─ UserBean.java

│    ├─ model

│    │    └─ UserModel.java

│    ├─ presente

│    │    └─ ChangeAgePresenter.java

│    └─ view

│           └─ ChangeAgeView.java

└─ mvvm

       ├─ ChangeAgeActivity.java

       └─ ChangeAgeViewModel.java

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • MVC
  • MVP
  • MVVM
  • MVP-databinding
  • 参考资料
  • End
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档