頁面結(jié)構(gòu)化在 Android 上的嘗試

導(dǎo)語:MVP開發(fā)模式可以幫助項目結(jié)構(gòu)解耦,但其龐大的方法數(shù)增加新啼,較為笨重設(shè)計對于手Q項目并不很適合伤靠。參考之前Web開發(fā)經(jīng)驗,提出以頁面結(jié)構(gòu)化的解耦方式組織代碼逾条。下面講講Lego在Android上一次小小嘗試

一琢岩,MVP簡介

MVC太過常見這里不啰嗦。實(shí)際應(yīng)用MVC當(dāng)中师脂,Activity占據(jù)打部分的工作担孔,View和Controller的身份分不清。而MVP則是一種設(shè)計模式專門優(yōu)化Activity / Fragment吃警。

先來看看MVP模式的核心思想:View不直接與Model交互

MVP 把 Activity 中的 UI 邏輯抽象成 View 接口攒磨,把業(yè)務(wù)邏輯抽象成 Presenter 接口,Model 類還是原來的 Model

在MVP設(shè)計模式中汤徽,

View:由Activity充當(dāng)娩缰,并且響應(yīng)生命周期

Model:還是原來的數(shù)據(jù)層,網(wǎng)絡(luò)谒府,緩存拼坎,解析等。

Presenter:作為View和Model的中間紐帶完疫,View不能直接對Model進(jìn)行操作泰鸡,必須經(jīng)過Presenter

View interface:需要View實(shí)現(xiàn)的接口,View通過View interface與Presenter進(jìn)行交互壳鹤,降低耦合

二盛龄,日跡MVP實(shí)戰(zhàn)應(yīng)用

【Mode層】我們直接忽略

【View Interface】首頁的View接口,抽離出view和presnter交互的接口芳誓。由Activity繼承實(shí)現(xiàn)(Now.java余舶,QQStoryMainActivity.java)

public interface IMyStoryListView {

? ? public void setData(MyStorys myStoryList, RecentStory recentStoryList);

? ? public void setSegmentData(String key, Object data,boolean needRefreshUi);

? ? /**

? ? * 更新數(shù)據(jù)后刷新界面走的回調(diào)

? ? * @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實(shí)現(xiàn)了View接口,并且實(shí)現(xiàn)生命周期

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

? ? }

}

舉個例子锹淌,用戶下拉刷新一下匿值。觸發(fā)到Activity的onStartAutoRequestFromeNet。View邏輯在Activity赂摆。

業(yè)務(wù)邏輯則由Presnter的requestAllDataFromNet去實(shí)現(xiàn)挟憔。

【Presenter】具體的View->Model,Mode->View由這里實(shí)現(xiàn),其中View是有View接口抽象烟号,進(jìn)一步規(guī)范化View的邏輯绊谭。

必要是可以抽出Presenter接口(其實(shí)日跡這里沒有必要)

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) {

? ? ? ? // 設(shè)置View接口(目前實(shí)現(xiàn)的是Activity,但其實(shí)由其他Fragment汪拥,View實(shí)現(xiàn)都是可以的达传,這就是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);? // 根據(jù)View接口調(diào)用View更新

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }).run();

? ? }

}

MVP的優(yōu)缺點(diǎn):

優(yōu)點(diǎn):

解耦,絕對的趟大。不然抽這么多接口干嘛

模塊職責(zé)明確鹤树,層次清晰

Presenter可復(fù)用(在日跡的需求中,首頁和4Tab公用一個Presnter)

方便單元測試

避免Activity內(nèi)存泄露逊朽, Acitvity一身輕松

MVP的缺點(diǎn)也是非常明確的:

非常的笨重罕伯。一個View就對應(yīng)一個Presenter,輕業(yè)務(wù)一個Activity能解決的就不要解決

Presnter依然邏輯繁重叽讳。Acitivty輕松了追他,業(yè)務(wù)邏輯龐大的時候Presnter依然是大胖子。

代碼復(fù)雜度岛蚤,學(xué)習(xí)成本邑狸。這玩意不好理解,需要實(shí)戰(zhàn)中理解涤妒。

在手Q項目里单雾,MVP會激增很多方法數(shù)。

三她紫,Lego頁面結(jié)構(gòu)化

前面鋪墊這么多硅堆,終于到我要吹水的時候了。MVC贿讹,MVP渐逃,還有MVVM等MVX系列的設(shè)計模式,都是一種大而全的統(tǒng)一管理民褂。在項目結(jié)構(gòu)中最為關(guān)鍵其實(shí)是:分模塊茄菊!

看看某寶的首頁,頂部搜索欄赊堪,banner面殖,導(dǎo)航分類,搶購雹食,特價畜普,底部Tab。這是一個Activity的話群叶,你再怎么MVP,也是需要劃分模塊钝荡,然后分而治之街立。

一個再大的系統(tǒng),都可以劃分一個個小的模塊埠通,分而治之

頁面結(jié)構(gòu)化赎离,并不是新玩意,是當(dāng)時做web的一套代碼風(fēng)格端辱。下圖是當(dāng)時做Web總結(jié)組件化的一張圖×禾蓿現(xiàn)在看來虽画,也就并沒有過時

頁面被劃分問一個個區(qū)域的模塊,有自身的邏輯和規(guī)劃荣病。有人說码撰,這不就是一個個組件嘛。然后“頁面結(jié)構(gòu)化”并不是指組件个盆。

例如上圖的tabContainer脖岛,imgsContainer,listContainer颊亮,每一個模塊都有自己的渲染模板(xml)柴梆,請求的數(shù)據(jù)的CGI(數(shù)據(jù)源),自身的事件綁定(listener) 终惑,狀態(tài)機(jī)(生命周期)绍在,并不只是一個組件,而是一個個有自己生命力雹有,能自己管理的小頁面偿渡。

根據(jù)頁面結(jié)構(gòu),劃分出一個個獨(dú)立維護(hù)模塊件舵,這就是頁面結(jié)構(gòu)化卸察。

頁面結(jié)構(gòu)化(Lego)與組件化的區(qū)別

組件處于通用性,是不帶業(yè)務(wù)邏輯的铅祸。而頁面結(jié)構(gòu)化是帶業(yè)務(wù)邏輯坑质。

頁面結(jié)構(gòu)化目的是為了代碼維護(hù)性,項目管理临梗,優(yōu)化涡扼。組件復(fù)用可以有,但不是必要

組件與Lego不沖突盟庞。組件 +數(shù)據(jù)吃沪,業(yè)務(wù)邏輯 = Lego

下面就以問答的形式,用日跡評論贊項目實(shí)戰(zhàn)什猖,來講解Lego好處

四票彪,分析頁面結(jié)構(gòu)化特性

Lego自己拉取自己的數(shù)據(jù),如果一個頁面5,6個模塊不狮,就拉5,6分PB協(xié)議降铸,談何性能?

這里帶出Lego兩個特性:

每個Lego是有自己的數(shù)據(jù)摇零,并不是一定要自己拉取推掸,數(shù)據(jù)可以有其他Lego傳遞

Lego有父子關(guān)系。一個頁面/Activity需要一個頂層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);

? ? // 頁面結(jié)構(gòu)

? ? 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的構(gòu)造方法,我們得知

我是爸爸谅畅,我有兩個兒子

我兩個兒子不爭氣登渣,需要我來喂養(yǎng)數(shù)據(jù),自己不會掙錢(自己不拉數(shù)據(jù))

全家我是一家之主毡泻,啟動我說了算(Lego啟動boot后胜茧,會自己拉數(shù)據(jù)自己渲染,同時子Lego也會相繼boot)

日跡710這里就有場景牙捉,體驗出Lego切換數(shù)據(jù)源的優(yōu)勢竹揍。

【首頁】出于性能優(yōu)化,都會做請求合并邪铲。返回多個Feed的視頻列表芬位,評論贊列表數(shù)據(jù)。

commentLego.feed(mHomeFeedItem.getCommentList());

likeLego.feed(mHomeFeedItem.getLikeEntryList());

被喂養(yǎng)數(shù)據(jù)后带到,Lego內(nèi)部的DataProvider將不啟動

【詳情頁】同一Lego昧碉,默認(rèn)情況就會啟動資金的DataProvider,會自己拉數(shù)據(jù)

@Override

public LegoDataProvider getDataProvider() {

? ? return new FeedLikeDataProvider(this, mIsDetailPage);

}

一個Lego類是究竟是什么揽惹?Lego類之間的紐帶被饿?

大部分頁面的渲染流程線,如下圖

我們把這些常用的網(wǎng)絡(luò)請求搪搏,處理數(shù)據(jù)狭握,事件綁定,上報疯溺,容錯處理等一系列邏輯方法论颅,以頁面塊為單位封裝成一個Lego模塊。

這樣的一個抽象層Lego囱嫩,我們可以清晰地看到該頁面塊恃疯,請求的數(shù)據(jù)是什么,綁定了什么事件墨闲,做了什么上報今妄,出錯怎么處理。

最后加上生命周期鸳碧,頁面結(jié)構(gòu)化的Lego盾鳞,已經(jīng)算是一個完整的功能單元了。

繼承LegoBase瞻离,有幾個核心的方法需要重寫:

還有生命周期方法可以重寫雁仲,但不是必要的。

你閱讀/接手一個Lego類琐脏,會是件很輕松的事情。一個Lego類,核心方法這幾個日裙,其余都是業(yè)務(wù)邏輯方法吹艇。

改事件去該Lego的EventHandler,數(shù)據(jù)要改去DataProvider昂拂,產(chǎn)品要求大V才展示底部尾巴受神,好,去render方法找格侯。

Lego之間的紐帶鼻听,有三個:

parentView(公用xml)

feedData(公用數(shù)據(jù))

getLego(Lego關(guān)系)

四,總結(jié)

Lego的核心思想是:頁面結(jié)構(gòu)分模塊联四,分而治之撑碴。解耦,代碼可讀性高朝墩,底層統(tǒng)一優(yōu)化醉拓。

在使用了兩個版本之后,感覺完成度還是不夠收苏。

頂層Lego情況復(fù)雜亿卤,底層統(tǒng)一優(yōu)化不好做

接口之間約束,不夠自由

但是對比MVP鹿霸,Lego能體驗出輕便排吴,邏輯清晰,方法數(shù)量少的優(yōu)勢懦鼠。

Lego頁面結(jié)構(gòu)化的應(yīng)用其實(shí)還在嘗試階段钻哩。以上算我的一些個人思考和總結(jié)。

更多MelonTeam團(tuán)隊文章葛闷,請點(diǎn)擊下方“閱讀原文”

如果您覺得我們的內(nèi)容還不錯憋槐,就請轉(zhuǎn)發(fā)到朋友圈,和小伙伴一起分享吧~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末淑趾,一起剝皮案震驚了整個濱河市阳仔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌扣泊,老刑警劉巖近范,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異延蟹,居然都是意外死亡评矩,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門阱飘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來斥杜,“玉大人虱颗,你說我怎么就攤上這事≌嵛梗” “怎么了忘渔?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長缰儿。 經(jīng)常有香客問我畦粮,道長,這世上最難降的妖魔是什么乖阵? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任宣赔,我火速辦了婚禮,結(jié)果婚禮上瞪浸,老公的妹妹穿的比我還像新娘儒将。我一直安慰自己,他們只是感情好默终,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布椅棺。 她就那樣靜靜地躺著,像睡著了一般齐蔽。 火紅的嫁衣襯著肌膚如雪两疚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天含滴,我揣著相機(jī)與錄音诱渤,去河邊找鬼。 笑死谈况,一個胖子當(dāng)著我的面吹牛勺美,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播碑韵,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼赡茸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了祝闻?” 一聲冷哼從身側(cè)響起占卧,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎联喘,沒想到半個月后华蜒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡豁遭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年叭喜,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蓖谢。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡捂蕴,死狀恐怖譬涡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情启绰,我是刑警寧澤昂儒,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站委可,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏腊嗡。R本人自食惡果不足惜着倾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望燕少。 院中可真熱鬧卡者,春花似錦、人聲如沸客们。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽底挫。三九已至恒傻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間建邓,已是汗流浹背盈厘。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留官边,地道東北人沸手。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像注簿,于是被迫代替她去往敵國和親契吉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內(nèi)容