android MVP模式介紹與實戰

来源:http://www.cnblogs.com/daheihei/archive/2016/06/22/5608053.html
-Advertisement-
Play Games

**MVP模式結構** - Model: 業務邏輯和實體模型 - View:用戶交互和視圖顯示,在android中對應activity - Presenter: 負責完成View於Model間的邏輯和交互 ...


android MVP模式介紹與實戰

描述


MVP模式是什麼?MVP 是從經典的模式MVC演變而來,它們的基本思想有相通的地方:Controller/Presenter負責邏輯的處理,Model提供數據,View負責顯示。

MVC和MVP的區別?


為什麼會出現MVP模式呢?這是因為原有的MVC模式有一些短板。比如在android開發中,activity充當著MVC中Controller的角色,但是在實際開發中處理view的邏輯和角色。當業務界面複雜時我的activity會顯得很龐大。於是出現了MVP模式,它新增了一個Presenter角色用於處理數據和界面的模型以及邏輯,Activity僅僅用於展示界面和用戶交互,這樣就解決了MVC中角色不清的局面。
所以,MVP與MVC的重大區別:在MVP中View並不直接使用Model,它們之間的通信是通過Presenter (MVC中的Controller)來進行的,所有的交互都發生在Presenter內部,而在MVC中View會直接從Model中讀取數據而不是通過 Controller。
在MVC里,View是可以直接訪問Model的!從而,View里會包含Model信息,不可避免的還要包括一些業務邏輯。 在MVC模型里,更關註的Model的不變,而同時有多個對Model的不同顯示,即View。所以,在MVC模型里,Model不依賴於View,但是View是依賴於Model的。不僅如此,因為有一些業務邏輯在View里實現了,導致要更改View也是比較困難的,至少那些業務邏輯是無法重用的。

MVC模式結構

  • Model 業務邏輯和實體模型
  • Controller 對應Activity
  • View 視圖以及佈局文件

Alt text

MVP模式結構

  • Model: 業務邏輯和實體模型
  • View:用戶交互和視圖顯示,在android中對應activity
  • Presenter: 負責完成View於Model間的邏輯和交互

小節:MVP模式相當於在MVC模式中又加了一個Presenter用於處理模型和邏輯,將View和Model完全獨立開,在android開發中的體現就是activity僅用於顯示界面和交互,activity不參與模型結構和邏輯,

 實戰

谷歌官網給了我們一個MVP模式實戰的例子,它是一個類似記事本的app,源碼地址在:https://github.com/googlesamples/android-architecture
官方案例的框架圖如下:
enter image description here

看完源碼後發現其不適合初學者理解,於是我自己寫了一個demo方便大家理解。
demo源碼地址:https://github.com/halibobo/AndroidMvpExample
源碼主要類的結構如下

Alt text

下麵來看源碼
View層對應的是MainActivity,它繼承了抽離出View所有操作方法的介面OperationView

/**
 * *Created by su on 2016/6/22.
 */
    public interface OperationView {

    void showCreatingPhone();

    void showPhoneCountChange();

    void showNoPhone();

    void showFactoryBusy();

    void showCreatedPhone();
}

MainActivity具體對每個操作進行了具體的實現

    public class MainActivity extends AppCompatActivity implements OperationView {

    private ListView listView;

    private Button btnCreate;

    private PhonePresenter phonePresenter;

    private ProgressDialog mLoadingDialog;
    ArrayAdapter<Phone> arrayAdapter;
    private Toast toast;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        PhoneFactory phoneFactory = new PhoneFactory();
        phoneFactory.createPhone("nokia",555);
        phonePresenter = new PhonePresenter(phoneFactory, this);
        btnCreate = (Button) findViewById(R.id.btnCreate);
        listView = (ListView) findViewById(R.id.listView);
        arrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, phoneFactory.getPhonesList());
        btnCreate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                phonePresenter.addPhone(new Phone("iphone", 4000+ new Random().nextInt(1000)));
            }
        });

        listView.setAdapter(arrayAdapter);

        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                phonePresenter.removePhone(position);
            }
        });

    }

    @Override
    public void showCreatingPhone() {
        mLoadingDialog = new ProgressDialog(this);
        mLoadingDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        mLoadingDialog.setMessage("工廠正在生產手機");
        mLoadingDialog.setCancelable(true);
        mLoadingDialog.show();
    }

    @Override
    public void showPhoneCountChange() {
        arrayAdapter.notifyDataSetChanged();
    }

    @Override
    public void showNoPhone() {
        findViewById(R.id.noPhone).setVisibility(View.VISIBLE);
    }

    @Override
    public void showFactoryBusy() {
        showToast("工廠繁忙,請稍後再試!");
    }

    @Override
    public void showCreatedPhone() {
        if (mLoadingDialog != null && mLoadingDialog.isShowing()) {
            mLoadingDialog.dismiss();
        }
        mLoadingDialog = null;
        showToast("新生產出一臺手機!");
        findViewById(R.id.noPhone).setVisibility(View.GONE);
    }

    private void showToast(String string) {
        if (toast == null) {
            toast = Toast.makeText(this, string, Toast.LENGTH_SHORT);
        }else{
            toast.setText(string);
        }
        toast.show();
    }
}

Model層對應的是PhoneFactory,它處理和數據相關的一些簡單操作

     /**
     * Created by su on 2016/6/22.
     */
    
    /**
     * 手機工廠類
     */

    public class PhoneFactory {
        private ArrayList<Phone> phonesList = new ArrayList<>();
    
    
        public void addPhone(Phone phone) {
            phonesList.add(phone);
        }
    
        public void removePhone(Phone phone) {
            phonesList.remove(phone);
        }
    
        public void removePhone(int index) {
            if (index >= 0 && index < phonesList.size()) {
                phonesList.remove(index);
            }
        }
    
        public void createPhone(String name, double price) {
            Phone phone = new Phone(name, price);
            phonesList.add(phone);
        }
    
        public ArrayList<Phone> getPhonesList() {
            return phonesList;
        }
    
        public int getPhoneCounts() {
            return phonesList.size();
        }
    }

下麵是最為重要的Presenter層 對應代碼中的PhonePresenter,它處理界面邏輯和數據模型等,源碼如下:

public class PhonePresenter implements TaskPresenter{

    private final PhoneFactory phoneFactory;
    private final OperationView operationView;

    private static final long createPhoneTime = 2000;
    private static final int msgWhat = 0x102;


    public PhonePresenter(@NonNull PhoneFactory phoneFactory, @NonNull OperationView operationView) {
        this.phoneFactory = phoneFactory;
        this.operationView = operationView;
    }

    @Override
    public void addPhone(Phone phone) {
        operationView.showPhoneCountChange();
        if (mHandler.hasMessages(msgWhat)) {
            operationView.showFactoryBusy();
            return;
        }
        Message message = new Message();
        message.what = msgWhat;
        message.obj = phone;
        mHandler.sendMessageDelayed(message, createPhoneTime);
        operationView.showCreatingPhone();
    }

    @Override
    public void removePhone(int index) {
        phoneFactory.removePhone(index);
        if (phoneFactory.getPhoneCounts() <= 0) {
            operationView.showNoPhone();
        }
        operationView.showPhoneCountChange();
    }

    @Override
    public void removePhone(Phone phone) {

    }

    public ArrayList<Phone> getPhones() {
        ArrayList<Phone> phones = phoneFactory.getPhonesList();
        if (phones.isEmpty()) {
            operationView.showNoPhone();
        }
        return phones;
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            phoneFactory.addPhone((Phone)msg.obj);
            operationView.showCreatedPhone();
            operationView.showPhoneCountChange();
        }
    };
}

運行效果圖如下:

Alt text

總結

使用MVP模式會使得代碼多出一些介面但是使得代碼邏輯更加清晰,尤其是在處理複雜界面和邏輯時,我們可以對同一個activity將每一個業務都抽離成一個Presenter,這樣代碼既清晰邏輯明確又方便我們擴展。當然如果我們的業務邏輯本身就比較簡單的話使用MVP模式就顯得,沒那麼必要。所以我們不需要為了用它而用它,具體的還是要要業務需要

謝謝大家

demo地址在:https://github.com/halibobo/AndroidMvpExample


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這是ZK學習筆記的下篇, 主要希望可以分享一些 ZK 的應用以及其應用原理 我本人的學習告一段落, 不過還遺留了一些ZK相關的任務開發和性能測試的任務, 留待以後完成之後再通過其他文章來進行分享了 ZK應用場景: 1. 一個服務多台機器, 快速修改配置 2. 服務的消費者如何動態知道服務有多少個提供 ...
  • 因為 PHP 那“集百家之長”的蛋疼語法,加上社區氛圍不好,很多人對新版本,新特征並無興趣。本文將會介紹自 PHP5.2 起,直至 PHP5.6 中增加的新特征 本文目錄:PHP5.2 以前:autoload, PDO 和 MySQLi, 類型約束PHP5.2:JSON 支持PHP5.3:棄用的功能 ...
  • 這篇文章主要介紹了C++實現郵件群發的方法,較為詳細的分析了郵件發送的原理與C++相關實現技巧,非常具有實用價值,需要的朋友可以參考下 本文實例講述了C++實現郵件群發的方法。分享給大家供大家參考。具體如下:關於生成隨機QQ郵箱不精確的問題,在之後版本打算另寫一個採集器插件進行帳號採集,所以,這個軟 ...
  • 今天又看了下Hangout的源碼,一般來說一個開源項目有好幾種啟動方式——比如可以從命令行啟動,也可以從web端啟動。今天就看看如何設計命令行啟動... Apache Commons CLI Apache Commons CLI是開源的命令行解析工具,它可以幫助開發者快速構建啟動命令,並且幫助你組織 ...
  • 在牛客網上刷題的時候看見這麼一道題 這很明顯是對我對父子執行流的嚴峻考察,很好,我喜歡,然後我錯了。。。騰訊實習生招聘的時候線上筆試題也有類似的題目,不過是把-換成可hello world而已,可見還是蠻重要的。這種題目只要畫一下圖很快就能理解了 顯而易見的6次 貼出結果 那麼接下來來個升級版的 執 ...
  • 在項目中我們有時候會為集合類型設定一些預設的值,使用spring後,我們可以通過配置文件的配置,用setter方式為對象的集合屬性提供一些預設值,下麵就是一個簡單的例子。 首先我們創建了一個名為Collection的類,這個類中包含四中基本的集合屬性,實現屬性的set方法和覆蓋toString()方 ...
  • 參考http://blog.csdn.net/yaopeng_2005/article/details/6935235 對小鵬_加油的代碼進行了部分修改,並加入了自己的文檔註釋 定義全局變數,以及主函數main 初始化變數Init函數 銀行家演算法Bank函數 安全性演算法Safe函數 顯示showda ...
  • 最近新做的一個項目需要使用qt5連接另一臺機器上的sql server,雖然網上已有類似文章,但還是有些其中很少提及的問題,故在這裡彙總下: qt連接sql server可以參考這篇文章: 《Qt 使用ODBC driver 連接SQL Server》 如果是連接另一臺機器的sql server就不 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...