

點此進入目錄:[干貨] 十天 教你從創(chuàng)意到上線APP
一、架構設計的目的
通過設計使程序模塊化,做到模塊內部的高聚合和模塊之間的低耦合。這樣做的好處是使得程序在開發(fā)的過程中,開發(fā)人員只需要專注于一點,提高程序開發(fā)的效率,并且更容易進行后續(xù)的測試以及定位問題。但設計不能違背最初的目的,對于不同量級的工程,具體架構的實現(xiàn)方式必然是不同的,切忌犯為了設計而設計,為了架構而架構的毛病。
下面我們詳細介紹下Android中常用的兩種架構類型,然后結合實例講解具體實現(xiàn)流程,最后根據(jù)業(yè)務場景選取合適我們的架構模式。
舉個簡單的例子:
一個Android App如果只有幾個Java文件,那只需要做點模塊和層次的劃分就可以,引入框架或者架構反而提高了工作量,降低了生產力;
但如果當前開發(fā)的App最終代碼量在10W行以上,本地需要進行復雜操作,同時也需要考慮到與其余的Android開發(fā)者以及后臺開發(fā)人員之間的同步配合,那就需要在架構上進行一些思考!
二、MVC設計架構

1、MVC簡介
MVC全名是Model View Controller,如圖,是模型(model)-視圖(view)-控制器(controller)的縮寫,一種軟件設計典范,用一種業(yè)務邏輯、數(shù)據(jù)、界面顯示分離的方法組織代碼,在改進和個性化定制界面及用戶交互的同時,不需要重新編寫業(yè)務邏輯。
其中M層處理數(shù)據(jù),業(yè)務邏輯等;V層處理界面的顯示結果;C層起到橋梁的作用,來控制V層和M層通信以此來達到分離視圖顯示和業(yè)務邏輯層。
2、Android中的MVC
視圖層(View)
一般采用XML文件進行界面的描述,這些XML可以理解為AndroidApp的View。使用的時候可以非常方便的引入。同時便于后期界面的修改。邏輯中與界面對應的id不變化則代碼不用修改,大大增強了代碼的可維護性。
控制層(Controller)
Android的控制層的重任通常落在了眾多的Activity的肩上。這句話也就暗含了不要在Activity中寫代碼,要通過Activity交割Model業(yè)務邏輯層處理,這樣做的另外一個原因是Android中的Actiivity的響應時間是5s,如果耗時的操作放在這里,程序就很容易被回收掉。
模型層(Model)
我們針對業(yè)務模型,建立的數(shù)據(jù)結構和相關的類,就可以理解為AndroidApp的Model,Model是與View無關,而與業(yè)務相關的,比如:對數(shù)據(jù)庫的操作、對網(wǎng)絡等的操作都應該在Model里面處理,當然對業(yè)務計算等操作也是必須放在的該層的。就是應用程序中二進制的數(shù)據(jù)。
3、MVC框架在Activity中的體現(xiàn)
我們這里以一個按鈕點擊后進行網(wǎng)絡請求然后將請求數(shù)據(jù)同步更新到界面的Demo為例,向大家展示MVC在Android中具體的應用場景和控制流程:
(1)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的數(shù)據(jù)然后向Model模型發(fā)起數(shù)據(jù)請求,也就是調用WeatherModel對象的getWeather()方法。當Model模型處理數(shù)據(jù)結束后,通過接口OnWeatherListener通知View視圖層數(shù)據(jù)處理,然后View視圖層調用displayResult()方法更新UI。至此,整個MVC框架流程就在Activity中體現(xiàn)出來了。
(2)WeatherModelImpl代碼實現(xiàn)&Model模型
public interface WeatherModel {
void getWeather(String cityNumber, OnWeatherListener listener);
}
public class WeatherModelImpl implements WeatherModel {
@Override
public void getWeather(String cityNumber, final OnWeatherListener listener) {
// 數(shù)據(jù)層操作
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模型接口,然后實現(xiàn)了接口WeatherModelImpl類,Controller控制器Activity調用WeatherModelImpl類中的方法發(fā)起網(wǎng)絡請求,然后通過實現(xiàn)OnWeatherListener接口來獲得網(wǎng)絡請求的結果通知View視圖層更新UI。至此,Activity就將View視圖顯示和Model模型數(shù)據(jù)處理隔離開了。Activity擔當Contronller完成了Model和View之間的協(xié)調作用。
至于這里為什么不直接設計成類里面的一個getWeather()方法直接請求網(wǎng)絡數(shù)據(jù)呢?我們考慮下面這種情況:現(xiàn)在代碼中的網(wǎng)絡請求是使用Volley框架來實現(xiàn)的,如果哪天老板非要你使用Afinal框架實現(xiàn)網(wǎng)絡請求,問題要怎么解決呢?難道是修改getWeather()方法的實現(xiàn)嗎?這樣當然不行,因為這樣修改不僅破壞了以前的代碼而且還不利于維護??紤]到以后代碼的擴展和維護性,我們選擇設計接口的方式來解決這個問題:我們實現(xiàn)另外一個WeatherModelWithAfinalImpl類,繼承自WeatherModel重寫里面的方法,這樣不僅保留了以前的WeatherModelImpl類請求網(wǎng)絡方式,還增加了WeatherModelWithAfinalImpl類的請求方式,這樣Activity調用代碼無需要任何修改。
三、MVP設計架構
1、MVP的誕生
在App開發(fā)過程中經(jīng)常出現(xiàn)的問題就是某一部分的代碼量過大,雖然做了模塊劃分和接口隔離,但也很難完全避免,而且從實踐中看出,這更多的出現(xiàn)在UI部分,也就是Activity里。不過Activity內容過多的原因也很好解釋,因為Activity本身需要擔負與用戶之間的操作交互、界面的展示,所以它不是單純的Controller或View,這就造成了Activity的臃腫。所以為了解決這個問題,讓我們引入MVP框架。
2、MVC的缺點
正如剛才所說,在Android開發(fā)中Activity并不是一個標準的MVC模式中的Controller,它的首要職責是加載應用的布局和初始化用戶界面,并接受和處理來自用戶的操作請求,進而作出響應。隨著界面及其邏輯的復雜度不斷提升,Activity類的職責不斷增加,以致變得龐大臃腫,這便是MVC的缺點所在。
3、什么是MVP?
MVP是從MVC框架演變過來的,當然也就與MVC有一定的相似性:Controller/Presenter負責邏輯的處理,Model提供數(shù)據(jù),View負責顯示。

MVP框架由3部分組成:View負責顯示;Presenter負責邏輯處理;Model提供數(shù)據(jù)。在MVP模式里通常包含3個要素(加上View interface是4個):
- View:負責繪制UI元素、與用戶進行交互(在Android中體現(xiàn)為Activity);
- Model:負責存儲、檢索、操縱數(shù)據(jù)(有時也實現(xiàn)一個Model interface用來降低耦合);
- Presenter:作為View與Model交互的中間紐帶,處理與用戶交互的負責邏輯;
- View interface:需要View實現(xiàn)的接口,View通過View interface與Presenter進行交互,以此降低耦合并且方便進行單元測試;
Tips:View interface的必要性
回想一下你在開發(fā)Android應用時是如何對代碼邏輯進行單元測試的?是否每次都要將應用部署到Android模擬器或真機上,然后通過模擬用戶操作進行測試?然而由于Android平臺的特性,每次部署都耗費了大量的時間,這直接導致開發(fā)效率的降低。而在MVP模式中,處理復雜邏輯的Presenter是通過interface與View(Activity)進行交互的,這說明我們可以通過自定義類實現(xiàn)這個interface來模擬Activity的行為對Presenter進行單元測試,省去了大量的部署及測試的時間。
4、MVC → MVP
當我們將Activity復雜的邏輯處理移至另外的一個類(Presenter)中時,Activity其實就是MVP模式中的View,它負責UI元素的初始化,建立UI元素與Presenter的關聯(lián)(Listener之類),同時自己也會處理一些簡單的邏輯(復雜的邏輯交由Presenter處理)。
MVP的Presenter是框架的控制者,承擔了大量的邏輯操作,而MVC的Controller更多時候承擔一種轉發(fā)的作用。因此在App中引入MVP的原因,是為了將此前在Activty中包含的大量邏輯操作放到控制層中,避免Activity的臃腫。
因此我們可以發(fā)現(xiàn)MVP的優(yōu)點如下:
- 模型與視圖完全分離,我們可以修改視圖而不影響模型;
- 可以更高效地使用模型,因為所有的交互都發(fā)生在一個地方 —— Presenter內部;
- 我們可以將一個Presenter用于多個視圖,而不需要改變Presenter的邏輯。這個特性非常的有用,因為視圖的變化總是比模型的變化頻繁;
- 如果我們把邏輯放在Presenter中,那么我們就可以脫離用戶接口來測試這些邏輯(單元測試)。
具體到Android App中,一般可以將App根據(jù)程序的結構進行縱向劃分,根據(jù)MVP可以將App分別為模型層(M)、UI層(V)和邏輯層(P)。UI層一般包括Activity、Fragment等直接和UI相關的類,UI層的Activity在啟動之后實例化相應的Presenter,App的控制權后移,由UI轉移到Presenter,兩者之間的通信通過BroadCast、Handler或者接口完成,只傳遞事件和結果。
舉個簡單的例子,UI層通知邏輯層(Presenter)用戶點擊了一個Button,邏輯層(Presenter)自己決定應該用什么行為進行響應,該找哪個模型(Model)去做這件事,最后邏輯層(Presenter)將完成的結果更新到UI層。
MVP的變種:Passive View
MVP的變種有很多,其中使用最廣泛的是Passive View模式,即被動視圖。在這種模式下,View和Model之間不能直接交互,而是通過Presenter與Model打交道。Presenter接受View的UI請求并完成簡單的UI處理邏輯并調用Model進行業(yè)務處理,然后調用View將相應的結果反映出來。View直接依賴Presenter,但是Presenter間接依賴View,它直接依賴的是View實現(xiàn)的接口。
相對于View的被動,Presenter就是主動的一方:
- Presenter是整個MVP體系的控制中心,而不是單純的處理View請求的人;
- View僅僅是用戶交互請求的匯報者,對于響應用戶交互相關的邏輯和流程,View不參與決策,真正的決策者是Presenter;
- View向Presenter發(fā)送用戶交互請求應該采用這樣的口吻:“我現(xiàn)在將用戶交互請求發(fā)送給你,你看著辦,需要我的時候我會協(xié)助你”,不應該是這樣:“我現(xiàn)在處理用戶交互請求了,我知道該怎么辦,但是我需要你的支持,因為實現(xiàn)業(yè)務邏輯的Model只信任你”;
- 對于綁定到View上的數(shù)據(jù),不應該是View從Presenter上“拉”回來的,應該是Presenter主動“推”給View的;
- View盡可能不維護數(shù)據(jù)狀態(tài),因為其本身僅僅實現(xiàn)單純的、獨立的UI操作;
- Presenter才是整個體系的協(xié)調者,它根據(jù)處理用于交互的邏輯給View和Model安排工作。
5、MVP架構存在的問題與解決辦法
加入模板方法(Template Method)
轉移邏輯操作之后可能部分較為復雜的Activity內代碼量還是不少,于是需要在分層的基礎上再加入模板方法(Template Method)。
具體做法是在Activity內部分層,其中最頂層為BaseActivity,不做具體顯示而是提供一些基礎樣式:Dialog、ActionBar在內的內容等。展現(xiàn)給用戶的Activity繼承BaseActivity,重寫B(tài)aseActivity預留的方法。如有必要再進行二次繼承,App中Activity之間的繼承次數(shù)最多不超過3次。
“愛閱”中采用的即是這樣的方法,加入模板方法不僅省去了很多重復的邏輯工作量,也更方便不同子視圖層的個性化定制;Model內部分層
模型層中的整體代碼量是最大的,一般由大量的Package組成,針對這部分需要做的就是在程序設計的過程中,做好模塊的劃分,進行接口隔離,在內部進行分層。強化Presenter
強化Presenter的作用,將所有邏輯操作都放在Presenter內也容易造成Presenter內的代碼量過大,對于這點有一個方法是在UI層和Presenter之間設置中介者Mediator,將例如數(shù)據(jù)校驗、組裝在內的輕量級邏輯操作放在Mediator中;在Presenter和Model之間使用代理Proxy;通過上述兩者分擔一部分Presenter的邏輯操作,但整體框架的控制權還是在Presenter手中。Mediator和Proxy不是必須的,只在Presenter負擔過大時才建議使用。
四 、MVP代碼實例
接下來我們看看MVP在Android開發(fā)中是怎么應用的!
(1)建立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;
}
}
(2)建立Model
public interface UserModel {
void setID(int id);
void setFirstName(String firstName);
void setLastName(String lastName);
int getID();
UserBean load(int id);// 通過id讀取user信息,返回一個UserBean
}
(3)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());
}
}
(4)View視圖
建立view(更新ui中的view狀態(tài)),這里列出需要操作當前view的方法,也是接口
public interface IUserView {
int getID();
String getFristName();
String getLastName();
void setFirstName(String firstName);
void setLastName(String lastName);
}
(5)MainActivity
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可以根據(jù)自己顯示View的不同實現(xiàn)View視圖接口IUserView。
五、“愛閱”中的架構設計
上面介紹了Android應用中目前最火熱的兩種架構模式,那么“愛閱”中采用的架構是什么呢?是MVC和MVP共存的架構模式!大家可能有些奇怪,難道MVC和MVP是可以共存的嗎?那是當然的了,只是我們在具體的業(yè)務場景中要根據(jù)不同的情況做出恰當?shù)倪x擇就好。
比如在“愛閱”的整個架構當中,MainActivity扮演了整個APP主要的業(yè)務角色,在APP運行的過程中,無論是側滑菜單的打開、搜索界面的呈現(xiàn)、收藏界面的展現(xiàn)等等,MainActivity都屬于常駐內存的角色,所以對于MainActivity中的一些基本功能而言采用MVC模式最為合適,比如:左右兩側菜單欄的初始化、主界面ViewPager的更新和顯示等。而在每個Fragment當中,選擇MVP架構最為合適,因為涉及到了許多相似的界面設計(比如主頁面中的頁面點選和滑動等),這樣不僅減少了界面重復邏輯的代碼編寫,而且把相同的業(yè)務邏輯處理抽取到Presenter中進行處理后,更容易實現(xiàn)不同界面的個性化定制并減少模塊之間的耦合。
另外,在MainActivity的設計中,對于一些長連接的邏輯處理同樣采用了MVP的架構設計。比如:對數(shù)據(jù)庫和網(wǎng)絡獲取的原始數(shù)據(jù)解析完成后,我采用了EventBus對事件進行分發(fā)并在MainActivity中進行捕獲,這樣就實現(xiàn)了高度的業(yè)務邏輯解耦,即前面所說的 —— 數(shù)據(jù)不是從MainActivity中拉取,而是把數(shù)據(jù)推給MainActivity;MainActivity只關心要什么、接受什么,而不關心具體是怎么獲得的;MainActivity不是整個APP的業(yè)務邏輯承擔者,而僅僅輕量化的實現(xiàn)與用戶的UI交互功能。
這樣,我們就根據(jù)具體的業(yè)務場景做出了合理的架構選擇和設計,在下一篇的課程當中,我們就根據(jù)此架構框架進行主頁面的開發(fā)工作,See You!
聯(lián)系方式: