十分钟上手MVC、MVP、MVVM

阅读 14
收藏 1
2019-12-05
原文链接:mp.weixin.qq.com

本文字数:2216字

预计阅读时间:15分钟

导读

关于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传递调用到Controller15        helloButton.setOnClickListener(new View.OnClickListener() {16            @Override17            public void onClick(View view) {18                // (2)Controller直接调用Model层19                new GreetingGeneratorModel("HelloWorld", greetingTextView).execute();20            }21        });22        // (1)View传递调用到Controller23        goodbyeButtonClicked.setOnClickListener(new View.OnClickListener() {24            @Override25            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    }1011    // Simulates computing and returns a random integer12    @Override13    protected Integer doInBackground(Void... params) {14        try {15            // Simulate computing16            Thread.sleep(2000);17        } catch (InterruptedException e) {18        }19        return (int) (Math.random() * 100);20    }2122    @Override23    protected void onPostExecute(Integer randomInt) {24        // (3)Model层调用View25        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);45    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            @Override16            public void onClick(View view) {17                // (1)调用Presenter层数据操作18                presenter.greetHello();19            }20        });21        goodbyeButtonClicked.setOnClickListener(new View.OnClickListener() {22            @Override23            public void onClick(View view) {24                // (1)调用Presenter层数据操作25                presenter.greetGoodbye();26            }27        });28    }2930    @Override31    protected void onDestroy() {32        super.onDestroy();33    }3435    @NonNull36    @Override37    public HelloWorldPresenter createPresenter() {38        return new HelloWorldPresenter();39    }4041    // 实现Mvp View 接口,具体对View的操作42    @Override43    public void showHello(String greetingText) {44        greetingTextView.setTextColor(Color.RED);45        greetingTextView.setText(greetingText);46    }4748    // 实现Mvp View 接口,具体对View的操作49    @Override50    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;1011    public GreetingGeneratorModel(String baseText, GreetingTaskListener listener) {12        this.baseText = baseText;13        this.listener = listener;14    }1516    // Simulates computing and returns a random integer17    @Override18    protected Integer doInBackground(Void... params) {19        try {20            // Simulate computing21            Thread.sleep(2000);22        } catch (InterruptedException e) {23        }24        return (int) (Math.random() * 100);25    }2627    @Override28    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    }1112    public void greetHello() {13        cancelGreetingTaskIfRunning();14        // 实现Model层的回调监听15        greetingTask = new GreetingGeneratorModel("HelloWorld", new GreetingGeneratorModel.GreetingTaskListener() {16            @Override17            public void onGreetingGenerated(String greetingText) {18                // (4)调用View层更新View19                if (isViewAttached())20                    getView().showHello(greetingText);21            }22        });23        // (2)调用Model层获取数据24        greetingTask.execute();25    }2627    public void greetGoodbye() {28        cancelGreetingTaskIfRunning();29        // 实现Model层的回调监听30        greetingTask = new GreetingGeneratorModel("Goodbye", new GreetingGeneratorModel.GreetingTaskListener() {31            public void onGreetingGenerated(String greetingText) {32                // (4)调用View层更新View33                if (isViewAttached())34                    getView().showGoodbye(greetingText);35            }36        });37        // (2)调用Model层获取数据38        greetingTask.execute();39    }4041    // Called when Activity gets destroyed, so cancel running background task42    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 = true4    }

然后对布局文件作出一定调整:根标签是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        <variable10            name="greetingGeneratorObj"11            type="com.example.architecturalpatterndemo.mvvm.GreetingGeneratorObj" />12    </data>1314    <LinearLayout15        android:layout_width="match_parent"16        android:layout_height="match_parent"17        android:orientation="vertical">1819        <TextView20            android:id="@+id/greetingTextView"21            android:layout_width="wrap_content"22            android:layout_height="wrap_content"23            android:text="@{greetingGeneratorObj.baseText}" />2425        <Button26            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" />3132        <Button33            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>39</layout>

在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    }1415}
 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    }1011    // Simulates computing and returns a random integer12    @Override13    protected Integer doInBackground(Void... params) {14        try {15            // Simulate computing16            Thread.sleep(2000);17        } catch (InterruptedException e) {18        }1920        return (int) (Math.random() * 100);21    }2223    @Override24    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    @Bindable10    public String getBaseText() {11        return baseText;12    }1314    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    }1213    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

也许你还想看

(▼点击文章标题或封面查看)

基于Android输入法开发,制作一个微信斗图APP

2019-08-15

干货!混合式架构App当中的通信安全

2019-10-17

Android消息机制Handler原理解析

2019-09-26

加入搜狐技术作者天团

千元稿费等你来!

戳这里!☛

    

      欢迎留言讨论

     ▼▼▼

     写留言

评论