本文來自於騰訊Bugly公衆號(weixinBugly),未經做者贊成,請勿轉載,原文地址:https://mp.weixin.qq.com/s/M45DM5Ix7a2fmrsE8VPvxgjava
做者:bizaitanweb
導語:MVP開發模式能夠幫助項目結構解耦,但其龐大的方法數增長,較爲笨重設計對於手Q項目並不很適合。參考以前Web開發經驗,提出以頁面結構化的解耦方式組織代碼。下面講講Lego在Android上一次小小嚐試設計模式
MVC太過常見這裏不囉嗦。實際應用MVC當中,Activity佔據打部分的工做,View和Controller的身份分不清。而MVP則是一種設計模式專門優化Activity / Fragment。緩存
先來看看MVP模式的核心思想:View不直接與Model交互性能優化
MVP 把 Activity 中的 UI 邏輯抽象成 View 接口,把業務邏輯抽象成 Presenter 接口,Model 類仍是原來的 Model微信
在MVP設計模式中,網絡
【Mode層】咱們直接忽略ide
【View Interface】首頁的View接口,抽離出view和presnter交互的接口。由Activity繼承實現(Now.java,QQStoryMainActivity.java)工具
public interface IMyStoryListView { public void setData(MyStorys myStoryList, RecentStory recentStoryList); public void setSegmentData(String key, Object data,boolean needRefreshUi); /** * 更新數據後刷新界面走的回調 * @param success * @param isManualPullRefresh */ public void pullRefreshCompleted(boolean success,boolean isReqCompleted); public void launchNewVideoTakeActivity(boolean autoStart, boolean checkSo, int entranceType,String extra); public void setPlayVideoBtnDisplay(boolean display); public void showStartDownload(); public void showDownloadCompleted(boolean success); public void storyPreLoadCompleted(String category, String uin); public void LoadMoreCompleted(boolean repositoryUpdated, boolean isEnd); public void showEmptyView(boolean display); public void requestDataCompleted(); public void openMyStoryListView(boolean open); }
【View】咱們的Activity實現了View接口,而且實現生命週期組件化
public class QQStoryMainAcitivty extends QQStoryBaseActivity implements IMyStoryListView { protected StoryHomePushYellowBarHandler mStoryHomePushYellowBarHandler = new StoryHomePushYellowBarHandler(); protected MystoryListView mainListView; protected IMyStroyPresenter myStoryListPresenter; @Override protected boolean doOnCreate(Bundle savedInstanceState) { super.doOnCreate(savedInstanceState); mainListView = (MystoryListView) super.findViewById(R.id.qqstory_story_main_listview); //Presenter myStoryListPresenter = new StoryListPresenter(this); myStoryListPresenter.setIView(this); return true; } @Override public void onStartAutoRequestFromNet() { startTitleProgress(); mainListView.pullToRefresh(); mStoryHomePushYellowBarHandler.clearYellowBar(); myStoryListPresenter.requestAllDataFromNet(); } private void startTitleProgress(){ // do more } }
舉個例子,用戶下拉刷新一下。觸發到Activity的onStartAutoRequestFromeNet。View邏輯在Activity。
業務邏輯則由Presnter的requestAllDataFromNet去實現。
【Presenter】具體的View->Model,Mode->View由這裏實現,其中View是有View接口抽象,進一步規範化View的邏輯。
必要是能夠抽出Presenter接口(其實日跡這裏沒有必要)
public class StoryListPresnter implements IMyStroyPresenter{ protected IMyStoryListView mIView; protected FeedItem mFeedItem; protected ParallelStepExecutor mRequestNetDataExecutor; @Override public void onCreate(boolean needUpdateFromNet) { // 生命週期的邏輯處理 mFeedItem = new FeedItem(); } @Override public void setIView(IMyStoryListView IView) { // 設置View接口(目前實現的是Activity,但其實由其餘Fragment,View實現都是能夠的,這就是MVP的好處之一,解耦) mIView = IView; } public boolean requestAllDataFromNet() { mRequestNetDataExecutor.addStep(new GetUserSelfInfoStep(null)) .addStep(new ReportWatchVideoListStep(StoryListPresenter.this)) .addStep(new GetUserGuideInfoStep(StoryListPresenter.this)) .onCompleted(new SimpleStepExector.CompletedHandler() { @Override public void done(FeedItem item) { // 僞代碼 mFeedItem = item; // 處理Model層 mIView.openMyStoryListView(mFeedItem); // 根據View接口調用View更新 } }).run(); } }
MVP的優缺點:
優勢:
MVP的缺點也是很是明確的:
前面鋪墊這麼多,終於到我要吹水的時候了。MVC,MVP,還有MVVM等MVX系列的設計模式,都是一種大而全的統一管理。在項目結構中最爲關鍵實際上是:分模塊!
看看某寶的首頁,頂部搜索欄,banner,導航分類,搶購,特價,底部Tab。這是一個Activity的話,你再怎麼MVP,也是須要劃分模塊,而後分而治之。
一個再大的系統,均可以劃分一個個小的模塊,分而治之
頁面結構化,並非新玩意,是當時作web的一套代碼風格。下圖是當時作Web總結組件化的一張圖。如今看來,也就並無過期
頁面被劃分問一個個區域的模塊,有自身的邏輯和規劃。有人說,這不就是一個個組件嘛。而後「頁面結構化」並非指組件。
例如上圖的tabContainer,imgsContainer,listContainer,每個模塊都有本身的渲染模板(xml),請求的數據的CGI(數據源),自身的事件綁定(listener) ,狀態機(生命週期),並不僅是一個組件,而是一個個有本身生命力,能本身管理的小頁面。
根據頁面結構,劃分出一個個獨立維護模塊,這就是頁面結構化。
頁面結構化(Lego)與組件化的區別
下面就以問答的形式,用日跡評論贊項目實戰,來說解Lego好處
Lego本身拉取本身的數據,若是一個頁面5,6個模塊,就拉5,6分PB協議,談何性能?
這裏帶出Lego兩個特性:
日跡首頁評論贊
public FeedCommentLikeLego(Context context, Activity activity, ViewGroup parentView, HomeFeedItem feedItem, int feedType) { super(context, parentView); mHomeFeedItem = feedItem; mFeedItem = feedItem.mFeedBasicItem; mActivity = activity; mFeedType = feedType; mLikeManager = (LikeManager) SuperManager.getAppManager(SuperManager.LIKE_MANAGER); mParentView = LayoutInflater.from(context).inflate(R.layout.qqstory_feed_commentlike_view, parentView, true); // 頁面結構 FeedCommentLego commentLego = new FeedCommentLego(mContext, mParentView, mFeedItem, mFeedType); FeedLikeLego likeLego = FeedLikeLego.createIndexFeedLikeLego(mContext, activity, mParentView, mFeedItem, mFeedType); addLego(LEGO_KEY_COMMENT, commentLego); addLego(LEGO_KEY_LIKE, likeLego); commentLego.feed(mHomeFeedItem.getCommentList()); likeLego.feed(mHomeFeedItem.getLikeEntryList()); boot(); }
從FeedCommentLikeLego的構造方法,咱們得知
日跡710這裏就有場景,體驗出Lego切換數據源的優點。
【首頁】出於性能優化,都會作請求合併。返回多個Feed的視頻列表,評論贊列表數據。
commentLego.feed(mHomeFeedItem.getCommentList()); likeLego.feed(mHomeFeedItem.getLikeEntryList());
被餵養數據後,Lego內部的DataProvider將不啓動
【詳情頁】同一Lego,默認狀況就會啓動資金的DataProvider,會本身拉數據
@Override public LegoDataProvider getDataProvider() { return new FeedLikeDataProvider(this, mIsDetailPage); }
一個Lego類是到底是什麼?Lego類之間的紐帶?
大部分頁面的渲染流程線,以下圖
咱們把這些經常使用的網絡請求,處理數據,事件綁定,上報,容錯處理等一系列邏輯方法,以頁面塊爲單位封裝成一個Lego模塊。
這樣的一個抽象層Lego,咱們能夠清晰地看到該頁面塊,請求的數據是什麼,綁定了什麼事件,作了什麼上報,出錯怎麼處理。
最後加上生命週期,頁面結構化的Lego,已經算是一個完整的功能單元了。
繼承LegoBase,有幾個核心的方法須要重寫:
還有生命週期方法能夠重寫,但不是必要的。
你閱讀/接手一個Lego類,會是件很輕鬆的事情。一個Lego類,核心方法這幾個,其他都是業務邏輯方法。
改事件去該Lego的EventHandler,數據要改去DataProvider,產品要求大V才展現底部尾巴,好,去render方法找。
Lego之間的紐帶,有三個:
Lego的核心思想是:頁面結構分模塊,分而治之。解耦,代碼可讀性高,底層統一優化
在使用了兩個版本以後,感受完成度仍是不夠。
可是對比MVP,Lego能體驗出輕便,邏輯清晰,方法數量少的優點。
Lego頁面結構化的應用其實還在嘗試階段。以上算個人一些我的思考和總結。
更多精彩內容歡迎關注騰訊 Bugly的微信公衆帳號:
騰訊 Bugly是一款專爲移動開發者打造的質量監控工具,幫助開發者快速,便捷的定位線上應用崩潰的狀況以及解決方案。智能合併功能幫助開發同窗把天天上報的數千條 Crash 根據根因合併分類,每日日報會列出影響用戶數最多的崩潰,精準定位功能幫助開發同窗定位到出問題的代碼行,實時上報能夠在發佈後快速的瞭解應用的質量狀況,適配最新的 iOS, Android 官方操做系統,鵝廠的工程師都在使用,快來加入咱們吧!