最近經過《Android源碼設計模式解析與實戰》對MVP應用架構進行了瞭解,摘其重點記錄於此。數據庫
MVP模式全稱Model View Presenter。能:
1.有效地下降View複雜性,避免業務邏輯被塞進View中;
2.解除View與Model的耦合,保證了系統的整潔性、靈活性。設計模式
理想化的MVP模式能夠實現同一份邏輯代碼搭配不一樣的顯示頁面,由於它們之間並不依賴於具體(class),而是依賴於抽象(interface)。
MVP並非一個標準化的模式,咱們可根據本身的需求和本身認爲對的方式去修正MVP的實現方式,它能夠隨着Presenter的複雜程度變化。
只要咱們是經過Presenter將View和Model解耦合、下降類型複雜度、各個模塊能夠獨立測試、獨立變化,這就是正確的方向。緩存
下面經過一個簡單的客戶端示例來直觀地體會MVP在開發中的運用。咱們的UI原型大體以下圖所示:主界面就是一個ListView和進度條,在加載數據時顯示進度條,加載完成以後隱藏。服務器
進入應用後首先會從服務器下載最新的20篇文章,而後將每一篇文章的簡介顯示到列表上。所以,咱們的業務邏輯大概有下列2個:網絡
關鍵類以下:架構
/** *數據加載監聽 */ public interface DataListener<T> { //泛型是數據集合的類型 public void onComplete(T articles); } /** *遠程數據獲取接口(remote倉庫接口) */ public interface RemoteRepository{ /** *遠程抓取數據 */ public void fetchArticles(DataListener<List<String>> listener); } /** *遠程數據獲取實現(remote倉庫實現) */ public class RemoteRepositoryImpl implements RemoteRepository{ @Override public void fetchArticles(DataListener<List<String>> listener) { List<String> articles = new ArrayList<>(); for (int i = 0; i < 20; i++) { articles.add("文章" + i); } listener.onComplete(articles); } } /** *數據層抽象(Model層接口、倉庫層接口) */ public interface ArticleModel { /** *調用遠程接口獲取文章列表 */ public void loadArticlesFromRemote(DataListener<List<String>> listener); /** *緩存網絡數據至本地 */ public void saveArticles(List<String> articles); /** *從緩存中加載數據 *@param listener 該參數由ModelImpl調用,由Presenter負責實現,用於解耦。 */ public void loadArticlesFromCache(DataListener<List<String>> listener); } /** *數據層實現(Model層實現、倉庫層實現) */ public class ArticleModelImpl implements ArticleModel { /** *持有local倉庫的引用 */ List<String> mCacheArticles = new LinkedList<String>(); /** *持有remote倉庫的引用 */ RemoteRepository remote= new RemoteRepositorympl(); @Override public void loadArticlesFromRemote(DataListener<List<String>> listener) { remote.fetchArticles(listener); } @Override public void saveArticles(List<String> articles) { //爲了代碼簡單,咱們這裏模擬數據庫的存取操做,將數據緩存到內存中 mCacheArticles.addAll(articles); } @Override public void loadArticlesFromCache(DataListener<List<String>> listener) { listener.onComplete(mCacheArticles); } } /** *Presenter層 */ public class ArticlePresenter { //Presenter層持有View層的接口 ArticleViewInterface mArticleView; //Presenter層持有Model層的接口 ArticleModel mArticleModel; //構造器,給持有的View層接口賦以具體的實現,進而能夠調用View層的方法 //同時實例化成員變量 public ArticlePresenter(ArticleViewInterface viewInterface) { mArticleView = viewInterface; mArticleModel = new ArticleModelImpl(); } /** *業務邏輯1:獲取遠程數據 */ public void fetchArticles() { mArticleView.showLoading(); mArticleModel .loadArticlesFromRemote(new DataListener<List<String>>() { @Override public void onComplete(List<String> result) { //數據加載完,調用View的showArticles函數將數據傳遞爲View顯示 mArticleView.showAricles(result); mArticleView.hideLoading(); //儲存到數據庫 mArticleModel.saveArticles(result); } }); } /** *業務邏輯2:獲取緩存數據: */ public void loadArticlesFromDB(){ mArticleModel.loadArticlesFromCache(new DataListener<List<String>>() { @Override public void onComplete(List<String> result) { mArticleView.showAricles(result); } }); } } /** *View層接口 */ public interface ArticleViewInterface { /** *展現數據 */ public void showAricles(List<String> articles); /** *顯示進度條 */ public void showLoading(); /** *隱藏進度條 */ public void hideLoading(); } /** *View層實現 */ public class MvpHomeActivity extends Activity implements ArticleViewInterface { private ListView lv; private ProgressBar pb; private List<String> mArticles = new LinkedList<String>(); private BaseAdapter mAdapter; //View層實現持有Presenter的引用 private ArticlePresenter mPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_mvp_home); //初始化各類控件 initViews(); //構建ArticlePresenter,與Activity關聯 mPresenter = new ArticlePresenter(this); boolean netAvailable = true; if (netAvailable) { mPresenter.fetchArticles(); }else{ mPresenter.loadArticlesFromDB(); } } private void initViews() { pb = (ProgressBar) findViewById(R.id.pb); lv = (ListView) findViewById(R.id.lv); mAdapter = new CommonAdapter<String>(this,R.layout.item_layout,mArticles) { @Override protected void convert(ViewHolder viewHolder, String item, int position) { viewHolder.setText(R.id.text1,item); } }; lv.setAdapter(mAdapter); } @Override public void showAricles(List<String> articles) { mArticles.addAll(articles); mAdapter.notifyDataSetChanged(); } @Override public void showLoading() { pb.setVisibility(View.VISIBLE); } @Override public void hideLoading() { pb.setVisibility(View.GONE); } }
須要注意的是,上述代碼中對於ArticlePresenter
並無進行接口抽象,而是使用了具體類,由於業務邏輯相對穩定,在此咱們直接使用具體類便可。固然,若是業務邏輯相對來講易於變化,使用Presenter
接口來應對更是最好不過了。ide
同時,因爲Presenter常常性地須要執行一些耗時操做,如網絡請求。而Presenter
持有了MvpHomeActivity
的強引用,若是在請求結束以前Activity被銷燬了,那麼因爲網絡請求尚未返回,致使Presenter
一直持有MvpHomeActivity
對象,使得MvpHomeActivity
對象沒法被回收,此時就發生了內存泄漏。函數
爲了解決上述問題,能夠經過弱引用和Activity,Fragment的生命週期來解決這個問題。首先創建一個Presenter抽象,它是一個泛型類,泛型類型爲View角色要實現的接口類型,代碼以下:測試
public abstract class BasePresenter<T> { protected Reference<T> mViewRef;//View接口類型的弱引用 public void attachView(T view){ mViewRef = new WeakReference<T>(view);//創建關聯 } //獲取View protected T getView(){ return mViewRef.get(); } //判斷是否與View創建了關聯 public boolean isViewAttached(){ return mViewRef!=null&&mViewRef.get()!=null; } //解除關聯 public void detachView(){ if(mViewRef!=null){ mViewRef.clear(); mViewRef = null; } } }
而後,建立一個MVPBaseActivity
基類,經過這個基類的生命週期來控制它和Presenter的關係,代碼以下:fetch
public abstract class MVPBaseActivity<V, P extends BasePresenter<V>> extends Activity { /** *Presenter對象 */ protected P mPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //建立Presenter mPresenter = createPresenter(); mPresenter.attachView((V) this); } @Override protected void onDestroy() { super.onDestroy(); /** *弱引用防止內存溢出 */ mPresenter.detachView(); } /** *子類負責提供具體的Presenter對象 */ protected abstract P createPresenter(); }
MVPBaseActivity
含有兩個泛型參數,第一個是View層接口,第二個是Presenter的具體類型。經過泛型參數,使得一些通用的邏輯能夠被抽象到MVPBaseActivity
類中。