安卓基礎開發庫,讓開發簡單點。
Demo地址:https://github.com/LJYcoder/MvpDagger
學習/參考地址:
http://www.lxweimin.com/p/91c2bb8e6369
http://www.lxweimin.com/p/9d40b298eca9
http://blog.csdn.net/lmj623565791/article/details/46596109
MVP是什么?
MVP指安卓中的一種開發模式。
它將代碼整體分為M(Model)、V(View)、P(Presenter)三層。
正經版:
M層(model):數據模型/處理層。負責數據處理、數據提供,如網絡請求,數據庫操作
V層(view):視圖展示層。負責界面展示,如Activity,Fragment
P層(presenter):業務邏輯層。負責業務邏輯服務,是V層與M層間的橋梁
你也可以這樣幫助理解下(餐廳版):
M層(model):廚師。負責做菜
V層(view):顧客。點餐吃飯
P層(presenter):服務員。提供下單、上菜等各種服務
與MVC模式的區別:
MVC中,V層與M層是可以互通的,而在MVP中V層與M層是不通的。
按餐廳版來說就是,MVC中顧客可以直接告訴廚師要吃什么菜,廚師做好后直接把菜端到你面前,而MVP中只能通過服務員來完成點餐到用餐的過程。
使用MVP有什么好處?
抽象些來說:
MVP可以降低代碼耦合度,提高代碼的結構清晰度、可讀性、維護性和復用性。
具體些來說(參考JessYan的例子):
現在有這么一個需求:Activity中從網絡獲取數據然后展示在A控件上。
如果不用MVP的話,那就直接把獲取展示等代碼都寫在Activity中,很快便可以寫完。
但現在需求變動了:
1.要求加入緩存功能,如果本地有數據,則先從本地獲取數據,然后再從網絡獲取最新數據進行替換
2.要求數據展示在B控件上而不是A控件。
如果代碼都是你自己寫的,那改起來還比較輕松,但假如是團隊開發,代碼不是你寫的,你需要花時間把邏輯重新看一遍再開始改,而且如果改錯的話,會影響之前已經寫好的功能。
但使用MVP模式進行開發就不同了。由于它的分工結構清晰,V層僅負責數據展示,P層僅負責業務邏輯,M層僅負責數據獲取/處理。所以改動起來就輕松很多。
對于變動的需求1:我們只需在P層加入邏輯判斷(先從本地獲取,再網絡獲取),然后M層增加一個從本地獲取數據方法。
對于變動的需求2:我們只需在V層修改獲取到數據后的展示方式,從控件A改成控件B。
當然還有可復用等優點,這里就不具體講了。
至于"缺點"嘛,就是會相應地增加代碼量。有得有失,但得大于失。
具體使用
以這么一個場景為例:
從網絡獲取“正在上映”的電影數據,獲取成功則將數據在頁面展示,獲取失敗則給出相應提示。
需要寫四個部分:Model層,View層,Presenter層,接口
接口
負責“連接”MVP三層,以便方法調用、數據流動。同時也便于進行單元測試。
IView
View層接口,定義View層需實現的方法,P層通過該接口回調通知View層。
public interface IMovieView {
//成功獲取到電影數據
void getMovieSuccess(List<MovieRes> list, int type);
//獲取電影數據失敗
void getMovieFail(int status, String desc, int type);
}
IModel
Model層接口,定義Model層需實現的方法,P層通過該接口調用M層獲取/處理數據的方法。
public interface IMovieMoel{
//請求正在上映的電影數據
Observable getPlayingMovie(int start, int count);
...
}
Model層
實現IModel接口中的方法,負責數據的獲取/處理。
public class MovieModel implements IMovieMoel{
@Override
public Observable getPlayingMovie(int start,int count) {
//提供數據源
return DevRing.httpManager().getService(MovieApiService.class).getPlayingMovie(start, count);
}
...
}
Presenter層
處理業務邏輯,調用M層獲取數據,調用V層傳遞展示數據。
public class MoviePresenter {
private IMovieView mIView;
private IMovieModel mIModel;
public MoviePresenter(IMovieView iMovieView, IMovieMoel iMovieMoel) {
mIView = iMovieView;
mIModel = iMovieModel;
}
/**
* 獲取正在上映的電影
*
* @param start 請求電影的起始位置
* @param count 獲取的電影數量
* @param type 類型:初始化數據INIT、刷新數據REFRESH、加載更多數據LOADMORE
*/
public void getPlayingMovie(int start, int count, final int type) {
DevRing.httpManager().commonRequest( mIModel.getPlayingMovie(start, count),
new CommonObserver<HttpResult<List<MovieRes>>>() {
@Override
public void onResult(HttpResult<List<MovieRes>> result) {
if (mIView != null) {
mIView.getMovieSuccess(result.getSubjects(), type);
}
}
@Override
public void onError(int errType, String errMessage) {
if (mIView != null) {
mIView.getMovieFail(errType, errMessage, type);
}
}
}, RxLifecycleUtil.bindUntilDestroy(mIView));
}
...
/**
* 釋放引用,防止內存泄露
*/
public void destroy() {
mIView = null;
}
}
View層
實現IView接口中的方法,對獲取到的數據進行展示
public class MovieActivity extends Activity implements IMovieView {
//獲取電影數據成功的網絡請求回調
@Override
public void getMovieSuccess(List<MovieRes> list, int type) {
//成功,對數據進行展示
....
}
//獲取電影數據失敗的網絡請求回調
@Override
public void getMovieFail(int status, String desc, int type) {
//失敗,界面上做出相應提示
...
}
...
}
完成以上幾步后,在View層初始化時,調用Presenter層方法即可。
@Override
protected void onCreate(Bundle saveInstanceState) {
...
mPresenter = new MoviePresenter(this, new MovieModel());
mPresenter.getPlayingMovie(start, mCount, type);
}
還有一點需注意:
如果Presenter層持有了View層的引用,那么記得在V層銷毀時,把Presenter層中對View層的引用置null,避免View層回收失敗導致內存泄漏。
@Override
public void onDestroy() {
super.onDestroy();
if (mPresenter != null) {
mPresenter.destroy();
mPresenter = null;
}
}
2018.4.13:
github的MvpDagger項目中新增了MVP一鍵生成模板,根據Demo的代碼結構定制的,有需要的可以查看。