Android中的MVP模式使用

jopen 9年前發布 | 10K 次閱讀 Android開發 移動開發

         在講MVP 之前,我們先來簡單說下什么是MVC, 即Model(模型)、View(視圖)、Control(控制器),相信大家對于MVC模式早已耳熟能詳。原理性的東西這里不再多說。MVC在AndroidApp里面就有很好的體現。因為對于Android本身來說,界面部分的開發一般會用XML文件進行界面的描述開發。也就是MVC中的View層。而對于Model部分則大多是對應本地數據文件的讀取或從網絡獲取數據。最后的Control控制器則有Activity來擔當。MVC就說到這里,相信大多數Android猿們也是基于這種模式進行開發的。接下來,我們就來談一談今天的主角MVP模式在Android中又是如何實現的。

         何為MVP呢?即Model,VIew,Presenter(交互中間人相當于Control) 。但這里的Presenter隔離了Model和View之間的通信,使得Model和View之間能夠完全解耦。在傳統的MVC中,Activity擔當了Control的角色,同時還要負責Dialog,Toast,PopupWindow的彈出,這就往往讓Activity的責任變得繁重。一個Activity可能動不動就是幾千行代碼。而在MVP中View的角色不僅僅只是XML,而變成了Activity,Activity只負責View的工作。結合上面說的MVP能使Model和View之間能完全解耦,也就是說,在Activity中不會有任何關于Model層的操作,這就符合面向對象設計原則中的單一職責,讓各個模塊只負責自己的部分。更易于維護,代碼也會更加簡介。下面我會以一個小的項目來演示MVP在Android中如何使用。我在今日頭天的web頁拿到了一個新聞的URL,獲取Json數據,解析顯示

Bean

package himan.mvp.bean;

import java.io.Serializable;

public class NewsInfo implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private String datetime;
    private int id;
    private String title;
    private String imageUrl;
    private String abstractInfo;

    public String getDatetime() {
        return datetime;
    }

    public void setDatetime(String datetime) {
        this.datetime = datetime;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }

    public String getAbstractInfo() {
        return abstractInfo;
    }

    public void setAbstractInfo(String abstractInfo) {
        this.abstractInfo = abstractInfo;
    }

    public NewsInfo(String datetime, int id, String title, String imageUrl,
            String abstractInfo) {
        super();
        this.datetime = datetime;
        this.id = id;
        this.title = title;
        this.imageUrl = imageUrl;
        this.abstractInfo = abstractInfo;
    }

    public NewsInfo() {
    }

}

Model層的接口:還記得前兩篇帖子講的面向對象的六大設計模式精髓:面向接口編程,依賴于抽象

package himan.mvp.model;

import himan.mvp.bean.NewsInfo;

import java.util.List;

public interface INewsModel {

    /**
     * 加載數據
     * 
     * @param dataListener
     */
    void loadNews(IOnDataListener<List<NewsInfo>> dataListener);

    /**
     * 緩存數據
     * 
     * @param listNews
     */
    void saveNews(List<NewsInfo> listNews);
}

Model實現類

package himan.mvp.modelimpl;

import java.util.List;

import himan.mvp.bean.NewsInfo;
import himan.mvp.model.INewsModel;
import himan.mvp.model.IOnDataListener;
import himan.mvp.net.NewsNetHelper;
import himan.mvp.net.volley.UIDataListener;

public class NewsModelImpl implements INewsModel {

    @Override
    public void loadNews(final IOnDataListener<List<NewsInfo>> dataListener) {  // 回調接口
        NewsNetHelper netHelper = new NewsNetHelper();
        netHelper.setDataListener(new UIDataListener<List<NewsInfo>>() {

            @Override
            public void onDataChanged(List<NewsInfo> data) {
                if (data == null) {
                    dataListener.error();
                } else {
                    dataListener.completeLoad(data);
                }
            }
        });
        netHelper
                .doHttpGet("http://toutiao.com/api/article/recent/?source=2&count=20&category=__all__&max_behot_time=1449467901.41&utm_source=toutiao&offset=0&_=1449468004256");
    }

    @Override
    public void saveNews(List<NewsInfo> listNews) {
        // 緩存數據到本地
    }

}

View層接口:依賴于抽象編程

package himan.mvp.view;

import himan.mvp.bean.NewsInfo;

import java.util.List;

public interface INewsView {
    /**
     * 顯示新聞列表
     * 
     * @param listNews
     */
    public void showNewsList(List<NewsInfo> listNews);

    /**
     * 顯示正在加載中進度條
     */
    public void showLoading();

    /**
     * 關閉正在加載中進度條
     */
    public void hideLoading();

    /**
     * 顯示數據加載失敗
     */
    public void showError();

}

MVP關鍵之處:中間人Presenter

package himan.mvp.presenter;

import java.util.List;

import himan.mvp.bean.NewsInfo;
import himan.mvp.model.INewsModel;
import himan.mvp.model.IOnDataListener;
import himan.mvp.modelimpl.NewsModelImpl;
import himan.mvp.view.INewsView;

public class NewsPresenter {
         // 同時持有Model層和View層的引用
    private INewsView mNewsView;
    private INewsModel mNewsModel = new NewsModelImpl();

    public NewsPresenter(INewsView newsView) {
        this.mNewsView = newsView;
    }
    public void showNews() {
        mNewsView.showLoading();
        mNewsModel.loadNews(new IOnDataListener<List<NewsInfo>>() {

            @Override
            public void error() {
                // 加載失敗
                mNewsView.hideLoading();
                mNewsView.showError();
            }

            @Override
            public void completeLoad(List<NewsInfo> t) {
                mNewsView.hideLoading();
                mNewsView.showNewsList(t);
            }
        });
    }

}

Activity實現了View接口

package himan.mvp;

import himan.mvp.adapter.NewsAdapter;
import himan.mvp.bean.NewsInfo;
import himan.mvp.net.volley.VolleyQueueController;
import himan.mvp.presenter.NewsPresenter;
import himan.mvp.utils.LoadingWindow;
import himan.mvp.view.INewsView;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;
import android.widget.Toast;

import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;

/**
 * 測試 加載的數據 是從今日頭條官網的直接push下來的
 * 
 * @author Mr.Himan
 * 
 */
public class MainActivity extends Activity implements INewsView {

    private ListView mlVNews;

    private NewsAdapter mNewsAdapter;

    private List<NewsInfo> mListNews;

    private NewsPresenter mNewsPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 初始化Volley框架
        VolleyQueueController.init(this);
        // ImageLoader 默認配置參數
        ImageLoaderConfiguration configuration = ImageLoaderConfiguration
                .createDefault(this);
        // 初始化ImageLoader
        ImageLoader.getInstance().init(configuration);
        initView();
        mNewsPresenter = new NewsPresenter(this);
        mNewsPresenter.showNews();

    }

    private void initView() {
        mlVNews = (ListView) findViewById(R.id.news_lv_news_list);
        mListNews = new ArrayList<NewsInfo>();
        mNewsAdapter = new NewsAdapter(this, mListNews, R.layout.lv_item_news);
        mlVNews.setAdapter(mNewsAdapter);
    }

    @Override
    public void showNewsList(List<NewsInfo> listNews) {
        mListNews.addAll(listNews);
        mNewsAdapter.notifyDataSetChanged();
    }

    @Override
    public void showLoading() {
        Toast.makeText(this, "正在加載數據...", Toast.LENGTH_LONG).show();
    }

    @Override
    public void hideLoading() {
        Toast.makeText(this, "數據加載成功", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showError() {

    }

}

這就是MVP實現一個新聞展示的全部代碼(貌似頭條的接口關掉了 - -),Activity實現了View接口,而Presenter中保存有View和Model層的引用,使得Presenter能通過Model拿到數據,通過View的引用控制視圖的顯示。流程大概是這樣,Presenter先通過Model的引用拿到數據,然后通過View的引用進行數據的顯示,視圖更新。Activity中的代碼也非常簡介。通過上面代碼我們能夠看出MVP的優點還是很多的,首先使得Model和View層完全解耦,我們知道項目中改動最多的一般都是View層,這就讓程序易于維護,而且能夠高度復用Presenter,而且也易于后期的拓展。缺點也顯而易見,就是爆炸式增長的類和接口。復用的時候也可能造成接口的冗余。最大的問題是Presenter持有著View的強應用,在請求網絡數據等耗時操作的時候,Activity可能被銷毀,可能會導致View無法回收,而造成內存問題。關于這個問題,我會在下一篇MVP的帖子中講解如何處理,歡迎關注





        

來自: http://blog.csdn.net/soul_code/article/details/50207857

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!