編輯:關於Android編程
很多人在開發Android項目時沒有考慮過架構模式的問題,以至於隨著項目的增大,Activty或者Fragment中代碼也會越來越多,導致項目的維護變的越來越復雜。然而在Android中使用比較多的兩種框架模式就是MVC和MVP,下面我將分別介紹一下這兩種框架模式。
MVC全名是Model View Controller,是模型(model)-視圖(view)-控制器(controller)的縮寫,一種軟件設計典范,用一種業務邏輯、數據、界面顯示分離的方法組織代碼,將業務邏輯聚集到一個部件裡面,在改進和個性化定制界面及用戶交互的同時,不需要重新編寫業務邏輯。其中M層處理數據,業務邏輯等;V層處理界面的顯示結果;C層起到橋梁的作用,來控制V層和M層通信以此來達到分離視圖顯示和業務邏輯層。
其實MVC在J2EE開發中應用是很多的,它將業務邏輯與界面分離開來,通過控制器將其連接,從而達到了很好的解耦效果,通過MVC編寫的代碼有利於後期的維護和改造。
在Android開發中,我們也是經常看到MVC框架模式的身影,例如我們開發Android端應用時,界面編寫時在XML中,而我們經常回把網絡訪問數據獨立出來,再通過Activity處理用戶交互的問題,這就是一種MVC的思想。采用MVC模式的好處是便於UI界面部分的顯示和業務邏輯,數據處理分開。下面具體以MVC的表現形式描述一下:
M層:適合做一些業務邏輯處理,比如數據庫存取操作,網絡操作,復雜的算法,耗時的任務等都在model層處理。
V層:應用層中處理數據顯示的部分,XML布局可以視為V層,顯示Model層的數據結果。
C層:在Android中,Activity處理用戶交互問題,因此可以認為Activity是控制器,Activity讀取V視圖層的數據(eg.讀取當前EditText控件的數據),控制用戶輸入(eg.EditText控件數據的輸入),並向Model發送數據請求(eg.發起網絡請求等)。
為方便理解,我下面舉一個例子,由於MVC容易理解,也鑒於篇幅問題,這裡只給出簡短的偽代碼,因為具體的代碼也沒有什麼意義:
我們通過一個獲取天氣預報數據的小項目來解讀 MVC for Android
Controller控制器
public class MainActivity extends Activity implements StrUIDataListener { private StrVolleyInterface networkHelper; private StrVolleyInterface expertNetworkHelper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } //加載網絡數據 private void initDate() { try { networkHelper = new StrVolleyInterface(DemandDetailActivity.this); networkHelper.setStrUIDataListener(DemandDetailActivity.this); ApiClient.getDataDetail(DemandDetailActivity.this, id, networkHelper); } catch (Exception e) { e.printStackTrace(); } } @Override public void onDataChanged(String data) { //數據加載成功 //....更新UI } @Override public void onErrorHappened(VolleyError error) { Toast.makeText(DemandDetailActivity.this, "加載錯誤,請檢查網絡!", Toast.LENGTH_SHORT).show(); } }
從上面代碼可以看到,Activity持有了ApiClient網絡請求模型的對象,當我們需要獲取數據時,只需要調用initData()方法。比如我們點擊Button,Activity作為Controller控制層會處理View視圖層,並調用ApiClient.getDataDetail方法,當Model模型處理數據結束後,通過接口onDataChanged通知View視圖層數據處理完畢,View視圖層該更新界面UI了。然後View視圖層去更新界面。看到這裡,我們會發現整個MVC框架流程就在Activity中體現出來了。
Model模型
這裡ApiClient就是充當Model層的,專門用於處理網絡數據請求,代碼就不展示了,讀者只要理解就行。上面的例子中,Activity將View視圖顯示和Model模型數據處理隔離開了。activity擔當contronller完成了model和view之間的協調作用。
那麼我們為什麼要這樣實現,直接在Activity中實現網球請求不是更方便嗎?
很多人可能會有這種疑問,可是大家想想,如果這種網絡請求都在Activity中實現的話,一是不利於代碼復用,而是後期維護起來是很困難的。比如我們現在的網絡請求用的是Volley,以後想換成NoHttp實現的話,我們好需要修改每一個Activity類,這是不是很繁瑣,看到這裡我們就會發現MVC的好處了。
MVC簡要總結:
在Android項目中,業務邏輯,數據處理等擔任了Model(模型)角色,XML界面顯示等擔任了View(視圖)角色,Activity擔任了Contronller(控制器)角色。contronller(控制器)是一個中間橋梁的作用,通過接口通信來協同 View(視圖)和Model(模型)工作,起到了兩者之間的通信作用。
我們發現,其實控制器Activity主要是起到解耦作用,將View視圖和Model模型分離,雖然Activity起到交互作用,但是Activity中有很多關於視圖UI的顯示代碼,因此View視圖和Activity控制器並不是完全分離的,也就是說一部分View視圖和Contronller控制器Activity是綁定在一個類中的。所以MVC應用在Android中,它的解耦效果並不是十分的完美,當我們的APP逐漸變大時,我們還會發現我們的Activity和Fragment會越來越龐大。
因此如果我們開發的Android項目不是很龐大,用MVC框架模式還是比較理想的,如果我們的項目是比較龐大,而且後期經常需要修改維護的話,我更建議使用MVP框架模式,下面對MVP框架模式做一個簡單的介紹。
什麼是MVP
MPV 是從經典的MVC模式演變過來的,其基本思路都是相通的。
MVP是模型(Model)、視圖(View)、控制者(Presenter)的縮寫,分別代表項目中3個不同的模塊。
模型(Model):負責處理數據的加載或者存儲,比如從網絡或本地數據庫獲取數據等;
視圖(View):負責界面數據的展示,與用戶進行交互;
控制者(Presenter):相當於協調者,是模型與視圖之間的橋梁,將模型與視圖分離開來。
在Andorid項目中,我們習慣將Activity作為MVC中的控制者來達到Model模型和View視圖分離,但是在MVP框架模式中,通常將Activity作為View視圖層,因為在MVC框架模式中Activity和View視圖顯示關聯緊密,Activity中包含大量的View視圖顯示代碼,如果哪天老板說需要修改View視圖顯示,這時候你是不是感覺需要修改Activity中的大量代碼?這麼一來會將Activity中控制邏輯破壞,也導致Activity中承擔太多的職責。根據單一職責原則,Activity主要起到用戶交互作用,也就是接收用戶輸入,顯示請求結果。因此可以通過MVP框架模式來減輕Activity的職責。如下圖所示,View與Model並不直接交互,而是使用Presenter作為View與Model之間的橋梁。其中Presenter中同時持有Viwe層以及Model層的Interface的引用,而View層持有Presenter層Interface的引用。當View層某個界面需要展示某些數據的時候,首先會調用Presenter層的某個接口,然後Presenter層會調用Model層請求數據,當Model層數據加載成功之後會調用Presenter層的回調方法通知Presenter層數據加載完畢,最後Presenter層再調用View層的接口將加載後的數據展示給用戶。這就是MVP模式的整個核心過程。
這樣分層的好處就是大大減少了Model與View層之間的耦合度。一方面可以使得View層和Model層單獨開發與測試,互不依賴。另一方面Model層可以封裝復用,可以極大的減少代碼量。當然,MVP還有其他的一些優點,這裡不再贅述。下面看下MVP模式在具體項目中的使用。
我舉一個獲取天氣的例子來說明,這樣大家會很容易看懂
Model模型
/** * 天氣Model接口 */ public interface WeatherModel { void loadWeather(String cityNO, OnWeatherListener listener); } ......... /** * 天氣Model實現 */ public class WeatherModelImpl implements WeatherModel { @Override public void loadWeather(String cityNO, final OnWeatherListener listener) { /*數據層操作*/ VolleyRequest.newInstance().newGsonRequest("http://www.weather.com.cn/data/sk/" + cityNO + ".html", Weather.class, new Response.Listener() { @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(); } }); } }
和MVC一樣,Model的代碼不變,只是用與處理網絡請求,請求成功後接口回調將Model模型處理的數據返回給Presenter控制者。
Presenter控制器
/** * 天氣 Presenter接口 */ public interface WeatherPresenter { /** * 獲取天氣的邏輯 */ void getWeather(String cityNO); } .......... /** * 在Presenter層實現,給Model層回調,更改View層的狀態,確保Model層不直接操作View層 */ public interface OnWeatherListener { /** * 成功時回調 * * @param weather */ void onSuccess(Weather weather); /** * 失敗時回調,簡單處理,沒做什麼 */ void onError(); } ......... /** * 天氣 Presenter實現 */ public class WeatherPresenterImpl implements WeatherPresenter, OnWeatherListener { /*Presenter作為中間層,持有View和Model的引用*/ private WeatherView weatherView; private WeatherModel weatherModel; public WeatherPresenterImpl(WeatherView weatherView) { this.weatherView = weatherView; weatherModel = new WeatherModelImpl(); } @Override public void getWeather(String cityNO) { weatherView.showLoading(); weatherModel.loadWeather(cityNO, this); } @Override public void onSuccess(Weather weather) { weatherView.hideLoading(); weatherView.setWeatherInfo(weather); } @Override public void onError() { weatherView.hideLoading(); weatherView.showError(); } }
從代碼中我們可以看到Presenter控制器同時持有 WeatherModel和WeatherView對象且實現了OnWeatherListener接口取回Model模型數據,因此,WeatherPresenterImpl向WeatherModel發送數據請求,然後通過OnWeatherListener接口實現獲取請求結果,在將結果通過接口WeatherView把數據顯示到Activity擔當的View視圖中。從而達到徹底將Model和View完全分離,試想在這種情況下,如果你需要修改Model是完全不會影響View視圖代碼的修改的,同理,修改View視圖層的時候,也完全無需修改Model層。相當於Model和View互相不知道對方的存在,都是通過中間控制器Presenter來傳達通信。
View視圖
先定義一個View視圖顯示的接口WeatherView
新聞列表模塊主要是展示從網絡獲取的新聞列表信息,View層的接口大概需要如下方法:
(1)加載數據的過程中需要提示“正在加載”的反饋信息給用戶
(2)加載成功後,將加載得到的數據填充到RecyclerView展示給用戶
(3)加載成功後,需要將“正在加載”反饋信息取消掉
(4)若加載數據失敗,如無網絡連接,則需要給用戶提示信息
根據上面描述,我們將View層的接口定義如下,分別對應上面四個方法:
public interface WeatherView { void showLoading(); void hideLoading(); void showError(); void setWeatherInfo(Weather weather); }然後實現Activity實現WeatherView接口
/** * 天氣界面 */ public class WeatherActivity extends BaseActivity implements WeatherView, View.OnClickListener { .......................... private WeatherPresenter weatherPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); } private void init() { .................... findView(R.id.btn_go).setOnClickListener(this); weatherPresenter = new WeatherPresenterImpl(this); //傳入WeatherView loadingDialog = new ProgressDialog(this); loadingDialog.setTitle("加載天氣中..."); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_go: weatherPresenter.getWeather(cityNOInput.getText().toString().trim()); break; } } @Override public void showLoading() { loadingDialog.show(); } @Override public void hideLoading() { loadingDialog.dismiss(); } @Override public void showError() { //Do something Toast.makeText(getApplicationContext(), "error", Toast.LENGTH_SHORT).show(); } @Override public void setWeatherInfo(Weather weather) { WeatherInfo info = weather.getWeatherinfo(); //更新界面 ..................... } }總結:
MVP框架模式完全將Model模型和View視圖分離,從而使得代碼的耦合低,利用MVP框架寫項目達到解耦作用。 MVP和MVC最大的區別是:MVC中的V和C關系比較緊密,耦合度太高,從C中訪問M獲取數據一定程度上也可以看成從V中訪問M。而MVP中M和V完全分離,互相不知道對方的存在,Presenter通過接口通信方式將V和M通信。 在Android中MVP框架 Activity擔當View視圖層,MVC框架模式Activity擔當控制器。
雖然MVP可以達到很好的解耦效果,可是在開發的過程中需要多寫一層(Presenter)的代碼,代碼量會擴大,而且業務邏輯對不太熟悉的開發者來說還是略顯復雜,因此建議如果開發相對較小的項目,而且項目不需要頻繁的修改可以選用MVC,如果開發比較大的項目,而且需要後期不斷地維護修改的話建議使用MVP框架模式開發。
理解球坐標系首先看下球的坐標系 ,如圖 : (圖來自百度百科 ) 設球上有一點 A ,球心為O ,OA在 xOy上的投影與X軸夾角為φ (范圍
過去的兩天,在項目中,拋棄了ListView, 想試一試RecyclerView, 在用的過程中,遇到了一些問題,比如:如何為RecyclerView添加Header和F
在android開發中對圖片處理很是頻繁,其中對圖片的顏色處理就是很常見的一種。我們經常看到一些類似美圖秀秀,美顏相機的app,為什麼那麼黑的人拍出來是確實那麼地白呢?長
與SAX和PULL解析不同,Dom解析是將XML文件全部載入,組裝成一顆Dom樹,然後通過節點以及節點之間的關系來解析XML文件,占用內存比較大,一般比較推薦用SAX和P