安卓基礎開發庫,讓開發簡單點。
DevRing & Demo地址:github.com/LJYcoder/De…
學習/參考地址:
https://www.jianshu.com/p/91c2bb8e6369
http://www.jianshu.com/p/9d40b298eca9
http://blog.csdn.net/lmj623565791/article/details/46596109
MVP是什麼?
Most Valuable Player(最有價值運動球員)? 不不不,雖然我很喜歡看nba,但此MVP非彼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 MovieFragment 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 initData() {
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;
}
}
複製程式碼