編輯:關於Android編程
工欲善其事,必先利其器,工作一段時間後,對於以上十個字的感觸是最深的。剛參加工作的時候,並沒有對於要做的事情有著自己的理解,經常是上面分配了工作,自己就乖乖地跑去做,也不會思考什麼。後來漸漸地發現,做著做著就會出現各種問題,對需求理解的不到位,對問題理解的不深入,會造成各種各樣的問題,新功能難以實現,程序BUG非常難以定位等等。要想成為一名優秀的軟件開發者,在做每一件事情之前,都應該深思熟慮,將可能出現的情況盡可能地考慮進去,這樣前期雖然感覺工作變慢了,但是後來的工作會變得輕松且得心應手。好了,廢話扯了很多,接下來就是正題,關於mvc、mvp、mvvm在android中的應用實踐分析,本人水平不高,有錯誤的地方歡迎指正,不勝感激。
mvc、mvp與mvvm都是軟件編程當中用以解決界面與功能關系的常見架構,好的架構自然是需要符合設計原則的,而這三個架構的目的也是在於將軟件當中的界面(View)與業務邏輯(Model)進行解耦,而另外的部分,就是連接M與V的橋梁,這裡多說一句,我將Model稱為業務邏輯的原因:Model翻譯過來是模型,很多其他文章當中對Model的解釋是數據,解釋的原因是程序的根本是數據,我覺得說的很對,就好像世界的本質是物質一樣,只不過這裡我將Model解釋為業務邏輯是因為我覺得數據給人一種“死”的感覺,而且有一種誤導向數據庫的感覺(雖然確實會包含),所以我覺得軟件的根本是業務,軟件開發者的工作在於將業務轉換為邏輯代碼,從業務邏輯的詞語這樣感覺比較“活”。
那好的,接下來首先要分析的就是Android當中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)。
首先是被動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裡面別寫業務邏輯代碼。
好吧,被動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的情況下,鳥兒每一次的數量變化(對應到代碼中是每一秒變動一次的鳥的數量變化)我們都可以通過望遠鏡來看到,而不是我們每一次拿起望遠鏡的時候,這樣,每次鳥兒飛起降落的時候,就必須要通知我們,數量變化了,並且還必須要按照我們規定的方式來告訴我們,這樣鳥兒就不自由了,一旦我們的望遠鏡升級換代,鳥兒就需要按照新的方式來告知我們,這就造成了我們最不想看到的開閉原則的破壞。
MVC作為一種經典的設計模式,在多種場合與環境下被廣泛使用,並已經出現了多種變種。本文提到的被動MVC在WEB開發中被有效地利用,因為web開發中Model的更新較難實時的通知到對應的具體View。而主動MVC在桌面應用也就是Native應用中被廣泛使用,是因為桌面應用中數據的更新比較容易與界面進行綁定。
總的來說,MVC作為一種經久不衰的設計模式,有著非常大的實用價值,被動MVC架構完全符合設計思想當中高內聚低耦合的理念,將M、V、C三者的智能劃分也很明確,相互之間的耦合性也很小。而主動MVC架構可以實時快速的使界面得到後台的數據,對M、V、C三者的作用也有著劃分,但是耦合度相對被動MVC更大,不過主動MVC的開發速度快,模塊劃分簡單易懂也是它的優勢。
關於本篇中提到的app工程的下載:稍後會添加
1.android緩存的介紹Android開發本質上就是手機和互聯網中的web服務器之間進行通信,就必然需要從服務端獲取數據,而反復通過網絡獲取數據是比較耗時的,特別是訪
在Eclipse完成上一個APP,決定轉到Studio2.0來。 這次主要進行的設置是 1、代碼區背景顏色的更改 2、注釋、代碼自動補全快捷鍵設定 3、工具欄自定
《最近做一個小功能遇到這麼一個問題,listview 與 baseadapter結合使用,關於點贊的的時候 圖片重復問題,比如:我在第1個item 點贊然後 心型換成了紅
一、VC與模板概念的理解MVC本來是存在於Desktop程序中的,M是指數據模型,V是指用戶界面,C則是控制器。使用MVC的目的是將M和V的實現代碼分離,從而使同一個程序