編輯:關於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工程的下載:稍後會添加
緩存之 ACache
1.android緩存的介紹Android開發本質上就是手機和互聯網中的web服務器之間進行通信,就必然需要從服務端獲取數據,而反復通過網絡獲取數據是比較耗時的,特別是訪
Android Studio適當修改
在Eclipse完成上一個APP,決定轉到Studio2.0來。 這次主要進行的設置是 1、代碼區背景顏色的更改 2、注釋、代碼自動補全快捷鍵設定 3、工具欄自定
Android Listview點贊問題關於圖片重復問題
《最近做一個小功能遇到這麼一個問題,listview 與 baseadapter結合使用,關於點贊的的時候 圖片重復問題,比如:我在第1個item 點贊然後 心型換成了紅
Android 自定義Adapter
一、VC與模板概念的理解MVC本來是存在於Desktop程序中的,M是指數據模型,V是指用戶界面,C則是控制器。使用MVC的目的是將M和V的實現代碼分離,從而使同一個程序