Android中MVP模式講解及實踐
前兩年的時候,我經常逛http://androidweekly.net這個網站,上面就有過很多文章介紹MVP模式,我很感興趣,於是把這個東西介紹給身邊的同事,同事們好像沒有多大反應,可能是當時在國內MVP用的範圍還比較少吧。後來我換了工作,再後來某一天我發現各類部落格紛紛在介紹這個東西,各類群裡面也在討論這個東西。我感覺到MVP被越來越多的人認可了,反倒是自己顯得落伍起來。所以,趁著空閒,做一個筆記,以做學習之路的備忘。
什麼是MVP模式?
看NBA的都知道MVP(National Basketball Association Most Valuable Player Award ,簡稱MVP)這個概念,我當時的第一反應也是這個。但是,此MVP非彼MVP.我們今天要討論的MVP其實同MVC一樣,是一種程式設計模式和思想,也許更準確地講是一種架構。
MVP和MVC
MVC簡介
開發Android的都知道MVC。
- M對應Model,代表業務資料
- V對應View,代表檢視
- C對應Controller,代表控制器。
MVC架構將檢視和資料分離,在WEB領域中應用的很廣泛。
使用者通過介面元件進行操作,也就是View層,相應的動作會傳遞給控制器也就是Controller層,而Controller根據自己的業務邏輯去操作資料層也就是Model,而最終資料層的變化會同步更新到檢視層。
MVC好處
這裡直接引用百度百科
MVC 分層有助於管理複雜的應用程式,因為您可以在一個時間內專門關注一個方面。例如,您可以在不依賴業務邏輯的情況下專注於檢視設計。同時也讓應用程式的測試更加容易。
MVC 分層同時也簡化了分組開發。不同的開發人員可同時開發檢視、控制器邏輯和業務邏輯。
可以看到MVC的主要目的是為了檢視和資料分離,這對於開發大型軟體來說更方便進行模組的劃分,提高編碼速度與質量。
Android中的MVC
Android世界中也經常運用到MVC模式。
Activity對應檢視介面也就是View層。
資料庫檔案,Sharedprefrence,記憶體緩衝,磁碟緩衝等資料內容對應Model層。
而Controller控制層基本上也由Activity層面來進行。
Android中mvc中基本動作流程
假設我們現在有這麼一個需求,需要在一個介面上顯示當天的天氣,不僅如此,還可以通過列表項選擇以往某一天的天氣。
mvc架構開發的話,大概是這樣。
- 在layout制定相應的佈局檔案,然後顯示在Activity上,用於顯示天氣資訊。這對應於View層,這裡的View並不是Android中開發中的元件view而是對檢視的統稱.
- Activity在onCreate方法或者onResume方法去伺服器獲取資料,或者通過介面上的某個按鈕之類去啟動獲取伺服器資料的任務,這裡就對應到View—>Controller,只不過這裡的View和Controller對是由Activity來完成。
- Controller獲取到了資料之後,分別存在,記憶體、磁碟和資料庫中,並且資料獲取成功或者失敗後,Activity介面需要同步更新狀態。這由對應上面流程中的Controller—>Model 和Model—->View。
這裡的流程還算清晰,也便於理解。
MVP為什麼?
上面講解了MVC的基礎知識,大家可能覺得MVC挺好的啊?怎麼還要整一個MVP。是的MVC是挺好的,但是它也有它的缺點,特別是針對Androi開發。
因為Android的特殊性,使得Activity對應了MVC中的V和C,同時擔任兩個角色,就有了類似“既當爹又當媽”的感覺,這顯然就不符合軟體設計原則的“單一職責”原則。但現實中是很多的APP程式碼中有這麼的處境,特別是Androi原生的很多系統APK,某些Activity動則幾千行程式碼。
況且,隨著專案的深入發展,很多邏輯很越來越複雜,Activity處理的東西也會越來越多,程式碼越來越臃腫。這樣一來維護起來的代價就會越來越高,這是因為View的變化會引起Controller的很多變化,反之亦然。用一句大白話來說明就是–某一段程式碼的變動會引起很多其他相關聯的程式碼的改動,而程式設計師都是懶惰的,所以會恨死這樣的程式碼。
而MVP就是要減輕在Android中的這種困惑。
MVP是基於MVC的,它的架構圖如下:
- M(Model) 資料相關層
- V(View) 檢視層,如Activity上的佈局
- P(Presenter) 紐帶層,用來連線Model與View.
MVP開發在Android中的基本流程
1. View層定義View.interface,用來定義View的行為。一般由Activity或者是Fragment來實現這個介面,它定義了View檢視的各種變化,如設定Textview,載入對話方塊,更新進度條等。
2. Model層定義Modle.interface,這個是用來定義資料層發生變化時的通知介面,因為Model不能直接與View互動,所以它與Presenter互動,然後再通過Presenter間接達到與View的互動。
3. Presenter翻譯的意思是主持人,也就是主持場合,控制節奏的意思。在這時Presenter就負責具體的業務邏輯,請求資料,把資料送到Model,或者監聽Model的資料變化,接受View層的動作,負責通過通知View層的檢視變化。
如果跟MVC的架構圖對比的話,可以發現它們有相似之處也有不同。
相似之處
模組劃分的相似
MVC由Model、View、Controller構成。
MVP由Model、View、Presenter構成。
不同的地方
- MVP中Presenter取代了MVC中的Controller
- MVC中Model、View、Controller之間相互發生通訊,而MVP中Model與Presenter相互通訊,View與Presenter相互通訊,而Model與View之間沒有通訊。
Android中MVP的好處?
就Android層面上來講MVC架構雖然好,但不是最好,情況前面有講過。用一句話概括就是“模組界限很模糊”。而MVP的出現實際上就是將MVC進行升級,對應Android開發中就是幫助Activity解壓。
MVC中Activity同時充當了V和C的角色,這就屬於界限劃分不清楚。而MVP則劃分的很清楚,Activity只充當V的角色,業務邏輯控制交給了Presenter.
個人對MVP模式的理解
這一段是我自己的看法,也許不正確。
我個人覺得MVP沒有什麼很神祕的,因為Android SDK上開發,本來就差不多是MVC的角色。Activity基本上Android開發中最重要的一環。
我以前在團隊工作的時候,團隊分工是每人負責相應的Activity,在這裡Activity是最小的開發單元。再後來,某些Activity變得越來越重要,越來越複雜,程式碼也越來越多,這樣會造成團隊某個人的開發任務重,而其他的團隊成員也幫不上忙。而MVP的出現可以將Activity再細分,劃為View和Presenter兩個部分,所以Activity不再是最小的開發單元,如果可以完全可以這樣分配任務,一個開發人員負責View部分,另一個開發人員負責Presenter部分。
況且因為MVP的劃分,所以各個部分其實相對獨立,V的變動會對P的部分造成較少的影響,而M對V或者說V對M幾乎是透明的。
因為Presenter的存在,View和Model就可以很輕鬆,頂多Presenter累一點。
還有一個特點是MVP模式很適合測試,單獨測試VIEW成了一種可能。我們可以模擬View和Model的資料來測試Presenter的邏輯。
MVP實戰
在現在的公司專案中,我已經用上了MVP模式開發。但是在這裡,我不想照搬程式碼。主要是因為怕複雜的程式碼或者其它的知識點干擾MVP本身的脈絡。所以,我用一個簡單的DEMO來講解,大家一看就明白。
場景需求
假設現在需要做一款APP,就是顯示天氣,介面很簡單,一個TextView顯示天氣資訊,一個Button用來請求實時天氣。
如下圖所示
軟體啟動後,會自動獲取天氣,然後TextView就可以顯示資訊。而使用者點選獲取實時天氣的按鈕,介面上會彈出正在獲取中的進度對話方塊,等待資料載入成功後,對話方塊消失。Textview顯示就新的天氣情況。
程式碼開發
因為選定MVP模式,所以第一步就是包的組織。
View層的介面定義及實現
在MVP中Activity用來專注檢視的表現。
而在本例子中View的表現有哪些呢?很多教程直接就上來貼程式碼,個人覺得這樣是不好的。View的表現當然要用View.interface介面來定義
現在我們來分析一下,在本例中View應該有哪些表現。
1.顯示天氣資訊
那好,介面方法可以這樣定義。
public void onInfoUpdate(String info);
2.顯示獲取資訊等待對話方塊
介面可以這樣寫
public void showWaitingDialog();
3.取消顯示對話方塊
public void dissmissWaitingDialog();
最終View.interface就完成了,非常簡單
public interface IWetherView {
public void onInfoUpdate(String info);
public void showWaitingDialog();
public void dissmissWaitingDialog();
}
介面檔案已經定義好了,那麼View的實現呢?在這裡用MainActivity去實現它。
public class MainActivity extends AppCompatActivity implements IWetherView{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void onInfoUpdate(String info) {
}
@Override
public void showWaitingDialog() {
}
@Override
public void dissmissWaitingDialog() {
}
}
具體的業務程式碼,我們等會再實現。
Model層的介面定義及實現
Model層是資料層,用來儲存資料並且提供資料。在這裡為了便於演示,資料被簡化為了String型別。
介面定義如下:
public interface IWetherModel {
//提供資料
public String getInfo();
//儲存資料
public void setInfo(String info);
}
它的實現檔案如下:
public class IWetherImpl implements IWetherModel {
@Override
public String getInfo() {
return null;
}
@Override
public void setInfo(String info) {
}
}
Presenter程式碼及實現
Presenter是個大忙人,因為要同時對View和Model對接,所以內部必須持有它們的介面引用。
所以有如下:
public class WetherPresenter {
IWetherModel mModel;
IWetherView mView;
}
Presenter與View的通訊
View—–>Presenter
從檢視介面出發,使用者要請求資料,而Presenter是具體實現者,所以Presenter要提供方法代View的實現者呼叫,並且View的實現中必須要有Presenter的引用。
所以MainActivity.java中要有WetherPresenter的引用。
public class MainActivity extends AppCompatActivity implements IWetherView{
......
WetherPresenter mPresenter;
......
}
而Presenter也要開發API供View呼叫。
所以Presenter要有requestWetherInfo()方法:
public class WetherPresenter {
IWetherModel mModel;
IWetherView mView;
//供View層呼叫,用來請求天氣資料
public void requestWetherInfo(){
}
}
presenter—–>View
presenter操作View,是通過View.interface,也就是View層定義的介面。
所以很容易得到下面的程式碼:
public class WetherPresenter {
......
private void showWaitingDialog(){
if (mView != null) {
mView.showWaitingDialog();
}
}
private void dissmissWaitingDialog(){
if (mView != null) {
mView.dissmissWaitingDialog();
}
}
private void updateWetherInfo(String info){
if (mView != null) {
mView.onInfoUpdate(info);
}
}
......
}
因為Presenter持有View的引用,所以在這裡要將View.interface注入到Presenter當中。
public class WetherPresenter {
IWetherModel mModel;
IWetherView mView;
......
public WetherPresenter(IWetherView mView) {
this.mView = mView;
}
......
}
Presenter與Model的通訊
Presenter與Model的通訊也是雙方的。
Presenter—->Model
presenter獲取到了資料,可以交給Model處理
private void saveInfo(String info){
mModel.setInfo(info);
}
Model—–>Presenter
Model處理完資料後它也能對Presenter提供資料。Presenter可以通過Model物件獲取本地資料。
WetherPresenter.java
private String localInfo(){
return mModel.getInfo();
}
Presenter程式碼實現
前面已經講了Presenter與Model,Presenter與View之間的通訊,現在就可以編寫程式碼將它們粘合起來。
Presenter本身需要向伺服器獲取程式碼,所以還要編寫它的相應方法:
public void requestWetherInfo(){
getNetworkInfo();;
}
private void getNetworkInfo(){
new Thread(new Runnable() {
@Override
public void run() {
try {
//開啟等待對話方塊
showWaitingDialog();
//模擬網路耗時
Thread.sleep(6000);
String info = "21度,晴轉多雲";
//儲存到Model層
saveInfo(info);
//從Model層獲取資料,為了演示效果,實際開發中根據情況需要。
String localinfo = localInfo();
//通知View層改變檢視
updateWetherInfo(localinfo);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//取消對話方塊
dissmissWaitingDialog();
}
}
}).start();
}
到此,完整的Presenter程式碼如下:
public class WetherPresenter {
IWetherModel mModel;
IWetherView mView;
public WetherPresenter(IWetherView mView) {
this.mView = mView;
mModel = new IWetherModelImpl();
}
public void requestWetherInfo(){
getNetworkInfo();;
}
private void showWaitingDialog(){
if (mView != null) {
mView.showWaitingDialog();
}
}
private void dissmissWaitingDialog(){
if (mView != null) {
mView.dissmissWaitingDialog();
}
}
private void updateWetherInfo(String info){
if (mView != null) {
mView.onInfoUpdate(info);
}
}
private void saveInfo(String info){
mModel.setInfo(info);
}
private String localInfo(){
return mModel.getInfo();
}
private void getNetworkInfo(){
new Thread(new Runnable() {
@Override
public void run() {
try {
//開啟等待對話方塊
showWaitingDialog();
//模擬網路耗時
Thread.sleep(6000);
String info = "21度,晴轉多雲";
//儲存到Model層
saveInfo(info);
//從Model層獲取資料,為了演示效果,實際開發中根據情況需要。
String localinfo = localInfo();
//通知View層改變檢視
updateWetherInfo(localinfo);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//取消對話方塊
dissmissWaitingDialog();
}
}
}).start();
}
}
MainActivity程式碼編寫
- 生成Presenter。這個在Activity中的onCreate方法中,並把自身當成IWetherView注入到presenter當中。
mPresenter = new WetherPresenter(this);
2 . 操作Presenter。當用戶點選按鈕時,通過呼叫mPresenter獲取資料,然後靜待更新。
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mPresenter.requestWetherInfo();
}
});
- View.interface回撥方法被觸發時,進行相應的檢視更新。
這裡主要的檢視有
- 顯示對話方塊
- 取消對話方塊
- 顯示 天氣資訊。
對應程式碼如下:
@Override
public void onInfoUpdate(final String info) {
Log.d(TAG, "onInfoUpdate: "+info);
runOnUiThread(new Runnable() {
@Override
public void run() {
mTvInfo.setText(info);
}
});
}
@Override
public void showWaitingDialog() {
runOnUiThread(new Runnable() {
@Override
public void run() {
if(mDialog != null && mDialog.isShowing()){
mDialog.dismiss();
}
mDialog = ProgressDialog.show(MainActivity.this,"","正在獲取中...");
}
});
}
@Override
public void dissmissWaitingDialog() {
runOnUiThread(new Runnable() {
@Override
public void run() {
if(mDialog != null && mDialog.isShowing()){
mDialog.dismiss();
}
}
});
}
所以整個MainActivity.java程式碼如下:
public class MainActivity extends AppCompatActivity implements IWetherView{
private static final String TAG = "MainActivity";
WetherPresenter mPresenter;
private TextView mTvInfo;
private Button mButton;
private ProgressDialog mDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPresenter = new WetherPresenter(this);
mTvInfo = (TextView) findViewById(R.id.tv_info);
mButton = (Button) findViewById(R.id.btn_request);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mPresenter.requestWetherInfo();
}
});
}
@Override
public void onInfoUpdate(final String info) {
Log.d(TAG, "onInfoUpdate: "+info);
runOnUiThread(new Runnable() {
@Override
public void run() {
mTvInfo.setText(info);
}
});
}
@Override
public void showWaitingDialog() {
runOnUiThread(new Runnable() {
@Override
public void run() {
if(mDialog != null && mDialog.isShowing()){
mDialog.dismiss();
}
mDialog = ProgressDialog.show(MainActivity.this,"","正在獲取中...");
}
});
}
@Override
public void dissmissWaitingDialog() {
runOnUiThread(new Runnable() {
@Override
public void run() {
if(mDialog != null && mDialog.isShowing()){
mDialog.dismiss();
}
}
});
}
}
效果如上面的:
總結
mvp非常適合大型的APP開發,越複雜它的優勢越明顯,但是如果APP程式碼本身很簡明,mvp就有點繞彎子的感覺了。
附錄
相關推薦
Android中MVP模式講解及實踐
前兩年的時候,我經常逛http://androidweekly.net這個網站,上面就有過很多文章介紹MVP模式,我很感興趣,於是把這個東西介紹給身邊的同事,同事們好像沒有多大反應,可能是當時在國內MVP用的範圍還比較少吧。後來我換了工作,再後來某一天我發
Android的MVP模式講解
UI層越來越複雜,為了減輕了UI層的責任,也是為了更好地細分檢視(View)與模型(Model)的功能,讓View專注於處理數 據的視覺化以及與使用者的互動,讓Model只關係資料的處理,MVP(Model-View-Presenter)模式
關於Android中通知欄的講解及適配Android 8.0
前言 通知欄是Android系統原創的一個功能,雖然喬布斯一直認為Android系統是徹徹底底抄襲IOS的一個產品,但是通知欄確實是Android系統原創的,反而蘋果在ios 5之後也加入了類似的通知欄功能。 通知欄的設計非常巧妙,他預設情況下不佔用任何空間,只有當用戶需要的時候用手
淺談Android中MVP模式與MVC模式的區別
一、概述 對於MVP(Model View Presenter),大多數人都能說出一二:“MVC的演化版本”,“讓Model和View完全解耦”等等。本篇博文僅是為了做下記錄,提出一些自己的看法,和幫助大家如何針對一個Activity頁面去編寫針對MVP風
[Android學習]Android中MVP模式初探1
前言: 1. 初識MVP模式時,看到它缺點是需要增加一倍的類的維護量。所以就暫時沒用它。但是,當一個類的程式碼行數達到一定的量(1000行以上),這時候維護類變得好麻煩,主要是功能變得多了,方法數量也變多了。這個時候真的是需要給類“瘦瘦身”。
Android開發中MVP模式
傳統的開發模式mvc大家都很熟悉。 View負責頁面展示,Model負責資料。 Controller一個控制協調前兩者的關係,很常見,耦合關係也很明顯。 在常見的android應用開發中Activity類可以是非常複雜的程式碼集合,裡面有各種view,事件,網路請求,資料bean。
對Android中設計模式MVC,MVP,MVVM的簡單理解
設計模式VS框架框架是程式碼的重用,可擴充套件。舉幾個簡單的例子。Spring架構,Struts架構。設計模式是設計的重用,是一種抽象的設計方法。例如MVC,MVP,MVVM。下面,我們以android開發為例,簡單比較一下三種不同的設計模式。MVCMVC是指Modle,Vi
Android開發中MVP模式淺析
目前為止,MVP的使用還沒有一個標準,在此先記錄一下目前學習到的一些Android中使用MVP的知識。 按傳統的方式開發,經常會使Activity中混雜著UI互動,業務邏輯等流程。而MVP模式能巧妙的解決這個問題。先直接上一個小例子吧。 /** * 定義一個對UI元件
nginxlocation重要語法講解及實踐檢驗
linuxnginx常見日誌收集及分析工具有rsyslog,awstats,flume,elk,storm等1 nginx location作用: location指令的作用是可以根據用戶請求的URL來執行不同的應用,就是根據用戶請求的網站地址URL匹配,匹配成功即進行相關的操作。2 loc
Android XListView實現原理講解及分析
就是 指定 不同 true -h -name 修改 一個 部分 XListview是一個非常受歡迎的下拉刷新控件,但是已經停止維護了。之前寫過一篇XListview的使用介紹,用起來非常簡單,這兩天放假無聊,研究了下XListview的實現原理,學到了很多,今天分享給大家。
細數android中MVP的“七宗罪”
前言 我們都知道,MVP是在MVC的基礎上做了一次升級,相比MVC,MVP中P層與V層隔離,V層只負責UI,業務邏輯由抽象出來的P層負責,真正意義上的隔離View的細節和複雜性的模式....... 好了好了,上面是網上MVP“鼓吹”的基本套路。“欲想讓其滅亡,必先使其膨脹”今天,反其道而行之
23中設計模式概括及六種設計原則(一)
一、設計模式分類 總體來說模式依據目的可分為建立型模式(Creational)、結構型模式(Structural)、行為型模式(Behavioral)三種。 建立型模式:處理物件的建立。共5種:工廠方法模式(Factory Method)、抽象工廠模式(Abstract Factory)、建造者模式(Bu
Android中夜間模式的三種實現方式
參考:https://www.jianshu.com/p/f3aaed57fa15 在本篇文章中給出了三種實現日間/夜間模式切換的方案: 使用 setTheme 的方法讓 Activity 重新設定主題; 設定 Android Support Library 中的 UiMode
Android GPU呈現模式原理及卡頓掉幀淺析
APP開發中,卡頓絕對優化的大頭,Google為了幫助開發者更好的定位問題,提供了不少工具,如Systrace、GPU呈現模式分析工具、Android Studio自帶的CPU Profiler等,主要是輔助定位哪段程式碼、哪塊邏輯比較耗時,影響UI渲染,導致了卡頓。拿Profile GPU Renderin
Android中設計模式-Builder模式
遇到多個構造器引數時要考慮使用構建器 引入:當我們建立物件傳遞引數的時候,往往通過構造方法來傳,如下程式碼: public class Person { private String name; private String id;
Android中APK安裝過程及原理解析
應用安裝是智慧機的主要特點,即使用者可以把各種應用(如遊戲等)安裝到手機上,並可以對其進行解除安裝等管理操作。APK是Android Package的縮寫,即Android安裝包。APK是類似Symbian Sis或Sisx的檔案格式。通過將APK檔案直接傳到Android模擬器或Android手機中執行即可
Android開發MVP模式--專案實戰
1 前言 蘇寧+App是蘇寧易購集團零售雲研發中心分銷研發中心主要產品之一,由於專案處於初期階段,業務邏輯複雜,導致業務需求變動快,常常在開發甚至測試過程中出現介面或者後臺介面調整的情況。 App客戶端如何在外部需求不斷變化的情況下,降低模組耦合,儘可能減少
Android中設計模式--狀態模式(將動作委託到當前狀態,狀態之間可以互相轉換)
狀態模式:將狀態封裝成為獨立類,並將動作委託到當前狀態;狀態之間可以相互轉換,因為實現了相同的介面;狀態改變,則動作會跟著改變。 理解: 1.定義狀態介面,所有的狀態均實現該介面,這樣對於客戶(呼叫者)來說,狀態是可以替換的,客戶不關心具體的狀態是什麼,只是呼叫介
Android中Sqlite的使用及效能優化
使用 可以使用安卓原生的工具類: SQLiteOpenHelper 抽象類:通過從此類繼承實現使用者類,來提供資料庫開啟、關閉等操作函式。 SQLiteDatabase 資料庫訪問類:執行對資料庫的插入記錄、查詢記錄等操作。 SQLiteCursor 查詢結構操作類:用來訪問查詢結果中的記錄
Android中的快取處理及非同步載入圖片類的封裝
一、快取介紹: (一)、Android中快取的必要性: 智慧手機的快取管理應用非常的普遍和需要,是提高使用者體驗的有效手段之一。 1、沒有快取的弊端: 流量開銷:對於客戶端——伺服器端應用,從遠端獲取圖片算是經常要用的一個功能,而圖片資源往往會消耗比較大的流量。 載入速