1.關(guān)于MVP架構(gòu)
有時(shí)候在實(shí)現(xiàn)一個(gè)APP的時(shí)候會(huì)發(fā)現(xiàn)亮蛔,寫著寫著Activity中的代碼會(huì)越來越多,控件和數(shù)據(jù)的初始化沒有問題浴讯。當(dāng)添加按鈕并綁定監(jiān)聽事件時(shí)候有網(wǎng)絡(luò)訪問朵夏,往往直接寫在OnClick()方法中(我以前就是這么干的),Activity中的代碼馬上就變得十分冗余榆纽,當(dāng)中出現(xiàn)了業(yè)務(wù)邏輯侍郭,數(shù)據(jù)訪問部分询吴,Activity簡直變成了一個(gè)萬能類。
Activity變成萬能類的后果是亮元,代碼變得龐大并且臃腫,代碼間嚴(yán)重耦合唠摹,改一個(gè)小部分往往牽一發(fā)而動(dòng)全身爆捞,而且代碼的可讀性很差,想象一下在一個(gè)功能眾多的Activity中尋找某一個(gè)功能某塊的場景勾拉。
這就誕生了各種APP分層架構(gòu)煮甥,其中MVP架構(gòu)是最近十分流行的APP架構(gòu)。
MVP的全稱為:Model-View-Presenter
MVP將APP劃分為三層藕赞,Model層負(fù)責(zé)數(shù)據(jù)存儲(chǔ)與數(shù)據(jù)處理成肘,View負(fù)責(zé)展示數(shù)據(jù),Presenter層負(fù)責(zé)具體的業(yè)務(wù)邏輯斧蜕。
雖然網(wǎng)上的對于MVP介紹的文章看了不少双霍,但是MVC都還沒搞明白,對于MVP就更是處于一知半解的狀態(tài)批销,剛好Google官方在Github上推出了一個(gè)項(xiàng)目實(shí)例(點(diǎn)擊查看)洒闸,用于展示Android各種各樣的MVP架構(gòu),其中的todo-mvp屬于MVP基礎(chǔ)架構(gòu)實(shí)例均芽,即使是基礎(chǔ)架構(gòu)的實(shí)現(xiàn)方案也使我受益匪淺丘逸,并仿照todo-mvp實(shí)現(xiàn)了一個(gè)小的案例。
2.案例介紹
在案例中使用了http://gank.io/api提供的福利接口掀宋,可從中獲取各種妹子的福利照片并展示深纲。
項(xiàng)目結(jié)構(gòu):
(1)BaseView和BasePresenter兩個(gè)基類接口。
BaseView.java:
public interface BaseView<T> {
void setPresenter(T presenter);
}
BasePresenter.java:
public interface BasePresenter {
void start();
}
所有的View和Presenter首先實(shí)現(xiàn)這兩個(gè)基類接口劲妙。
其中BaseView的setPresenter(T presenter)方法用于綁定View對應(yīng)的Presenter湃鹊,BasePresenter的start()方法用于初始化時(shí)執(zhí)行相應(yīng)的邏輯。
(2)具體的實(shí)現(xiàn)接口
之后在特定的功能中是趴,在契約類WelfareDetailContact.java中聲明具體的View和Presenter接口涛舍,在其中聲明實(shí)現(xiàn)的具體功能。
WelfareDetailContact.java:
public interface WelfareDetailContact {
interface View extends BaseView<Presenter> {
void showPic(Welfare data);
}
interface Presenter extends BasePresenter {
void displayHome();
void refreshRandom();
}
}
其中View的showPic(Welfare data)方法只負(fù)責(zé)根據(jù)結(jié)構(gòu)化的數(shù)據(jù)進(jìn)行相應(yīng)的顯示唆途,Presenter的兩個(gè)方法只負(fù)責(zé)展示主頁和隨機(jī)刷新邏輯(目前只添加了這兩個(gè)邏輯)富雅。
具體的WelfareDetailView與WelfareDetailPresenter需實(shí)現(xiàn)契約類中的兩個(gè)接口。
(3)Model數(shù)據(jù)層
WelfareDataSource.java:
public interface WelfareDataSource {
interface GetCallback {
void onWelfareGet(Welfare welfare);
void onDataNotAvailable();
}
void queryHome(GetCallback callback);
void queryPage(String page, GetCallback callback);
void queryRandom(GetCallback callback);
}
此接口聲明了獲取數(shù)據(jù)的方法和回調(diào)接口肛搬。
實(shí)現(xiàn)此接口的一共有三個(gè)類:WelfareRepository没佑,WelfareLocalDataSource和WelfareRemoteDataSource。
WelfareRepository 負(fù)責(zé)總調(diào)度温赔,WelfareLocalDataSource負(fù)責(zé)本地?cái)?shù)據(jù)的加載與存儲(chǔ)蛤奢,WelfareRemoteDataSource負(fù)責(zé)網(wǎng)絡(luò)遠(yuǎn)程數(shù)據(jù)的獲取。
在本地和遠(yuǎn)程數(shù)據(jù)獲取時(shí),都會(huì)將JSON數(shù)據(jù)組裝成Welfare對象后傳入回調(diào)接口啤贩。
WelfareRepository 作為負(fù)責(zé)的總調(diào)度待秃,分別持有WelfareLocalDataSource和WelfareRemoteDataSource的實(shí)例引用。三個(gè)類均使用單例模式痹屹。
下面看看具體實(shí)現(xiàn):
總調(diào)度類WelfareRepository
WelfareRepository.java:
public class WelfareRepository implements WelfareDataSource {
private WelfareLocalDataSource mLocalDataSource;
private WelfareRemoteDataSource mRemoteDataSource;
......
@Override
public void queryHome(final GetCallback callback) {
mLocalDataSource.queryHome(new GetCallback() {
@Override
public void onWelfareGet(Welfare welfare) {
callback.onWelfareGet(welfare);
}
@Override
public void onDataNotAvailable() {
mRemoteDataSource.queryHome(new GetCallback() {
@Override
public void onWelfareGet(Welfare welfare) {
callback.onWelfareGet(welfare);
mLocalDataSource.updateWelfare(welfare);
}
@Override
public void onDataNotAvailable() {
}
});
}
});
}
......
}
以queryHome()查詢主頁為例章郁,總調(diào)度WelfareRepository先是調(diào)用了WelfareLocalDataSource實(shí)例的queryHome()方法進(jìn)行本地讀取,若是本地讀取成功志衍,則正常執(zhí)行傳入的回調(diào)暖庄;若是本地?cái)?shù)據(jù)獲取失敗,則調(diào)用WelfareRemoteDataSource實(shí)例的queryHome()方法獲取網(wǎng)絡(luò)數(shù)據(jù)請求楼肪,成功時(shí)正常執(zhí)行回調(diào)培廓,并在本地進(jìn)行存儲(chǔ)。
(4)具體View和Presenter的實(shí)現(xiàn)類春叫。
WelfareDetailFragment.java:
public class WelfareDetailFragment extends Fragment implements WelfareDetailContact.View {
private WelfareDetailContact.Presenter mPresenter;
......
@Override
public void showPic(Welfare data) {
mAdapter.setData(data.results);
mAdapter.notifyDataSetChanged();
}
@Override
public void setPresenter(WelfareDetailContact.Presenter presenter) {
this.mPresenter = presenter;
}
@Override
public void onResume() {
super.onResume();
mPresenter.start();
}
}
在實(shí)例中使用Fragment來代替Activity來執(zhí)行View的角色肩钠,這使得Activity中的代碼更為簡潔,使角色分工更加明確象缀。
可以看到蔬将,WelfareDetailFragment作為View的角色,持有Presenter的引用央星,并在onResume()方法中調(diào)用mPresenter.start()來執(zhí)行初始化時(shí)需要執(zhí)行的邏輯霞怀。
WelfareDetailPresenter.java:
public class WelfareDetailPresenter implements WelfareDetailContact.Presenter {
private WelfareRepository mWelfareRepository;
private WelfareDetailContact.View mView;
......
public WelfareDetailPresenter(WelfareRepository welfareRepository, WelfareDetailContact.View view) {
this.mWelfareRepository = welfareRepository;
this.mView = view;
view.setPresenter(this);
}
@Override
public void start() {
displayHome();
}
@Override
public void displayHome() {
mWelfareRepository.queryHome(new WelfareDataSource.GetCallback() {
@Override
public void onWelfareGet(Welfare welfare) {
mView.showPic(welfare);
}
@Override
public void onDataNotAvailable() {
}
});
}
}
WelfareDetailPresenter在構(gòu)造初始化時(shí)與相應(yīng)的View進(jìn)行綁定,并且分別持有WelfareRepository和View實(shí)例引用莉给。
以start()初始化中調(diào)用的displayHome()方法為例毙石,在具體的實(shí)現(xiàn)中,通過對兩個(gè)實(shí)例的操作來進(jìn)行相應(yīng)的業(yè)務(wù)邏輯颓遏。簡單來說徐矩,就是對Model層和View層實(shí)例提供的功能進(jìn)行組合,使邏輯更加清晰叁幢,沒有多余的代碼滤灯。
(5)項(xiàng)目示例
通過仿照官方示例聲明一些列的接口,實(shí)現(xiàn)了Model層-View層-Presenter層的分離曼玩,使代碼和邏輯更加的清晰鳞骤,具體源碼托管在我的Github主頁(點(diǎn)擊查看)。