十分钟上手MVC、MVP、MVVM
导读
关于MVC、MVP、MVVM的具体概念和优缺点网上有很多文章讲解,这里不再赘述。
但是对于一个没有实战过这三种架构模式的同学们,当你辛辛苦苦花了几个小时读了关于这三种模式的文章,看了一些彼此分离没有对比性的示例代码。你们真的会很快上手这三种架构模式的写法吗?
本文通过一个三合一demo,关键点代码进行注释,十分钟带你入门。让你分分钟看懂三种模式的区别,快速上手三种写法。
这是一个点击按钮,模拟耗时任务获取数据,然后设置Textview的demo。对,就是这么简单,方便大家入门。
01
MVC
对于MVC模式,本demo只有两个类:MvcMainActivity和GreetingGeneratorModel。
在MVC模式中,Activity就是Controller,同时也是View的一部分,它会调用Model层进行数据获取。Model层,封装各种数据来源,和View层是直接通信的,它会调用View层里的更新View方法。
1// Activity即是Controller,同时也是View的一部分
2public class MvcMainActivity extends AppCompatActivity {
3 TextView greetingTextView;
4 Button helloButton;
5 Button goodbyeButtonClicked;
6
7 @Override
8 protected void onCreate(Bundle savedInstanceState) {
9 super.onCreate(savedInstanceState);
10 setContentView(R.layout.activity_mvcmain);
11 greetingTextView = findViewById(R.id.greetingTextView);
12 helloButton = findViewById(R.id.helloButton);
13 goodbyeButtonClicked = findViewById(R.id.goodbyeButtonClicked);
14 // (1)View传递调用到Controller
15 helloButton.setOnClickListener(new View.OnClickListener() {
16 @Override
17 public void onClick(View view) {
18 // (2)Controller直接调用Model层
19 new GreetingGeneratorModel("HelloWorld", greetingTextView).execute();
20 }
21 });
22 // (1)View传递调用到Controller
23 goodbyeButtonClicked.setOnClickListener(new View.OnClickListener() {
24 @Override
25 public void onClick(View view) {
26 // (2)Controller直接调用Model层
27 new GreetingGeneratorModel("GoodBye", greetingTextView).execute();
28 }
29 });
30 }
31}
1// Model层,封装各种数据来源,和View层是直接通信的
2public class GreetingGeneratorModel extends AsyncTask<Void, Void, Integer> {
3 private String baseText;
4 private TextView greetingTextView;
5
6 public GreetingGeneratorModel(String baseText, TextView greetingTextView) {
7 this.baseText = baseText;
8 this.greetingTextView = greetingTextView;
9 }
10
11 // Simulates computing and returns a random integer
12 @Override
13 protected Integer doInBackground(Void... params) {
14 try {
15 // Simulate computing
16 Thread.sleep(2000);
17 } catch (InterruptedException e) {
18 }
19 return (int) (Math.random() * 100);
20 }
21
22 @Override
23 protected void onPostExecute(Integer randomInt) {
24 // (3)Model层调用View
25 if ("HelloWorld".equals(baseText)) {
26 greetingTextView.setTextColor(Color.RED);
27 } else {
28 greetingTextView.setTextColor(Color.BLUE);
29 }
30 greetingTextView.setText(baseText + randomInt);
31 }
32}
好啦。MVC模式讲完啦,就是这么简单,这也是大家初学Android时最常见的写法。
02
MVP
在实现MVP模式写法时,我们借助了Mosby这个开源库。通过Mosby我们可以很轻松地实现MVP的写法。
对于MVP模式,本demo有四个类:除了有MvpMainActivity和GreetingGeneratorModel外,新增了HelloWorldView和HelloWorldPresenter。
可以看到,相比于MVC模式,首先,HelloWorldView定义了操作View的接口。
1// Mvp View 接口,定义对View的操作接口
2public interface HelloWorldView extends MvpView {
3 void showHello(String greetingText);
4
5 void showGoodbye(String greetingText);
6}
MvpMainActivity一方面要调用HelloWorldPresenter层进行数据操作;另一方面要实现HelloWorldView中具体操作View的接口。
1// View层,视图层
2public class MvpMainActivity extends MvpActivity<HelloWorldView, HelloWorldPresenter> implements HelloWorldView {
3 TextView greetingTextView;
4 Button helloButton;
5 Button goodbyeButtonClicked;
6
7 @Override
8 protected void onCreate(Bundle savedInstanceState) {
9 super.onCreate(savedInstanceState);
10 setContentView(R.layout.activity_mvpmain);
11 greetingTextView = findViewById(R.id.greetingTextView);
12 helloButton = findViewById(R.id.helloButton);
13 goodbyeButtonClicked = findViewById(R.id.goodbyeButtonClicked);
14 helloButton.setOnClickListener(new View.OnClickListener() {
15 @Override
16 public void onClick(View view) {
17 // (1)调用Presenter层数据操作
18 presenter.greetHello();
19 }
20 });
21 goodbyeButtonClicked.setOnClickListener(new View.OnClickListener() {
22 @Override
23 public void onClick(View view) {
24 // (1)调用Presenter层数据操作
25 presenter.greetGoodbye();
26 }
27 });
28 }
29
30 @Override
31 protected void onDestroy() {
32 super.onDestroy();
33 }
34
35 @NonNull
36 @Override
37 public HelloWorldPresenter createPresenter() {
38 return new HelloWorldPresenter();
39 }
40
41 // 实现Mvp View 接口,具体对View的操作
42 @Override
43 public void showHello(String greetingText) {
44 greetingTextView.setTextColor(Color.RED);
45 greetingTextView.setText(greetingText);
46 }
47
48 // 实现Mvp View 接口,具体对View的操作
49 @Override
50 public void showGoodbye(String greetingText) {
51 greetingTextView.setTextColor(Color.BLUE);
52 greetingTextView.setText(greetingText);
53 }
54}
GreetingGeneratorModel仍旧提供各种数据来源,但是要定义回调监听,对Presenter提供接口。
1// Model层,封装各种数据来源,对Prestener层提供接口
2public class GreetingGeneratorModel extends AsyncTask<Void, Void, Integer> {
3 // 异步任务中要定义回调监听
4 public interface GreetingTaskListener {
5 public void onGreetingGenerated(String greetingText);
6 }
7
8 private String baseText;
9 private GreetingTaskListener listener;
10
11 public GreetingGeneratorModel(String baseText, GreetingTaskListener listener) {
12 this.baseText = baseText;
13 this.listener = listener;
14 }
15
16 // Simulates computing and returns a random integer
17 @Override
18 protected Integer doInBackground(Void... params) {
19 try {
20 // Simulate computing
21 Thread.sleep(2000);
22 } catch (InterruptedException e) {
23 }
24 return (int) (Math.random() * 100);
25 }
26
27 @Override
28 protected void onPostExecute(Integer randomInt) {
29 // (3)执行回调关联到Presenter层
30 listener.onGreetingGenerated(baseText + " " + randomInt);
31 }
32}
HelloWorldPresenter既要调用Model层进行数据获取,也要实现GreetingGeneratorModel层的回调监听操作View层更新View。
1// Presenter层,逻辑控制层
2public class HelloWorldPresenter extends MvpBasePresenter<HelloWorldView> {
3 // Greeting Task is "business logic"
4 private GreetingGeneratorModel greetingTask;
5
6 private void cancelGreetingTaskIfRunning() {
7 if (greetingTask != null) {
8 greetingTask.cancel(true);
9 }
10 }
11
12 public void greetHello() {
13 cancelGreetingTaskIfRunning();
14 // 实现Model层的回调监听
15 greetingTask = new GreetingGeneratorModel("HelloWorld", new GreetingGeneratorModel.GreetingTaskListener() {
16 @Override
17 public void onGreetingGenerated(String greetingText) {
18 // (4)调用View层更新View
19 if (isViewAttached())
20 getView().showHello(greetingText);
21 }
22 });
23 // (2)调用Model层获取数据
24 greetingTask.execute();
25 }
26
27 public void greetGoodbye() {
28 cancelGreetingTaskIfRunning();
29 // 实现Model层的回调监听
30 greetingTask = new GreetingGeneratorModel("Goodbye", new GreetingGeneratorModel.GreetingTaskListener() {
31 public void onGreetingGenerated(String greetingText) {
32 // (4)调用View层更新View
33 if (isViewAttached())
34 getView().showGoodbye(greetingText);
35 }
36 });
37 // (2)调用Model层获取数据
38 greetingTask.execute();
39 }
40
41 // Called when Activity gets destroyed, so cancel running background task
42 public void detachView(boolean retainPresenterInstance) {
43 super.detachView(retainPresenterInstance);
44 if (!retainPresenterInstance) {
45 cancelGreetingTaskIfRunning();
46 }
47 }
48}
03
MVVM
要使用MVVM模式,首先你需要在app下的buld.gradle中添加DataBinding框架配置:
1 // DataBinding配置
2 dataBinding {
3 enabled = true
4 }
然后对布局文件作出一定调整:根标签是layout,根标签之下增加了一个data标签,它包含两个variable标签。
对于原先的Button按钮通过@{handlers.onClickHello}进行事件绑定前置操作。对于原先的Textview通过@{greetingGeneratorObj.baseText} 进行数据绑定前置操作。
1<layout xmlns:android="http://schemas.android.com/apk/res/android">
2
3 <data>
4
5 <variable
6 name="handlers"
7 type="com.example.architecturalpatterndemo.mvvm.MyHandlers" />
8
9 <variable
10 name="greetingGeneratorObj"
11 type="com.example.architecturalpatterndemo.mvvm.GreetingGeneratorObj" />
12 data>
13
14 <LinearLayout
15 android:layout_width="match_parent"
16 android:layout_height="match_parent"
17 android:orientation="vertical">
18
19 <TextView
20 android:id="@+id/greetingTextView"
21 android:layout_width="wrap_content"
22 android:layout_height="wrap_content"
23 android:text="@{greetingGeneratorObj.baseText}" />
24
25 <Button
26 android:id="@+id/helloButton"
27 android:layout_width="wrap_content"
28 android:layout_height="wrap_content"
29 android:onClick="@{handlers.onClickHello}"
30 android:text="hello" />
31
32 <Button
33 android:id="@+id/goodbyeButtonClicked"
34 android:layout_width="wrap_content"
35 android:layout_height="wrap_content"
36 android:onClick="@{handlers.onClickGoodbye}"
37 android:text="Good bye" />
38 LinearLayout>
39layout>
在MVVM模式中,本demo有四个类:除了MvvmMainActivity和GreetingGeneratorModel外,新增了GreetingGeneratorObj数据对象类和MyHandlers绑定方法类。
在MvvmMainActivity中,通过ActivityMvvmmainBinding对象的setHandlers方法进行事件绑定,可以看到,setOnClickListener代码消失了;
通过ActivityMvvmmainBinding对象的setGreetingGeneratorObj方法进行数据绑定。具体双向数据绑定是怎么执行的,都由DataBinding框架来完成。
1public class MvvmMainActivity extends AppCompatActivity {
2
3 @Override
4 protected void onCreate(Bundle savedInstanceState) {
5 super.onCreate(savedInstanceState);
6 ActivityMvvmmainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_mvvmmain);
7 GreetingGeneratorObj greetingGeneratorObj = new GreetingGeneratorObj("");
8 // (1)按套路写好数据绑定,具体怎么实现双向数据绑定,都由DataBinding框架来完成
9 // 数据绑定
10 binding.setGreetingGeneratorObj(greetingGeneratorObj);
11 // 事件绑定
12 binding.setHandlers(new MyHandlers(greetingGeneratorObj));
13 }
14
15}
1// Model层,封装各种数据来源
2public class GreetingGeneratorModel extends AsyncTask<Void, Void, Integer> {
3 private String baseText;
4 private GreetingGeneratorObj greetingGeneratorObj;
5
6 public GreetingGeneratorModel(String baseText, GreetingGeneratorObj greetingGeneratorObj) {
7 this.baseText = baseText;
8 this.greetingGeneratorObj = greetingGeneratorObj;
9 }
10
11 // Simulates computing and returns a random integer
12 @Override
13 protected Integer doInBackground(Void... params) {
14 try {
15 // Simulate computing
16 Thread.sleep(2000);
17 } catch (InterruptedException e) {
18 }
19
20 return (int) (Math.random() * 100);
21 }
22
23 @Override
24 protected void onPostExecute(Integer randomInt) {
25 // (3)ViewModel层调用ViewModel层
26 greetingGeneratorObj.setBaseText(baseText + randomInt);
27 }
28}
数据对象的set方法需要增加notifyPropertyChanged方法。
1// 数据对象类
2public class GreetingGeneratorObj extends BaseObservable {
3 public static String baseText;
4
5 public GreetingGeneratorObj(String baseText) {
6 this.baseText = baseText;
7 }
8
9 @Bindable
10 public String getBaseText() {
11 return baseText;
12 }
13
14 public void setBaseText(String baseText) {
15 GreetingGeneratorObj.baseText = baseText;
16 notifyPropertyChanged(BR.baseText);
17 }
18}
MyHandlers中实现操作Model的方法,这些方法与xml中布局文件是对应的,会被DataBinding框架自动调用。
1public class MyHandlers {
2 private GreetingGeneratorObj greetingGeneratorObj;
3
4 public MyHandlers(GreetingGeneratorObj greetingGeneratorObj) {
5 this.greetingGeneratorObj = greetingGeneratorObj;
6 }
7
8 public void onClickHello(View view) {
9 // (2)ViewModel层调用Model层
10 new GreetingGeneratorModel("HelloWorld", greetingGeneratorObj).execute();
11 }
12
13 public void onClickGoodbye(View view) {
14 // (2)ViewModel层调用Model层
15 new GreetingGeneratorModel("GoodBye", greetingGeneratorObj).execute();
16 }
17}
04
demo地址
以上就是本demo的全部啦。通过关系图和关键代码注释,使用三种模式实现同样的小功能,这样能很清楚地对比。十分钟上手三种模式的写法!具体demo地址如下:
https://github.com/youjinjin/ArchitecturalPatternDemo