轉(zhuǎn)載請(qǐng)注明出處:
android 官方mvp框架優(yōu)化:lifecycle-mvp嗅榕,像前端那樣組合式寫頁(yè)面
地址:http://www.reibang.com/p/837168325131
目錄
1 前言
雖然在標(biāo)題上澡匪,自己很隨意的起了這么一個(gè)名字。其實(shí)并不是說(shuō)它起個(gè)英文名就牛逼了减江。說(shuō)白了,它其實(shí)就是mvp的思想加了lifecycle-component捻爷,然后加入了分層的思想您市,最后用TypeFactory取代presenter。為什么要這么改呢役衡?因?yàn)橛胢vp框架時(shí)確實(shí)存在了一些問題,這些小修小改都是基于業(yè)務(wù)的基礎(chǔ)上薪棒。目的就是:在這種框架下手蝎,別人用起來(lái)你寫的組件更方便,溝通成本更低俐芯,移植性也更好棵介。每個(gè)人對(duì)mvp的理解不一樣,給大家提供一種思路~
2 mvp框架含義
之前寫過(guò)一篇文章介紹了一下google官方提倡的mvp框架:吧史。為什么官方要推薦這個(gè)呢邮辽?因?yàn)殡m然mvp框架定義很簡(jiǎn)單:m代表model,提供數(shù)據(jù)贸营;v即view吨述,提供的是供presenter調(diào)用的view相關(guān)的方法;p 即presenter钞脂,提供的是頁(yè)面里觸發(fā)動(dòng)作的邏輯方法揣云。那么實(shí)施起來(lái)呢?并不是那么統(tǒng)一冰啃,因?yàn)樽杂啥群艽蟮讼ΑS檬裁磥?lái)做view?presenter的調(diào)用者都是誰(shuí)阎毅?他們?cè)趺搓P(guān)聯(lián)焚刚?他們和activity又有什么關(guān)系?所以一時(shí)之間扇调,網(wǎng)上有很多自創(chuàng)的mvp框架矿咕。
3 谷歌官方推薦的mvp框架
于是在眾說(shuō)紛紜之中,官方推薦了一個(gè)mvp的版本狼钮,具體詳情的可看上面的那個(gè)鏈接痴腌。大體說(shuō)下:
- 用contract來(lái)承載view和presenter的接口定義。這一點(diǎn)不是重點(diǎn)燃领,設(shè)計(jì)接口規(guī)范很好士聪。但是沒有也沒有關(guān)系,我認(rèn)為有時(shí)候還會(huì)有過(guò)度設(shè)計(jì)之嫌猛蔽,因?yàn)関iew和presenter都是和業(yè)務(wù)相關(guān)的剥悟,a業(yè)務(wù)場(chǎng)景硬要用b業(yè)務(wù)的view或presenter灵寺,肯定會(huì)有不合適之處(難道要用if...else)。即便現(xiàn)在合適区岗,你擋不住pm給你加場(chǎng)景略板。
- 用fragment來(lái)實(shí)現(xiàn)view接口。為什么要用fragment來(lái)實(shí)現(xiàn)view接口呢慈缔?因?yàn)閒ragment有生命周期叮称。有生命周期怎么了?這個(gè)就要從presenter的使用說(shuō)起了藐鹤,我們知道presenter是頁(yè)面里觸發(fā)動(dòng)作的邏輯方法瓤檐。觸發(fā)動(dòng)作比如頁(yè)面初始化加載,加載下一頁(yè)娱节,下拉刷新挠蛉,編輯,提交肄满,刪除等谴古。這些的方法執(zhí)行邏輯我們都寫在presenter里,這也是mvp區(qū)分視圖邏輯和業(yè)務(wù)邏輯的核心稠歉。那么presenter的在哪里調(diào)用呢掰担?兩大類:一:頁(yè)面的生命周期onStart()調(diào)用presenter頁(yè)面初始化加載的方法。二:頁(yè)面view的監(jiān)聽回調(diào)onXxxListener()方法里面調(diào)用presenter的刷新怒炸,編輯恩敌,提交等方法。所以fragment做為view層的實(shí)現(xiàn)類的好處就體現(xiàn)出來(lái)了:既有生命周期方法横媚,又有view的監(jiān)聽回調(diào)onXxxListener()方法纠炮,可以滿足對(duì)應(yīng)的presenter的調(diào)用。
- presenter處理頁(yè)面里觸發(fā)動(dòng)作的邏輯灯蝴。在fragment使用恢口。
- 在activty層關(guān)聯(lián)fragment和presenter,傳入?yún)?shù)穷躁,在setContentView()塞進(jìn)去對(duì)應(yīng)的view耕肩。
ok,基本說(shuō)完了问潭,以上幾點(diǎn)大體就是google官方推薦的mvp框架猿诸。
4 谷歌官方mvp框架的無(wú)奈
4.1 官方mvp的缺點(diǎn)
為什么說(shuō)它無(wú)奈呢?前面說(shuō)了狡忙,對(duì)于view層的接口梳虽,使用fragment來(lái)進(jìn)行實(shí)現(xiàn),因?yàn)閒ragment有生命周期灾茁。所以正是因?yàn)閒ragment有生命周期才使用的fragment作為view層窜觉。但fragment太笨重了谷炸。試想一下,我有一個(gè)頁(yè)面禀挫,里面有四五塊內(nèi)容旬陡。為了以后的各塊內(nèi)容的移動(dòng)、去除语婴、移植更方面描孟,我希望每一塊內(nèi)容都做成mvp形式,塊與塊之間不耦合砰左。那么官方的這個(gè)mvp框架就不適用了匿醒。因?yàn)槟悴豢赡茉谝粋€(gè)頁(yè)面寫5個(gè)fragment把。android的activity中不建議寫那么多的fragment菜职,fragment典型的使用場(chǎng)景是ViewPager。
4.2 常規(guī)變通
那么變通一下旗闽,5塊內(nèi)容的view層酬核,不再用fragment實(shí)現(xiàn),而只是一個(gè)個(gè)普通的view适室,每個(gè)view監(jiān)聽事件的響應(yīng)還是在view中進(jìn)行(調(diào)用各自的presenter方法)嫡意。而對(duì)于整個(gè)頁(yè)面的初始化加載或者下拉刷新加載,這5塊內(nèi)容共用一個(gè)fragment捣辆,在這個(gè)fragment的onStart()和下拉刷新的監(jiān)聽回調(diào)中加載5塊內(nèi)容對(duì)應(yīng)的presenter的方法蔬螟。然后在fragment的onCreateView()中把5塊內(nèi)容的view填充進(jìn)來(lái)。5塊內(nèi)容之間可能還需要通信汽畴,數(shù)據(jù)交流旧巾,這些借助presenter在fragment中進(jìn)行。
5 帶生命周期的mvp:lifecycle-mvp
上面那么做完全沒有問題忍些,并且上面那種做法也存在于我們的項(xiàng)目中鲁猩。但通過(guò)幾個(gè)版本的迭代,我發(fā)現(xiàn)了一些問題:presenter太亂罢坝,太散廓握。fragment需要持有所有的presenter,在onStart()時(shí)load()數(shù)據(jù)嘁酿。各自的view也需要持有各自的presenter隙券。并且view和presenter之間需要互相set()。你還需要在activty或者fragment的onDetroy()方法中管理presenter闹司∮樽校總體讓人覺得很亂。尤其是如果你的組件需要被別人使用游桩,或組件用需要用到其他app時(shí)拟枚,其他人拿到你的組件薪铜,你要關(guān)心兩個(gè)東西view和presenter,他得知道這兩個(gè)東西里面的方法恩溅,并且他需要在activty/fragment的生命周期中關(guān)聯(lián)他們并調(diào)用一些方法隔箍。嗯。這個(gè)過(guò)程肯定存在的大量溝通成本~
5.1 build方式
我認(rèn)為一個(gè)好的組件脚乡,應(yīng)該是這樣的:這個(gè)組件提供了一系列的build方法蜒滩,你只需要
View component = new Component.Builder()
.setXxx()
.setXxx()
.setXxx()
.build();
這樣這個(gè)組件就構(gòu)建成功了。并且這個(gè)組件是一個(gè)view奶稠,你可以把它用在任何的地方俯艰,不局限是activty或fragment。你可以把它放到更大的一個(gè)viewGroup中锌订,然后直接把這個(gè)viewGroup放到activty的setContentView()中就可以運(yùn)行竹握。就像小時(shí)候玩積木一樣,放到頁(yè)面中就可以辆飘。沒有presenter啦辐,外部不需要去管這個(gè)component是怎么加載數(shù)據(jù)的,加載是在其內(nèi)部進(jìn)行的蜈项。
看著不錯(cuò)芹关,那么會(huì)遇到幾個(gè)問題:
5.2 首先就沒有activty或者fragment提供生命周期的話,怎么加載數(shù)據(jù)紧卒?
以前確實(shí)存在這個(gè)問題:只有fragment和activty能夠提供生命周期侥衬。這也是google官方推薦的mvp框架中view層不得不使用fragment的原因∨芊迹可能是google意識(shí)到了這個(gè)問題轴总,于是就有了lifecycle-component這個(gè)組件。這個(gè)組件能夠提供給任意持有非全局context的類提供生命周期博个。具體怎么提供肘习,大致的原理可以看我寫的這一篇博客。
5.3 真的沒有presenter嗎坡倔?
如果沒有presenter漂佩,那么我們就又回到以前的老路了,什么業(yè)務(wù)邏輯罪塔,視圖邏輯都在一個(gè)類中處理投蝉。這肯定是不行的。所以還是需要presenter征堪。不同的地方是瘩缆,這個(gè)presenter不暴露給外部,在view層內(nèi)部持有佃蚜,因?yàn)関iew層還有了生命周期庸娱,所以對(duì)presenter的方法調(diào)用都可以在view層進(jìn)行着绊。比如:
public class AskAndAnswerEditorBlock extends FrameLayout implements LifecycleObserver {
//成員變量定義
public AskAndAnswerEditorBlock(Context context, boolean oldContentEditable, int maxLength, ...,boolean isEditSingleLine) {
....//構(gòu)造邏輯
init();
}
private void init() {
//new 出來(lái)對(duì)應(yīng)的 controller
askAndAnswerEditorController = new AskAndAnswerEditorController(getContext(), this, editTypeFactory);
//添加生命周期監(jiān)聽
((LifecycleRegistryOwner) getContext()).getLifecycle().addObserver(this);
...//其他初始化
}
//生命周期方法,調(diào)用controllor進(jìn)行加載數(shù)據(jù)熟尉,在controller的load()內(nèi)部归露,加載成功數(shù)據(jù)會(huì)調(diào)用view層的 binddata()方法。
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onStart() {
askAndAnswerEditorController.load();
}
//view層操作view的方法斤儿。controller使用
public void bindData(AskAndAnswerEditorBlock_Bean bean) {
}
//view層的操作view的方法剧包。controller使用
public void showProgress(String msg) {
mProgressHolder.showProgressIfAlreadyExisting(msg, true);
}
//view層的操作view的方法。controller使用
public void hideProgress() {
if (mProgressHolder!=null){
mProgressHolder.hideProgress();
}
}
//供外部使用觸發(fā)方法
public void submit() {
askAndAnswerEditorController.submit();
}
//供外部使用的觸發(fā)方法
public void refresh() {
askAndAnswerEditorController.refresh();
}
//使用它來(lái)進(jìn)行相似業(yè)務(wù)的拓展
public interface EditTypeFactory {
Observable getSubmitObservable(Context context, String content);
void updateAndSync(Context context, Object o);
String getOldContent(Context context);
}
//block對(duì)應(yīng)的 Builder
public static class Builder {
...//成員變量定義
public Builder(Context context) {
this.context = context;
}
public Builder setOldContentEditable(boolean isEditable) {
oldContentEditable = isEditable;
return this;
}
...//build方法
public AskAndAnswerEditorBlock build() {
...
}
}
}
從上面代碼中我們可以看到往果,presenter的初始化和數(shù)據(jù)加載等都放在了view層里面疆液,不需要借助activty/fragment的生命周期。
5.4 提供TypeFactory陕贮。
這樣復(fù)用presenter大體邏輯的同時(shí)堕油,也能進(jìn)行相似業(yè)務(wù)的拓展。在上面的例子中也看到了這么一個(gè)接口肮之。為什么要提供這么一個(gè)接口呢掉缺?因?yàn)槲覀冊(cè)谕獠坎恍枰峁﹑resenter,而有時(shí)候又想拓展這個(gè)block的使用場(chǎng)景局骤。比如我們上面這個(gè)例子攀圈,這個(gè)是一個(gè)編輯文字的組件暴凑,我們不但想讓它能夠編輯問題峦甩,答案,我們還想編輯點(diǎn)評(píng)短評(píng)现喳。所以我們需要提供一組接口凯傲,還對(duì)不同的場(chǎng)景進(jìn)行不同的實(shí)現(xiàn),TypeFactory就是用來(lái)做這個(gè)的嗦篱。那讀者可能會(huì)問了冰单,為什么不讓外界提供presenter,這樣豈不是更自由灸促。是基于這么考慮的:這個(gè)AskAndAnswerEditorBlock組件的作用是提供文字編輯诫欠,然后提供,修改浴栽,還有一些提示荒叼,如果讓外界提供presenter,那么不論是電影問答的presenter還是電影影評(píng)的presenter典鸡,肯定都有很大一部分的邏輯是相同的被廓。比如
class AskAndAnswerEditorController {
...
public AskAndAnswerEditorController(Context context, AskAndAnswerEditorBlock dataObserver, AskAndAnswerEditorBlock.EditTypeFactory editTypeFactory) {
...
}
public void load(){
AskAndAnswerEditorBlock.AskAndAnswerEditorBlock_Bean bean=new AskAndAnswerEditorBlock.AskAndAnswerEditorBlock_Bean();
if(editTypeFactory==null){
Log.e("ask_and_answer","askAndAnswerEditorController==null");
}
bean.content=editTypeFactory.getOldContent(context);
view.bindData(bean);
}
public void submit(){
if(editTypeFactory==null){
Log.e("ask_and_answer","askAndAnswerEditorController==null");
}
if (editTypeFactory.getSubmitObservable(context,view.editTextContent)==null) return;
submitSubscription=editTypeFactory.getSubmitObservable(context,view.editTextContent).compose(MovieSchedulersTransformer.applySchedulers()).subscribe(
new Subscriber() {
@Override
public void onStart() {
view.submitableStatuspublishSubject.onNext(false);
view.showProgress("提交中...");
}
@Override
public void onCompleted() {
view.hideProgress();
view.submitableStatuspublishSubject.onNext(true);
}
@Override
public void onError(Throwable e) {
Toast.makeText(context,"提交失敗",Toast.LENGTH_LONG).show();
view.hideProgress();
// TODO: 17/8/17 進(jìn)行handleException
}
@Override
public void onNext(Object o) {
Toast.makeText(context, R.string
.movie_detail_commentary_submit_success_first, Toast.LENGTH_LONG).show();
editTypeFactory.updateAndSync(context,o);
if(context instanceof Activity){
((Activity) context).finish();
}
}
}
);
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestroy() {
if (submitSubscription!=null){
submitSubscription.unsubscribe();
}
}
這么邏輯都是共有的。如果你讓外界去提供presenter萝玷,那么他們需要寫這些重復(fù)的邏輯嫁乘。并且昆婿,有時(shí)候你讓別人去寫presenter,別人不知道你的view層是怎么寫的蜓斧,還得花時(shí)間讀你的view層代碼仓蛆,還得花時(shí)間去寫整個(gè)的presenter,這些都是需要很大成本的法精。如果只讓他們提供差異性的東西多律,其他的不用他們管,這個(gè)組件豈不是更好用嗎搂蜓?build的構(gòu)造方式就是這么個(gè)理念狼荞,只需要set()一些需要的東西,這個(gè)組件就構(gòu)造出來(lái)就能用了帮碰。比如AskAndAnswerEditorController的使用:
public class AnswerEditPage extends LinearLayout{
public AskAndAnswerEditorBlock editView;
public AnswerEditPage(Context context, AskAndAnswerEditorBlock.IReaddlySubmitListener listener, long movieId, long askId, long oldAnswerId,String question,String answer) {
super(context);
DimenUtils dimenUtils = DimenUtils.getInstance(context.getApplicationContext());
View view= LayoutInflater.from(getContext()).inflate(R.layout.edit_answer_pager_textview,null);
setOrientation(LinearLayout.VERTICAL);
((TextView) view).setText(question);
addView(view,new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
editView = new AskAndAnswerEditorBlock.Builder(context)
.setcontentMaxlength(2000)
.setcontentMinlength(5)
.setOldContentEditable(true)
.setEditTypeFactory(new AnswerEditTypeFactory(movieId, askId,oldAnswerId,answer))
.setTextChangeListener(listener)
.setTextHint("快來(lái)說(shuō)說(shuō)你的看法吧 (5到2000字)")
.build();
addView(editView,new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
}
這個(gè)AnswerEditPage直接放到activty的setContentView()中相味,頁(yè)面就可以運(yùn)行了⊙惩欤可能還會(huì)有讀者會(huì)問丰涉,我提供presenter,豈不是會(huì)更自由嗎斯碌?而不像TypeFactory那樣一死,只能實(shí)現(xiàn)里面的一些接口,業(yè)務(wù)場(chǎng)景的適用性會(huì)不會(huì)太低了傻唾?但我想說(shuō)的是投慈,業(yè)務(wù)場(chǎng)景已經(jīng)被view層限制死了,view視圖已經(jīng)存在了冠骄,那么業(yè)務(wù)場(chǎng)景基本上也就差不多定下來(lái)了伪煤。即使你提供presenter,業(yè)務(wù)場(chǎng)景也就那么一個(gè)種類凛辣。這時(shí)候還需要那么自由的presenter干什么呢抱既??
5.5 與外界交互扁誓。
一個(gè)組件肯定少不了與外界的交互防泵。這里就有一個(gè)問題,從上面看蝗敢,貌似 block的數(shù)據(jù)加載都是在內(nèi)部進(jìn)行的捷泞,外部無(wú)法干涉。那如果我想獲取block加載后額數(shù)據(jù)怎么辦前普?還是前面的例子肚邢,比如AskAndAnswerEditorBlock 我編輯提交成功了,成功后會(huì)返回來(lái)一些數(shù)據(jù),這些數(shù)據(jù)我需要用來(lái)更新其他的組件骡湖,怎么做到呢贱纠?其實(shí)數(shù)據(jù)的交流不在block里面,我們并不想要侵入寫好的block响蕴。而只需要?jiǎng)觿?dòng)TypeFactory谆焊,TypeFactory是我們需要提供的,所以改動(dòng)它再自然不過(guò)了浦夷。比如:
public class AnswerEditTypeFactory implements AskAndAnswerEditorBlock.EditTypeFactory {
public AnswerEditTypeFactory(long movieId,long questionId ,long oldAnswerId,String oldAnswer) {
...
}
public Observable getSubmitObservable(Context context, String content) {
...
MovieAskAndAnswerApiProxy movieDetailApi=new MovieAskAndAnswerApiProxy(context.getApplicationContext());
Observable result= movieDetailApi.submitAnswer(context, movieId, questionId,oldAnswerId,content);
submitObservableUseForExternal(result);
return result;
}
public void submitObservableUseForExternal(Observable result) {
}
@Override
public void updateAndSync(Context context, Object result) {
...
}
@Override
public String getOldContent(Context context) {
...
}
}
我們?cè)趕ubmitObservable這個(gè)鏈?zhǔn)讲僮髦?插入一個(gè)submitObservableUseForExternal(result)方法辖试。這樣蠢正,我們就可以在page層對(duì)處理組件與組件的交流了姚淆,比如:
public class AnswerEditPage extends LinearLayout{
public AskAndAnswerEditorBlock editView;
public AnswerEditPage(Context context, AskAndAnswerEditorBlock.IReaddlySubmitListener listener, long movieId, long askId, long oldAnswerId,String question,String answer) {
super(context);
DimenUtils dimenUtils = DimenUtils.getInstance(context.getApplicationContext());
View view= LayoutInflater.from(getContext()).inflate(R.layout.edit_answer_pager_textview,null);
setOrientation(LinearLayout.VERTICAL);
((TextView) view).setText(question);
addView(view,new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
editView = new AskAndAnswerEditorBlock.Builder(context)
.setcontentMaxlength(2000)
.setcontentMinlength(5)
.setOldContentEditable(true)
.setEditTypeFactory(new AnswerEditTypeFactory(movieId, askId,oldAnswerId,answer){
@Override
public void submitObservableUseForExternal(Observable result) {
//使用result來(lái)更新其他組件
}
})
.setTextChangeListener(listener)
.setTextHint("快來(lái)說(shuō)說(shuō)你的看法吧 (5到2000字)")
.build();
addView(editView,new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
}
當(dāng)然,我們的block需要提供一個(gè)refresh()方法狞尔,供外部其他組件調(diào)用肥缔。其實(shí)refresh()的就是:
public void refresh() {
askAndAnswerEditorController.refresh();
}
因?yàn)槲覀儾幌蛲馓峁﹑resenter莲兢,所以向外提供的refresh()方法我們就寫在block里面了。不過(guò)像refresh()這樣被外界調(diào)用的方法并不多续膳,所以這么寫也可以接受改艇。
5.6 分層思想
其實(shí)這點(diǎn)從上面的代碼中就可以體現(xiàn)出來(lái)了,只是這里明確一下坟岔。這里的分層是為了明確邊界谒兄,每一層盛放的是什么邏輯,不要混亂社付,明確職責(zé)承疲。這樣對(duì)代碼的可讀性和復(fù)用都有很大的好處。舉個(gè)例子瘦穆,一個(gè)頁(yè)面有很多組件纪隙,組件之間肯定會(huì)有交流聯(lián)系赊豌。我們復(fù)用組件時(shí)扛或,肯定不會(huì)復(fù)用這個(gè)組件與其他組件的交流邏輯,那些是特定頁(yè)面特定業(yè)務(wù)的產(chǎn)物碘饼。我們復(fù)用的只是組件的一般性功能邏輯熙兔。所以,如果沒有分層艾恼,你復(fù)用的時(shí)候怎么搞住涉?讀代碼钠绍,把公用邏輯摘出來(lái)?所以分層是必要的。當(dāng)然這里的分層碱屁,只是我基于我們業(yè)務(wù)的理解,層次的多少蛾找,職責(zé)娩脾,因項(xiàng)目而異打毛,大家看看心里有數(shù)就可以了。
-
首先是view幻枉,這個(gè)view和前面講的view層不同碰声。這個(gè)view只是單純的view,有一個(gè)bindData(D data)方法熬甫,被其他層調(diào)用作數(shù)據(jù)綁定罗珍。這個(gè)獨(dú)立出來(lái)是因?yàn)樗梢宰鳛橐晥D的最小單位,被其他各個(gè)層復(fù)用覆旱。舉個(gè)例子:
public class MovieQuestionNumTips extends FrameLayout implements IBlock<MovieQuestionNumTips.Bean> { public MovieQuestionNumTips(Context context) { this(context,null,0); } public MovieQuestionNumTips(Context context, AttributeSet attrs) { this(context, attrs,0); } public MovieQuestionNumTips(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { LayoutInflater.from(getContext()).inflate(R.layout.view_movie_question_num_tips,this); } @Override public void bindData(Bean bean) { if (bean==null) return; ((TextView) findViewById(R.id.movie_question_num_tips_movie_name)).setText("《"+bean.movieName+"》"); ((TextView) findViewById(R.id.movie_question_num_tips_num)).setText(bean.movieQuestionNum+""); } public static class Bean { public String movieName; public int movieQuestionNum; public Bean(String movieName, int movieQuestionNum) { this.movieName = movieName; this.movieQuestionNum = movieQuestionNum; } } }
如果是一個(gè)列表的話扣唱,這里會(huì)有一個(gè)adapter層,里面填充數(shù)據(jù)時(shí)噪沙,拿的就是上面講的view進(jìn)行填充正歼。
-
具有數(shù)據(jù)加載功能的模塊是一個(gè)block,比如前面講的AskAndAnswerEditorBlock局义。我們通過(guò)build實(shí)例化出來(lái)萄唇,放到業(yè)務(wù)page里。在比如一個(gè)下拉刷新的列表也是一個(gè)block另萤,set進(jìn)來(lái)需要的TypeFactory(即adapter和數(shù)據(jù)加載的observable)就ok,比如:
private MovieRcPagePullToRefreshStatusBlock getRcViewBlock(Context context, final long movieId) { return new MovieRcPagePullToRefreshStatusBlock.Builder(context) .setPulltoRefreshable(true) .setEnablePinned(true) .setTypeFactory(new MovieRcPagePullToRefreshStatusBlock.TypeFactory() { @Override public Observable<? extends PageBase> getListObservable(Context context, boolean isRefresh, int offset, long timestamp) { MovieAskAndAnswerApiProxy movieDetailApi=new MovieAskAndAnswerApiProxy(context.getApplicationContext()); return movieDetailApi.getAskAndAnswerList(context,movieId,10,offset,isRefresh? CachePolicy.PREFER_NETWORK:CachePolicy.PREFER_CACHE,timestamp); } @Override public HeaderFooterAdapter getAdapter(Context context) { return new MovieAskAdaper(context,movieId); } }).build(); }
一個(gè)頁(yè)面就是一個(gè)pager揍障,pager其實(shí)是一個(gè)view俩由。這里面盛放一個(gè)頁(yè)面所有的block。以及各個(gè)block組件之間的交流通信邏輯兜畸。
為什么不直接用activity碘梢,而加了一層pager?因?yàn)檫@是為多端復(fù)用準(zhǔn)備的煞躬。我把一個(gè)頁(yè)面的邏輯寫在pager中恩沛,這樣我可以很方便的
把pager放到其他app中(因?yàn)槊總€(gè)app都有自己的BaseActivty,如果使用你自己的activity雷客,有兼容工作要處理)搅裙。關(guān)于這點(diǎn),可以參考我之前寫的美團(tuán)貓眼android模塊化實(shí)戰(zhàn)-可能是最詳細(xì)的模塊化實(shí)戰(zhàn),上面有詳細(xì)的介紹部逮。最近就是Activity層兄朋。這個(gè)里面除了放pager外,還需要做和actionBar相關(guān)的邏輯蜈漓。因?yàn)閍ctionBar是與activity關(guān)聯(lián)的宫盔。pager中沒辦法獲取這個(gè)東西灼芭,也就沒法寫關(guān)于actionBar的視圖邏輯。
6 最后的話
俗話說(shuō),優(yōu)化要建立在業(yè)務(wù)場(chǎng)景上茴迁。以上的修修補(bǔ)補(bǔ)也都是建立在我們的業(yè)務(wù)基礎(chǔ)之上萤衰,因?yàn)槲覀兊臉I(yè)務(wù)場(chǎng)景很多都是需要移植,需要復(fù)用倦卖〈徽考慮溝通成本,移植難度褐捻,我們做了以上的優(yōu)化椅邓。希望對(duì)大家有借鑒意義~