Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 基於Android的mvc、mvp以及mvvm架構分析(上)

基於Android的mvc、mvp以及mvvm架構分析(上)

編輯:關於Android編程

前言:

工欲善其事,必先利其器,工作一段時間後,對於以上十個字的感觸是最深的。剛參加工作的時候,並沒有對於要做的事情有著自己的理解,經常是上面分配了工作,自己就乖乖地跑去做,也不會思考什麼。後來漸漸地發現,做著做著就會出現各種問題,對需求理解的不到位,對問題理解的不深入,會造成各種各樣的問題,新功能難以實現,程序BUG非常難以定位等等。要想成為一名優秀的軟件開發者,在做每一件事情之前,都應該深思熟慮,將可能出現的情況盡可能地考慮進去,這樣前期雖然感覺工作變慢了,但是後來的工作會變得輕松且得心應手。好了,廢話扯了很多,接下來就是正題,關於mvc、mvp、mvvm在android中的應用實踐分析,本人水平不高,有錯誤的地方歡迎指正,不勝感激。

mvc、mvp與mvvm都是軟件編程當中用以解決界面與功能關系的常見架構,好的架構自然是需要符合設計原則的,而這三個架構的目的也是在於將軟件當中的界面(View)與業務邏輯(Model)進行解耦,而另外的部分,就是連接M與V的橋梁,這裡多說一句,我將Model稱為業務邏輯的原因:Model翻譯過來是模型,很多其他文章當中對Model的解釋是數據,解釋的原因是程序的根本是數據,我覺得說的很對,就好像世界的本質是物質一樣,只不過這裡我將Model解釋為業務邏輯是因為我覺得數據給人一種“死”的感覺,而且有一種誤導向數據庫的感覺(雖然確實會包含),所以我覺得軟件的根本是業務,軟件開發者的工作在於將業務轉換為邏輯代碼,從業務邏輯的詞語這樣感覺比較“活”。

那好的,接下來首先要分析的就是Android當中MVC的具體實現與應用。

一、MVC在Android當中的應用:

1、MVC的介紹:

MVC全名是Model ViewController,是模型(model)-視圖(view)-控制器(controller)的縮寫,其中Model代表業務邏輯,Controller是View與Model交互的橋梁,而View就是我們用戶可以直接看見和操作的界面,而MVC又分為主動和被動兩種,其轉換圖如下:

\

圖1 被動MVC

\

 

圖2 主動MVC

可以看到,主動MVC與被動MVC的結構是基本一致的,最主要的區別在於M與V之間是否可以直接溝通,所以此處的主動以及被動的主語是Model,指的是Model能否主動地與View之間進行通信。

而在Android當中,MVC的實現可以說是非常的簡單,Android的V就是我們的各種Layout.xml文件,而M是我們進行具體業務邏輯實現的部分(比如計算器的後台計算方式),而C就是他們的橋梁(比如Activity)。

2、被動MVC

首先是被動MVC,被動MVC的宗旨是M與V互相不知道,完全通過C來進行數據的交互,C是整個系統中的橋梁,起到非常重要的中介作用。

這裡打算用一個例子來描述MVC,我們來設想這樣一個場景,在一片蒼茫的草地上,有著一群鳥,鳥兒總是向往天空,但卻會時而落地,我們騎上奔馳的駿馬在草地上紅塵作伴潇潇灑灑,拿著一種高科技的望遠鏡想要知道在某一時刻落地的鳥兒有幾只。

好的,場景描述完畢,現在可以看到,鳥兒落地起飛的具體情況可以認為是Model,人通過“高科技望遠鏡”來看鳥兒的數量,可以認為高科技望遠鏡裡看到的就是View,而看過去的過程就是Controller。

這裡用一個實際的App來仿真之前提到的場景。

首先是activity_main.xml,也就是View的部分:

xml version="1.0"encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">


<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="@string/tv_birds"
android:id="@+id/tv_birds"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="143dp"/>

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/btn_see"
android:id="@+id/btn_see"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"/>
RelativeLayout>

比較的簡單,就一個用來顯示鳥的數量的TextView和一個用來看鳥數量的Button

 

然後是MainActivity,也就是Controller的部分:

package com.brick.mvctest;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

 TextView tv_birds = null;
 Button btn_see = null;
 BirdsModelInterface birdsModel = null;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 tv_birds = (TextView)findViewById(R.id.tv_birds);
 btn_see = (Button)findViewById(R.id.btn_see);

 btn_see.setOnClickListener(this);

 birdsModel = BirdsModel.getInstance();
 }

 protected void onResume(){
 super.onResume();
 }

 @Override
 public void onClick(View v){
 switch (v.getId()){
 case R.id.btn_see:
 int num = birdsModel.getBirds();
 set_Tv_Birds(num);
 break;
 default:
 break;
 }
 }

 private void set_Tv_Birds(int num){
 tv_birds.setText(num + "");
 }
}

MainActivity的代碼也比較簡單,主要就是通過onClick方法來把按鈕的點擊事件獲取,然後從BirdsModel當中來獲取鳥的數量,再通過set_Tv_Birds來返回到View來顯示,實現了橋梁的作用。

 

然後是Model部分,一個簡單的類,來讓鳥兒一秒鐘運動一次,改變鳥的數量:

 

基類:

package com.brick.mvctest;

/**
* Created by brick on 2017/1/6.
*/
public interface BirdsModelInterface {
 public int getBirds();
}

 

實現:

package com.brick.mvctest;

import java.util.Random;

/**
* Created by brick on 2017/1/5.
*/
public class BirdsModel implements BirdsModelInterface {
 private int birds = 0;
 private final int MAX_BIRDS = 1000;

 private static BirdsModel instance = null;
 private static Object birds_Lock = new Object();


 private BirdsModel(){
 new Thread(new Runnable() {
 @Override
 public void run() {
 while(true){
 setBirds(new Random().nextInt(MAX_BIRDS));
 try {
 Thread.sleep(1000);
 }catch (Exception e){
 e.printStackTrace();
 }
 }
 }
 }).start();
 }

 public static BirdsModel getInstance(){
 if(instance == null) {
 synchronized (BirdsModel.class) {
 if(instance == null){
 instance = new BirdsModel();
 }
 }
 }
 return instance;
 }

 private void setBirds(int num){
 synchronized (birds_Lock) {
 birds = num;
 }
 }

 public synchronized int getBirds(){
 synchronized (birds_Lock) {
 return birds;
 }
 }
}

 

這裡的Model就是BirdsModel,鳥每分鐘會自行運動一次,並修改當前鳥的數量。然後在MainActivity當中實際上持有的是BirdsModelInterface,也可以實現一定程度的M與C的解耦,當遇到需求修改或者說Model變化的時候,可以用相同的接口,不同的Model實現。

 

至此,一個簡單的被動MVC框架就算搭建完成了,感覺非常簡單,其實就算不了解MVC的框架,稍微重視代碼規范的程序員也至少會把代碼寫成這樣,主要是因為在Android當中,V和C已經簡單地劃分好了,所以在被動MVC當中,只需要注意將M的部分抽離出來,就能夠實現一個合理的MVC框架。

簡而言之一句話:Activity裡面別寫業務邏輯代碼。

 

3、主動MVC

好吧,被動MVC看來很簡單也很無趣,也就是一句話解決,把Model要做的事情丟給Model做,別讓Activity裡面太多代碼。

接下來就是主動MVC了,主動MVC與被動MVC最大的不同就在於M與V是有直接的通信的,這樣一來M的變化會直接地影響到V,通常這裡通過觀察者模式來實現。同樣的,還是在那片蒼茫的草地上,有著那一群可愛的鳥兒,不過這一次紅塵作伴的我們想要實時地知道鳥兒落地的情況,而不是只有我們拿起望遠鏡時才能看到。好的,要做到這一點,只需要簡單地修改一下之前的代碼。

 

首先還是View的部分,activity_main.xml並沒有修改:

xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent">


 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:textAppearance="?android:attr/textAppearanceLarge"
 android:text="@string/tv_birds"
 android:id="@+id/tv_birds"
 android:layout_alignParentTop="true"
 android:layout_centerHorizontal="true"
 android:layout_marginTop="143dp" />

 <Button
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="@string/btn_see"
 android:id="@+id/btn_see"
 android:layout_centerVertical="true"
 android:layout_centerHorizontal="true" />
RelativeLayout>

依然是一個按鈕,一個文本視圖

 

然後是Controller的部分:

 

Observer的基類:

package com.brick.mvctest;

/**
* Created by brick on 2017/1/6.
*/
public interface BirdsModelObserver {
 public void update(int birds);
}

 

實現:

package com.brick.mvctest;

import android.os.Bundle;
import android.os.Looper;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.util.logging.Handler;

public class MainActivity extends AppCompatActivity implements View.OnClickListener,BirdsModelObserver{

 TextView tv_birds = null;
 Button btn_see = null;
 BirdsModelInterface birdsModel = null;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 tv_birds = (TextView)findViewById(R.id.tv_birds);
 btn_see = (Button)findViewById(R.id.btn_see);

 btn_see.setOnClickListener(this);

 birdsModel = BirdsModel.getInstance();
 ((BirdsModel)birdsModel).registerObserver(this);
 }

 protected void onResume(){
 super.onResume();

 }

 @Override
 public void onClick(View v){
 switch (v.getId()){
 case R.id.btn_see:
 int num = birdsModel.getBirds();
 set_Tv_Birds(num);
 break;
 default:
 break;
 }
 }

 private void set_Tv_Birds(int num){
 tv_birds.setText(num + "");
 }

 @Override
 public void update(final int birds){
 android.os.Handler handler = new android.os.Handler(Looper.getMainLooper());
 handler.post(new Runnable() {
 @Override
 public void run() {
 set_Tv_Birds(birds);
 }
 });
 }
}

代碼的改動主要是將Activity繼承了BirdsModelObserver成為觀察者,然後實現了update的方法並將自己注冊到Model當中。

 

接下來是Model的部分:

Model的基類:

package com.brick.mvctest;

/**
* Created by brick on 2017/1/6.
*/
public interface BirdsModelInterface{
 public int getBirds();
}

Observable的基類:

package com.brick.mvctest;

/**
* Created by brick on 2017/1/6.
*/
public interface Observable {
 public void registerObserver(BirdsModelObserver birdsModelObserver);
 public void removeObserver(BirdsModelObserver birdsModelObserver);
 public void notifyObservers();
}








package com.brick.mvctest;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
* Created by brick on 2017/1/5.
*/
public class BirdsModel implements BirdsModelInterface, Observable{
 List birdsModelObservers = null;
 private int birds = 0;
 private final int MAX_BIRDS = 1000;

 private static BirdsModel instance = null;
 private static Object birds_Lock = new Object();


 private BirdsModel(){
 birdsModelObservers = new ArrayList<>();
 new Thread(new Runnable() {
 @Override
 public void run() {
 while(true){
 setBirds(new Random().nextInt(MAX_BIRDS));
 try {
 Thread.sleep(1000);
 }catch (Exception e){
 e.printStackTrace();
 }
 }
 }
 }).start();
 }

 public static BirdsModel getInstance(){
 if(instance == null) {
 synchronized (BirdsModel.class) {
 if(instance == null){
 instance = new BirdsModel();
 }
 }
 }
 return instance;
 }

 private void setBirds(int num){
 synchronized (birds_Lock) {
 birds = num;
 notifyObservers();
 }
 }

 public synchronized int getBirds(){
 synchronized (birds_Lock) {
 return birds;
 }
 }

 @Override
 public void registerObserver(BirdsModelObserver birdsModelObserver){
 birdsModelObservers.add(birdsModelObserver);
 }

 public void removeObserver(BirdsModelObserver birdsModelObserver){
 try{
 birdsModelObservers.remove(birdsModelObserver);
 } catch (Exception e){
 e.printStackTrace();
 }
 }

 public void notifyObservers(){
 for(BirdsModelObserver birdsModelObserver : birdsModelObservers){
 birdsModelObserver.update(getBirds());
 }
 }
}

這裡主要也就是將Model繼承了Observable,使之成為可供觀察的主題,在Activity當中進行注冊。

這樣一來,草原上的鳥兒就可以實時的被關注到,並將每一次的變化發送到View來進行顯示。

簡單地說一下,似乎看來主動MVC與被動MVC在這裡沒有很明顯的區別,但是他們之間其實是有很根本的區別的,在被動MVC當中,V可以說完全與M沒有任何的聯系,V只是與C進行通信,當按鈕被點擊時,V僅僅是告訴C,按鈕被點擊了,我需要一個int數據,然後後面的事情全權交給了C來處理。而在主動MVC當中,M與V有著直接的聯系,V注冊為了M的觀察者,M必須要知道V上有什麼控件,需要什麼樣的信息,而造成了M與V的耦合,這其實是不符合設計原則的。

結合之前的例子,把被動MVC和主動MVC進行一次解釋。

在被動MVC的情況下,紅塵作伴的我們潇潇灑灑地拿起高科技望遠鏡(按下獲取鳥數量的按鈕),我們想要的只是能看到東西,究竟望遠鏡怎麼合成影像,怎麼調整光線來獲取鳥的數量都不由我們關心,而鳥在飛起降落的過程也是完全自由的,不會知道有人在看它們,這是被動MVC。

在主動MVC的情況下,鳥兒每一次的數量變化(對應到代碼中是每一秒變動一次的鳥的數量變化)我們都可以通過望遠鏡來看到,而不是我們每一次拿起望遠鏡的時候,這樣,每次鳥兒飛起降落的時候,就必須要通知我們,數量變化了,並且還必須要按照我們規定的方式來告訴我們,這樣鳥兒就不自由了,一旦我們的望遠鏡升級換代,鳥兒就需要按照新的方式來告知我們,這就造成了我們最不想看到的開閉原則的破壞。

 

4、總結:

MVC作為一種經典的設計模式,在多種場合與環境下被廣泛使用,並已經出現了多種變種。本文提到的被動MVC在WEB開發中被有效地利用,因為web開發中Model的更新較難實時的通知到對應的具體View。而主動MVC在桌面應用也就是Native應用中被廣泛使用,是因為桌面應用中數據的更新比較容易與界面進行綁定。

總的來說,MVC作為一種經久不衰的設計模式,有著非常大的實用價值,被動MVC架構完全符合設計思想當中高內聚低耦合的理念,將M、V、C三者的智能劃分也很明確,相互之間的耦合性也很小。而主動MVC架構可以實時快速的使界面得到後台的數據,對M、V、C三者的作用也有著劃分,但是耦合度相對被動MVC更大,不過主動MVC的開發速度快,模塊劃分簡單易懂也是它的優勢。

 

關於本篇中提到的app工程的下載:稍後會添加

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved