Android App的設計架構:MVC,MVP,MVVM與架構經驗談

原文出處:https://www.tianmaying.com/tutorial/AndroidMVC

和MVC框架模式一樣,Model模型處理數據代碼不變在Android的App開發中,很多人經常會頭疼於App的架構如何設計:

  • 我的App需要應用這些設計架構嗎?

  • MVC,MVP等架構講的是什麼?區別是什麼?

本文就來帶你分析一下這幾個架構的特性,優缺點,以及App架構設計中應該注意的問題。

1.架構設計的目的

通過設計使程序模塊化,做到模塊內部的高聚合和模塊之間的低耦合。這樣做的好處是使得程序在開發的過程中,開發人員只需要專注於一點,提高程序開發的效率,並且更容易進行後續的測試以及定位問題。但設計不能違背目的,對於不同量級的工程,具體架構的實現方式必然是不同的,切忌犯爲了設計而設計,爲了架構而架構的毛病。

舉個簡單的例子:

一個Android App如果只有3個Java文件,那只需要做點模塊和層次的劃分就可以,引入框架或者架構反而提高了工作量,降低了生產力;

但如果當前開發的App最終代碼量在10W行以上,本地需要進行複雜操作,同時也需要考慮到與其餘的Android開發者以及後臺開發人員之間的同步配合,那就需要在架構上進行一些思考!

2.MVC設計架構

微信截圖_20151201004323.png

MVC簡介

MVC全名是Model View Controller,如圖,是模型(model)-視圖(view)-控制器(controller)的縮寫,一種軟件設計典範,用一種業務邏輯、數據、界面顯示分離的方法組織代碼,在改進和個性化定製界面及用戶交互的同時,不需要重新編寫業務邏輯。

其中M層處理數據,業務邏輯等;V層處理界面的顯示結果;C層起到橋樑的作用,來控制V層和M層通信以此來達到分離視圖顯示和業務邏輯層。

Android中的MVC

Android中界面部分也採用了當前比較流行的MVC框架,在Android中:

  • 視圖層(View)

一般採用XML文件進行界面的描述,這些XML可以理解爲AndroidApp的View。使用的時候可以非常方便的引入。同時便於後期界面的修改。邏輯中與界面對應的id不變化則代碼不用修改,大大增強了代碼的可維護性。

  • 控制層(Controller)

Android的控制層的重任通常落在了衆多的Activity的肩上。這句話也就暗含了不要在Activity中寫代碼,要通過Activity交割Model業務邏輯層處理,這樣做的另外一個原因是Android中的Actiivity的響應時間是5s,如果耗時的操作放在這裏,程序就很容易被回收掉。

  • 模型層(Model)

我們針對業務模型,建立的數據結構和相關的類,就可以理解爲AndroidApp的Model,Model是與View無關,而與業務相關的(感謝@Xander的講解)。對數據庫的操作、對網絡等的操作都應該在Model裏面處理,當然對業務計算等操作也是必須放在的該層的。就是應用程序中二進制的數據。

MVC代碼實例

我們來看看MVC在Android開發中是怎麼應用的吧!

先上界面圖

20150606093217165.png

Controller控制器&View

public class MainActivity extends ActionBarActivity implements OnWeatherListener, View.OnClickListener {

    private WeatherModel weatherModel;
    private EditText cityNOInput;
    private TextView city;
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        weatherModel = new WeatherModelImpl();
        initView();
    }

    //初始化View
    private void initView() {
        cityNOInput = findView(R.id.et_city_no);
        city = findView(R.id.tv_city);
        ...
        findView(R.id.btn_go).setOnClickListener(this);
    }

    //顯示結果
    public void displayResult(Weather weather) {
        WeatherInfo weatherInfo = weather.getWeatherinfo();
        city.setText(weatherInfo.getCity());
        ...
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_go:
                weatherModel.getWeather(cityNOInput.getText().toString().trim(), this);
                break;
        }
    }

    @Override
    public void onSuccess(Weather weather) {
        displayResult(weather);
    }

    @Override
    public void onError() {
        Toast.makeText(this, 獲取天氣信息失敗, Toast.LENGTH_SHORT).show();
    }

    private T findView(int id) {
        return (T) findViewById(id);
    }
}

從上面代碼可以看到,Activity持有了WeatherModel模型的對象,當用戶有點擊Button交互的時候,Activity作爲Controller控制層讀取View視圖層EditTextView的數據,然後向Model模型發起數據請求,也就是調用WeatherModel對象的方法 getWeather()方法。當Model模型處理數據結束後,通過接口OnWeatherListener通知View視圖層數據處理完畢,View視圖層該更新界面UI了。然後View視圖層調用displayResult()方法更新UI。至此,整個MVC框架流程就在Activity中體現出來了。

Model模型

來看看WeatherModelImpl代碼實現

public interface WeatherModel {
    void getWeather(String cityNumber, OnWeatherListener listener);
}

................

public class WeatherModelImpl implements WeatherModel {
    /*這部分代碼範例有問題,網絡訪問不應該在Model中,應該把網絡訪問換成從數據庫讀取*/
    @Override
    public void getWeather(String cityNumber, final OnWeatherListener listener) {

        /*數據層操作*/
        VolleyRequest.newInstance().newGsonRequest(http://www.weather.com.cn/data/sk/ + cityNumber + .html,
                Weather.class, new Response.Listener<weather>() {
                    @Override
                    public void onResponse(Weather weather) {
                        if (weather != null) {
                            listener.onSuccess(weather);
                        } else {
                            listener.onError();
                        }
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        listener.onError();
                    }
                });
    }
}

以上代碼看出,這裏設計了一個WeatherModel模型接口,然後實現了接口WeatherModelImpl類。controller控制器activity調用WeatherModelImpl類中的方法發起網絡請求,然後通過實現OnWeatherListener接口來獲得網絡請求的結果通知View視圖層更新UI 。至此,Activity就將View視圖顯示和Model模型數據處理隔離開了。activity擔當contronller完成了model和view之間的協調作用。

至於這裏爲什麼不直接設計成類裏面的一個getWeather()方法直接請求網絡數據?你考慮下這種情況:現在代碼中的網絡請求是使用Volley框架來實現的,如果哪天老闆非要你使用Afinal框架實現網絡請求,你怎麼解決問題?難道是修改 getWeather()方法的實現? no no no,這樣修改不僅破壞了以前的代碼,而且還不利於維護, 考慮到以後代碼的擴展和維護性,我們選擇設計接口的方式來解決着一個問題,我們實現另外一個WeatherModelWithAfinalImpl類,繼承自WeatherModel,重寫裏面的方法,這樣不僅保留了以前的WeatherModelImpl類請求網絡方式,還增加了WeatherModelWithAfinalImpl類的請求方式。Activity調用代碼無需要任何修改。

3.MVP設計架構

在App開發過程中,經常出現的問題就是某一部分的代碼量過大,雖然做了模塊劃分和接口隔離,但也很難完全避免。從實踐中看到,這更多的出現在UI部分,也就是Activity裏。想象一下,一個2000+行以上基本不帶註釋的Activity,我的第一反應就是想吐。Activity內容過多的原因其實很好解釋,因爲Activity本身需要擔負與用戶之間的操作交互,界面的展示,不是單純的Controller或View。而且現在大部分的Activity還對整個App起到類似IOS中的【ViewController】的作用,這又帶入了大量的邏輯代碼,造成Activity的臃腫。爲了解決這個問題,讓我們引入MVP框架。

MVC的缺點

在Android開發中,Activity並不是一個標準的MVC模式中的Controller,它的首要職責是加載應用的佈局和初始化用戶 界面,並接受並處理來自用戶的操作請求,進而作出響應。隨着界面及其邏輯的複雜度不斷提升,Activity類的職責不斷增加,以致變得龐大臃腫。

什麼是MVP?

MVP從更早的MVC框架演變過來,與MVC有一定的相似性:Controller/Presenter負責邏輯的處理,Model提供數據,View負責顯示。

Clipboard Image.png

MVP框架由3部分組成:View負責顯示,Presenter負責邏輯處理,Model提供數據。在MVP模式裏通常包含3個要素(加上View interface是4個):

  • View:負責繪製UI元素、與用戶進行交互(在Android中體現爲Activity)

  • Model:負責存儲、檢索、操縱數據(有時也實現一個Model interface用來降低耦合)

  • Presenter:作爲View與Model交互的中間紐帶,處理與用戶交互的負責邏輯。

  • *View interface:需要View實現的接口,View通過View interface與Presenter進行交互,降低耦合,方便進行單元測試

Tips:*View interface的必要性

回想一下你在開發Android應用時是如何對代碼邏輯進行單元測試的?是否每次都要將應用部署到Android模擬器或真機上,然後通過模擬用 戶操作進行測試?然而由於Android平臺的特性,每次部署都耗費了大量的時間,這直接導致開發效率的降低。而在MVP模式中,處理複雜邏輯的Presenter是通過interface與View(Activity)進行交互的,這說明我們可以通過自定義類實現這個interface來模擬Activity的行爲對Presenter進行單元測試,省去了大量的部署及測試的時間。

MVC → MVP

當我們將Activity複雜的邏輯處理移至另外的一個類(Presenter)中時,Activity其實就是MVP模式中的View,它負責UI元素的初始化,建立UI元素與Presenter的關聯(Listener之類),同時自己也會處理一些簡單的邏輯(複雜的邏輯交由 Presenter處理)。

MVP的Presenter是框架的控制者,承擔了大量的邏輯操作,而MVC的Controller更多時候承擔一種轉發的作用。因此在App中引入MVP的原因,是爲了將此前在Activty中包含的大量邏輯操作放到控制層中,避免Activity的臃腫。

兩種模式的主要區別

  • (最主要區別)View與Model並不直接交互,而是通過與Presenter交互來與Model間接交互。而在MVC中View可以與Model直接交互
  • 通常View與Presenter是一對一的,但複雜的View可能綁定多個Presenter來處理邏輯。而Controller是基於行爲的,並且可以被多個View共享,Controller可以負責決定顯示哪個View
  • Presenter與View的交互是通過接口來進行的,更有利於添加單元測試。

MVC與MVP區別

因此我們可以發現MVP的優點如下:

1、模型與視圖完全分離,我們可以修改視圖而不影響模型;

2、可以更高效地使用模型,因爲所有的交互都發生在一個地方——Presenter內部;

3、我們可以將一個Presenter用於多個視圖,而不需要改變Presenter的邏輯。這個特性非常的有用,因爲視圖的變化總是比模型的變化頻繁;

4、如果我們把邏輯放在Presenter中,那麼我們就可以脫離用戶接口來測試這些邏輯(單元測試)。

具體到Android App中,一般可以將App根據程序的結構進行縱向劃分,根據MVP可以將App分別爲模型層(M),UI層(V)和邏輯層(P)。

UI層一般包括Activity,Fragment,Adapter等直接和UI相關的類,UI層的Activity在啓動之後實例化相應的Presenter,App的控制權後移,由UI轉移到Presenter,兩者之間的通信通過BroadCast、Handler或者接口完成,只傳遞事件和結果。

舉個簡單的例子,UI層通知邏輯層(Presenter)用戶點擊了一個Button,邏輯層(Presenter)自己決定應該用什麼行爲進行響應,該找哪個模型(Model)去做這件事,最後邏輯層(Presenter)將完成的結果更新到UI層。

**MVP的變種:Passive View

MVP的變種有很多,其中使用最廣泛的是Passive View模式,即被動視圖。在這種模式下,View和Model之間不能直接交互,View通過Presenter與Model打交道。Presenter接受View的UI請求,完成簡單的UI處理邏輯,並調用Model進行業務處理,並調用View將相應的結果反映出來。View直接依賴Presenter,但是Presenter間接依賴View,它直接依賴的是View實現的接口。

clip_image002[4]_thumb.jpg

相對於View的被動,那Presenter就是主動的一方。對於Presenter的主動,有如下的理解:

  • Presenter是整個MVP體系的控制中心,而不是單純的處理View請求的人;
  • View僅僅是用戶交互請求的彙報者,對於響應用戶交互相關的邏輯和流程,View不參與決策,真正的決策者是Presenter;
  • View向Presenter發送用戶交互請求應該採用這樣的口吻:“我現在將用戶交互請求發送給你,你看着辦,需要我的時候我會協助你”,不應該是這樣:“我現在處理用戶交互請求了,我知道該怎麼辦,但是我需要你的支持,因爲實現業務邏輯的Model只信任你”;
  • 對於綁定到View上的數據,不應該是View從Presenter上“拉”回來的,應該是Presenter主動“推”給View的;
  • View儘可能不維護數據狀態,因爲其本身僅僅實現單純的、獨立的UI操作;Presenter纔是整個體系的協調者,它根據處理用於交互的邏輯給View和Model安排工作。

MVP架構存在的問題與解決辦法

  • 加入模板方法(Template Method)

轉移邏輯操作之後可能部分較爲複雜的Activity內代碼量還是不少,於是需要在分層的基礎上再加入模板方法(Template Method)。

具體做法是在Activity內部分層。其中最頂層爲BaseActivity,不做具體顯示,而是提供一些基礎樣式,Dialog,ActionBar在內的內容,展現給用戶的Activity繼承BaseActivity,重寫BaseActivity預留的方法。如有必要再進行二次繼承,App中Activity之間的繼承次數最多不超過3次。

  • Model內部分層

模型層(Model)中的整體代碼量是最大的,一般由大量的Package組成,針對這部分需要做的就是在程序設計的過程中,做好模塊的劃分,進行接口隔離,在內部進行分層。

  • 強化Presenter

強化Presenter的作用,將所有邏輯操作都放在Presenter內也容易造成Presenter內的代碼量過大,對於這點,有一個方法是在UI層和Presenter之間設置中介者Mediator,將例如數據校驗、組裝在內的輕量級邏輯操作放在Mediator中;在Presenter和Model之間使用代理Proxy;通過上述兩者分擔一部分Presenter的邏輯操作,但整體框架的控制權還是在Presenter手中。Mediator和Proxy不是必須的,只在Presenter負擔過大時才建議使用。

最終的架構如下圖所示:

1422253914_8854.jpg

MVP代碼實例

我們來看看MVP在Android開發中是怎麼應用的吧!!

我們用另一個例子來解釋。

先來看包結構圖

20140928093820322.png

建立Bean

public class UserBean {
     private String mFirstName;
     private String mLastName;
     public UserBean(String firstName, String lastName) {
            this. mFirstName = firstName;
            this. mLastName = lastName;
     }
     public String getFirstName() {
            return mFirstName;
     }
     public String getLastName() {
            return mLastName;
     }
}

建立Model

(處理業務邏輯,這裏指數據讀寫),先寫接口,後寫實現

public interface IUserModel {
     void setID(int id);

     void setFirstName(String firstName);

     void setLastName(String lastName);

     int getID();

     UserBean load(int id);// 通過id讀取user信息,返回一個UserBean
}

實現不在這裏寫了

Presenter控制器

建立presenter(主導器,通過iView和iModel接口操作model和view),activity可以把所有邏輯給presenter處理,這樣java邏輯就從手機的activity中分離出來。

public class UserPresenter {
     private IUserView mUserView;
     private IUserModel mUserModel;

     public UserPresenter(IUserView view) {
            mUserView = view;
            mUserModel = new UserModel();
     }

     public void saveUser( int id, String firstName, String lastName) {
            mUserModel.setID(id);
            mUserModel.setFirstName(firstName);
            mUserModel.setLastName(lastName);
     }

     public void loadUser( int id) {
           UserBean user = mUserModel.load(id);
            mUserView.setFirstName(user.getFirstName()); // 通過調用IUserView的方法來更新顯示
            mUserView.setLastName(user.getLastName());
     }
}

View視圖

建立view(更新ui中的view狀態),這裏列出需要操作當前view的方法,也是接口

public interface IUserView {
     int getID();

     String getFristName();

     String getLastName();

     void setFirstName(String firstName);

     void setLastName(String lastName);
}

activity中實現iview接口,在其中操作view,實例化一個presenter變量。

public class MainActivity extends Activity implements OnClickListener,IUserView {

     UserPresenter presenter;
     EditText id,first,last;
     @Override
     protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
           setContentView(R.layout. activity_main);

           findViewById(R.id. save).setOnClickListener( this);
           findViewById(R.id. load).setOnClickListener( this);
            id = (EditText) findViewById(R.id. id);
            first = (EditText) findViewById(R.id. first);
            last = (EditText) findViewById(R.id. last);

            presenter = new UserPresenter( this);
     }

     @Override
     public void onClick(View v) {
            switch (v.getId()) {
            case R.id. save:
                 presenter.saveUser(getID(), getFristName(), getLastName());
                 break;
            case R.id. load:
                 presenter.loadUser(getID());
                 break;
            default:
                 break;
           }
     }

     @Override
     public int getID() {
            return new Integer( id.getText().toString());
     }

     @Override
     public String getFristName() {
            return first.getText().toString();
     }

     @Override
     public String getLastName() {
            return last.getText().toString();
     }

     @Override
     public void setFirstName(String firstName) {
            first.setText(firstName);
     }

     @Override
     public void setLastName(String lastName) {
            last.setText(lastName);
     }

}

因此,Activity及從MVC中的Controller中解放出來了,這會Activity主要做顯示View的作用和用戶交互。每個Activity可以根據自己顯示View的不同實現View視圖接口IUserView。

通過對比同一實例的MVC與MVP的代碼,可以證實MVP模式的一些優點:

  • 在MVP中,Activity的代碼不臃腫;
  • 在MVP中,Model(IUserModel的實現類)的改動不會影響Activity(View),兩者也互不干涉,而在MVC中會;
  • 在MVP中,IUserView這個接口可以實現方便地對Presenter的測試;
  • 在MVP中,UserPresenter可以用於多個視圖,但是在MVC中的Activity就不行。

4.MVC、MVP與MVVM的關係

首先介紹下MVVM。

MVVM

MVVM可以算是MVP的升級版,其中的VM是ViewModel的縮寫,ViewModel可以理解成是View的數據模型和Presenter的合體,ViewModel和View之間的交互通過Data Binding完成,而Data Binding可以實現雙向的交互,這就使得視圖和控制層之間的耦合程度進一步降低,關注點分離更爲徹底,同時減輕了Activity的壓力。

在比較之前,先從圖上看看三者的異同。

2f9e4ee7d9616257ab41de204c06ffd5_b.jpg

剛開始理解這些概念的時候認爲這幾種模式雖然都是要將view和model解耦,但是非此即彼,沒有關係,一個應用只會用一種模式。後來慢慢發現世界絕對不是隻有黑白兩面,中間最大的一塊其實是灰色地帶,同樣,這幾種模式的邊界並非那麼明顯,可能你在自己的應用中都會用到。實際上也根本沒必要去糾結自己到底用的是MVC、MVP還是MVVP,不管黑貓白貓,捉住老鼠就是好貓。

MVC->MVP->MVVM演進過程

MVC -> MVP -> MVVM 這幾個軟件設計模式是一步步演化發展的,MVVM 是從 MVP 的進一步發展與規範,MVP 隔離了MVC中的 M 與 V 的直接聯繫後,靠 Presenter 來中轉,所以使用 MVP 時 P 是直接調用 View 的接口來實現對視圖的操作的,這個 View 接口的東西一般來說是 showData、showLoading等等。M 與 V已經隔離了,方便測試了,但代碼還不夠優雅簡潔,所以 MVVM 就彌補了這些缺陷。在 MVVM 中就出現的 Data Binding 這個概念,意思就是 View 接口的 showData 這些實現方法可以不寫了,通過 Binding 來實現。

如果把這三者放在一起比較,先說一下三者的共同點,也就是Model和View:

  • Model:數據對象,同時,提供本應用外部對應用程序數據的操作的接口,也可能在數據變化時發出變更通知。Model不依賴於View的實現,只要外部程序調用Model的接口就能夠實現對數據的增刪改查。

  • View:UI層,提供對最終用戶的交互操作功能,包括UI展現代碼及一些相關的界面邏輯代碼。

三者的差異在於如何粘合View和Model,實現用戶的交互操作以及變更通知

  • Controller

Controller接收View的操作事件,根據事件不同,或者調用Model的接口進行數據操作,或者進行View的跳轉,從而也意味着一個Controller可以對應多個View。Controller對View的實現不太關心,只會被動地接收,Model的數據變更不通過Controller直接通知View,通常View採用觀察者模式監聽Model的變化。

  • Presenter

Presenter與Controller一樣,接收View的命令,對Model進行操作;與Controller不同的是Presenter會反作用於View,Model的變更通知首先被Presenter獲得,然後Presenter再去更新View。一個Presenter只對應於一個View。根據Presenter和View對邏輯代碼分擔的程度不同,這種模式又有兩種情況:Passive View和Supervisor Controller。

  • ViewModel

注意這裏的“Model”指的是View的Model,跟MVVM中的一個Model不是一回事。所謂View的Model就是包含View的一些數據屬性和操作的這麼一個東東,這種模式的關鍵技術就是數據綁定(data binding),View的變化會直接影響ViewModel,ViewModel的變化或者內容也會直接體現在View上。這種模式實際上是框架替應用開發者做了一些工作,開發者只需要較少的代碼就能實現比較複雜的交互。

一點心得

MVP和MVVM完全隔離了Model和View,但是在有些情況下,數據從Model到ViewModel或者Presenter的拷貝開銷很大,可能也會結合MVC的方式,Model直接通知View進行變更。在實際的應用中很有可能你已經在不知不覺中將幾種模式融合在一起,但是爲了代碼的可擴展、可測試性,必須做到模塊的解耦,不相關的代碼不要放在一起。網上有一個故事講,一個人在一家公司做一個新產品時,一名外包公司的新員工直接在View中做了數據庫持久化操作,而且一個hibernate代碼展開後發現竟然有幾百行的SQL語句,搞得他們驚訝不已,一時成爲笑談。

個人理解,在廣義地談論MVC架構時,並非指本文中嚴格定義的MVC,而是指的MV*,也就是視圖和模型的分離,只要一個框架提供了視圖和模型分離的功能,我們就可以認爲它是一個MVC框架。在開發深入之後,可以再體會用到的框架到底是MVC、MVP還是MVVM。

5. 基於AOP的框架設計

AOP(Aspect-Oriented Programming, 面向切面編程),誕生於上個世紀90年代,是對OOP(Object-Oriented Programming, 面向對象編程)的補充和完善。OOP引入封裝、繼承和多態性等概念來建立一種對象層次結構,用以模擬公共行爲的一個集合。當我們需要爲分散的對象引入公共行爲的時候,OOP則顯得無能爲力。也就是說,OOP允許你定義從上到下的關係,但並不適合定義從左到右的關係。例如日誌功能。日誌代碼往往水平地散佈在所有對象層次中,而與它所散佈到的對象的核心功能毫無關係。對於其他類型的代碼,如安全性、異常處理和透明的持續性也是如此。這種散佈在各處的無關的代碼被稱爲橫切(Cross-Cutting)代碼,在OOP設計中,它導致了大量代碼的重複,而不利於各個模塊的重用。而AOP技術則恰恰相反,它利用一種稱爲“橫切”的技術,剖解開封裝的對象內部,並將那些影響了多個類的公共行爲封裝到一個可重用模塊,並將其名爲“Aspect”,即方面。所謂“方面”,簡單地說,就是將那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任封裝起來,便於減少系統的重複代碼,降低模塊間的耦合度,並有利於未來的可操作性和可維護性。

aop2.1.gif

5.1 AOP在Android中的使用

AOP把軟件系統分爲兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在覈心關注點的多處,而各處都基本相似。AOP的作用在於分離系統中的各種關注點,將核心關注點和橫切關注點分離開來。在Android App中,哪些是我們需要的橫切關注點?個人認爲主要包括以下幾個方面:Http, SharedPreferences, Json, Xml, File, Device, System, Log, 格式轉換等。Android App的需求差別很大,不同的需求橫切關注點必然是不一樣的。一般的App工程中應該有一個Util Package來存放相關的切面操作,在項目多了之後可以將其中使用較多的Util封裝爲一個Jar包供工程調用。

在使用MVP和AOP對App進行縱向和橫向的切割之後,能夠使得App整體的結構更清晰合理,避免局部的代碼臃腫,方便開發、測試以及後續的維護。

6. 乾貨:AndroidApp架構的設計經驗

首先是作者最最喜歡的一句話,也是對創業公司特別適用的一句話,也是對不要過度設計的一種詮釋:

先實現,再重構吧。直接考慮代碼不臃腫得話,不知道什麼時候才能寫好了joy

先實現,再重構吧。直接考慮代碼不臃腫得話,不知道什麼時候才能寫好了joy

先實現,再重構吧。直接考慮代碼不臃腫得話,不知道什麼時候才能寫好了joy

(重要的事情說三遍sunglasses

6.1 整體架構

代碼和文檔規範,根據需求進行模塊劃分,確定交互方式,形成接口文檔,這些較爲通用的內容不再細說。做Android App時,一般將App進行縱向和橫向的劃分。縱向的App由UI層,邏輯層和模型層構成,整體結構基於MVP思想(圖片來自網絡)。

9fe1e5679a642f9f82373d2a32d461b7_b.jpg

UI層內部多用模板方法,以Activity爲例一般有BaseActivity,提供包括一些基礎樣式,Dialog,ActionBar在內的內容,展現的Activity都會繼承BaseActivity並實現預留的接口,Activity之間的繼承不超過3次;爲避免Activity內代碼過多,將App的整體控制權後移,也借鑑了IOC做法,大量的邏輯操作放在邏輯層中,邏輯層和UI層通過接口或者Broadcast等實現通信,只傳遞結果。一般Activity裏的代碼量都很大,通過這兩種方式一般我寫的單個Activity內代碼量不超過400行。

邏輯層實現的是絕大部分的邏輯操作,由UI層啓動,在內部通過接口調用模型層的方法,在邏輯層內大量使用了代理。打個比方,UI層告訴邏輯層我需要做的事,邏輯層去找相應的人(模型層)去做,最後只告訴UI這件事做的結果。

模型層沒什麼好說的,這部分一般由大量的Package組成,代碼量是三層中最大的,需要在內部進行分層。

橫向的分割依據AOP面向切面的思想,主要是提取出共用方法作爲一個單獨的Util,這些Util會在App整體中穿插使用。很多人的App都會引入自己封裝的Jar包,封裝了包括文件、JSON、SharedPreference等在內的常用操作,自己寫的用起來順手,也大幅度降低了重複作業。

這樣縱,橫兩次對於App代碼的分割已經能使得程序不會過多堆積在一個Java文件裏,但靠一次開發過程就寫出高質量的代碼是很困難的,趁着項目的間歇期,對代碼進行重構很有必要。

6.2 類庫的使用

現在有很多幫助快速開發的類庫,活用這些類庫也是避免代碼臃腫和混亂的好方法,下面給題主推薦幾個常用類庫。

減少Activity代碼量的依賴注入框架ButterKnife:

https://github.com/JakeWharton/butterknife

簡化對於SQlite操作的對象關係映射框架OrmLite:

https://github.com/j256/ormlite-android

圖片緩存類庫Fresco(by facebook):

https://github.com/facebook/fresco

能用第三方庫就用第三方庫。別管是否穩定,是否被持續維護,因爲,任何第三方庫的作者,都能碾壓剛入門的菜鳥,你絕對寫不出比別人更好的代碼了。

最後附上知乎上面點贊次數很高的一段話:

如果“從零開始”,用什麼設計架構的問題屬於想得太多做得太少的問題。

從零開始意味着一個項目的主要技術難點是基本功能實現。當每一個功能都需要考慮如何做到的時候,我覺得一般人都沒辦法考慮如何做好。

因爲,所有的優化都是站在最上層進行統籌規劃。在這之前,你必須對下層的每一個模塊都非常熟悉,進而提煉可複用的代碼、規劃邏輯流程。

所以,如果真的是從零開始,別想太多了wink

參考文獻:

1、http://blog.csdn.net/luyi325xyz/article/details/43085409

2、http://blog.csdn.net/napolunyishi/article/details/22722345

3、https://www.zhihu.com/question/27645587/answer/37579829

4、http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0202/2397.html

5、http://www.tuicool.com/articles/VJZrYb

6、https://www.zhihu.com/question/27645587

7、http://blog.csdn.net/luyi325xyz/article/details/43482123

8、https://www.zhihu.com/question/30976423

9、http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0313/2599.html

10、http://www.2cto.com/kf/201506/405766.html

11、https://www.zhihu.com/question/19766132

12、http://www.cnblogs.com/artech/archive/2010/03/25/1696205.html

13、https://segmentfault.com/a/1190000003927200

14、http://blog.csdn.net/knxw0001/article/details/39637273

版權聲明 本文由周鴻博創作,轉載需署名作者且註明文章出處 參考代碼 要獲取本文的參考代碼,請訪問: https://www.tianmaying.com/tutorial/AndroidMVC/repo
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章