寫在前面的話
嗯 聊聊架構(gòu)。 這段時間一直在維護舊項目胆剧。 包括自己之前寫的新項目 越來越發(fā)現(xiàn) 一個架構(gòu)清晰的項目往往讓人賞心悅目缭保。不至于在一個bug丟過來之后手足無措。包括以后別人接收自己的項目 能很快上手 何樂而不為呢核无?
曾經(jīng)的"MVC"
先從我們曾經(jīng)的 "MVC" 說起, "MVC" 全名 "Model View Controller".即 視圖層(View), 控制層(Controller)藕坯,模型層(Model).他們之間的關(guān)系 如圖:
MVC
視圖層(View) 一般由 xml布局文件擔(dān)任 控制層(Controller) 一般由 activity和fragment 擔(dān)任 模型層 (Model) 一般指的是 抽離出來的接口擴展团南。 嘗嘗可以復(fù)用的那一部分。 你只需要把接口 在Controller 拋出炼彪。 更新 View即可吐根。其實我們剛開始參與項目開發(fā) 技術(shù)薄弱的時候 都會想到把復(fù)用的部分抽離出來 但是activity類寫的很臃腫 至少不夠優(yōu)雅這其實就是MVC
寫到這里 我們知道android中 Controller 控制層一般有Activity(主要)和fragment 擔(dān)任。但是activity類中 常常包含一些ui 初始化 或者 網(wǎng)絡(luò) 第三方sdk 的初始化又或者是業(yè)務(wù)邏輯辐马。那么 這樣寫起代碼層就會變得臃腫拷橘。而且不夠優(yōu)雅。那么MVP就從MVC中眼花誕生了喜爷。
MVP
MVP中的model層繼續(xù)沿用MVC中的model
大家看這個圖 也許不是很清楚或者說不是太理解冗疮。
在這里 "View" 由presenter中的接口擔(dān)任 實現(xiàn)向View實現(xiàn)類通信,你也可以在Android組件中實現(xiàn)它檩帐。有時最好直接使用Activity术幔,F(xiàn)ragment或自定義View。>The view that receives commands from a presenter in MVP, is referred to as "view"
先說Model: 它是一個負(fù)責(zé)管理數(shù)據(jù)的接口湃密。模型的職責(zé)包括使用API诅挑、緩存數(shù)據(jù)、管理數(shù)據(jù)庫等泛源。該模型也可以是一個接口揍障,與其他模塊負(fù)責(zé)這些職責(zé)的溝通。例如俩由,如果您使用的是存儲庫模式,則該模型可能是一個存儲庫癌蚁。如果您使用的是干凈的建筑幻梯,相反兜畸,該模型可以關(guān)聯(lián)。(翻譯過來略顯生硬 簡而言之 Model層是數(shù)據(jù)訪問層碘梢,如數(shù)據(jù)庫API或遠(yuǎn)程服務(wù)器API)
Presenter:大家可以看到Model和View是完全沒有交互的咬摇。那么 Presenter則擔(dān)當(dāng)Model與View之間的中間人。你所有的業(yè)務(wù)邏輯都屬于它煞躬。Presenter負(fù)責(zé)查詢模型和更新視圖肛鹏,響應(yīng)用戶交互更新模型。
View:The view can be implemented by Activities, Fragments, any Android widget or anything that can do operations like showing a ProgressBar, updating a TextView, populating a RecyclerView and so on.(這個翻譯起來有點尷尬 感覺大家應(yīng)該都能看懂)
對照MVC圖來看 我們發(fā)現(xiàn) View 中出現(xiàn)了<Interface> 而Presenter中也出現(xiàn)了<Interface> .什么意思呢恩沛? 我們看Presenter中的代碼實例:
View中的TicTacToeView示例
關(guān)于
- 怎么寫Model
- 如何理解View
- 如何寫Presenter
參考 MVP詳解
關(guān)于MVP文字性的描述就這么多在扰。其實網(wǎng)上大部分講解都是套三者之間的關(guān)系交互圖來講的。只有理解了嘗試敲幾遍就差不多會了雷客。另外 符合項目需要的才是最好的芒珠。下面我嘗試著動手仿照 ONE 一個app將MVP模式套入其中練手(項目會不斷完善的 架構(gòu) 分包開始)
先截個圖 看一下目前大致的結(jié)構(gòu)。
沒用到Presenter之前切換Fragment MainActivity切換標(biāo)簽頁:
<code>
FragmentUtils fragmentUtil = new FragmentUtils(this, R.id.view_container);
fragmentUtil.switchTo(OneFragment.class);
mBinding.radio.setOnCheckedChangeListener((group, checkedId) -> {
switch (checkedId) {
case R.id.home_one:
fragmentUtil.switchTo(OneFragment.class);
break;
case R.id.home_reading:
fragmentUtil.switchTo(ReadingFragment.class);
break;
case R.id.home_music:
fragmentUtil.switchTo(MusicFragment.class);
break;
case R.id.home_movie:
fragmentUtil.switchTo(MovieFragment.class);
break;
default:
break;
}
});
</code>
用到Presenter之后:
<code>
if(presenter==null)
presenter = new MainTabPresenter(this, mBinding.radio);
presenter.switchTo();
</code>
其中 HomeActivity 類:
<code>
public class HomeActivity extends BaseActivity<HomeActBinding> {
private MainTabPresenter presenter;
@Override
protected int getLayoutId() {
return R.layout.home_act;
}
@Override
protected void setupView() {
if(presenter==null)
presenter = new MainTabPresenter(this, mBinding.radio);
presenter.switchTo();
}
}
</code>
BaseActivity類:
<code>
public abstract class BaseActivity<T extends ViewDataBinding> extends AppCompatActivity{
protected T mBinding;
protected ApiInteractor api;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initLayout();
}
private void initLayout() {
AppComponent appComponent = ((App) getApplication()).getAppComponent();
api = appComponent.getApiInteractor();
ViewDataBinding inflate = DataBindingUtil.setContentView(this, getLayoutId());
mBinding = DataBindingUtil.bind(inflate.getRoot());
if (mBinding == null) {
if (getLayoutId() == 0) {
throw new InflateException("activity no source_ID");
} else {
throw new NullPointerException("mBinding==NULL");
}
}
setupView();
}
@LayoutRes
protected abstract int getLayoutId();
protected abstract void setupView();
}
</code>
Presenter類:
<code>
public class MainTabPresenter implements MainTabContract.Presenter {
private RadioGroup radioGroup;
private final FragmentActivity mContext;
public MainTabPresenter(@Nullable FragmentActivity mActivity, @Nullable RadioGroup group){
mContext = checkNotNull(mActivity, "FragmentActivity cannot be null!");
radioGroup = checkNotNull(group, "RadioGroup cannot be null!");
}
@Override
public void switchTo() {
FragmentUtils fragmentUtil = new FragmentUtils(mContext, R.id.view_container);
fragmentUtil.switchTo(OneFragment.class);
radioGroup.setOnCheckedChangeListener((group1, checkedId) -> {
switch (checkedId) {
case R.id.home_one:
fragmentUtil.switchTo(OneFragment.class);
break;
case R.id.home_reading:
fragmentUtil.switchTo(ReadingFragment.class);
break;
case R.id.home_music:
fragmentUtil.switchTo(MusicFragment.class);
break;
case R.id.home_movie:
fragmentUtil.switchTo(MovieFragment.class);
break;
default:
break;
}
});
}
@Override
public void start() {//有些地方 RxJava MediaPlayer Handler 等生命周期用得上
}
}
</code>
簡單的邏輯代碼交由Presenter 和Model協(xié)作實現(xiàn)搅裙≈遄浚可能表述 和理解的不是太好 歡迎指正。Demo架構(gòu)Dagger2+RxJava+Retrofit 雛形已經(jīng)大致出現(xiàn) 后面會陸續(xù)補充一些使用的小技巧 和 第三方的插入 譬如 微信熱修復(fù) 等等吧部逮。謝謝大家~
參考:
MVC vs. MVP vs. MVVM on Android
Android App的設(shè)計架構(gòu):MVC,MVP,MVVM與架構(gòu)經(jīng)驗談
官方MVP Dagger2
等等 分享幾個免費的api(侵刪 謝謝)
干貨Api
和風(fēng)天氣
one 抓包接口
歡迎加群:
QQ 群 521039620
原文博客地址