Android MVP Pattern 學習總結
導讀:MVP模式是MVC模式在Android上的一種變體,要介紹MVP就得先介紹MVC。在MVC模式中,Activity應該是屬於View這一層。而實質上,它既承擔了View,同時也包含一些Controller的東西在裡面。這對於開發與維護來說不太友好,耦合度大高了。把Activity的View和Controller抽離出來就變成了View和Presenter,這就是MVP模式。
MVC模式
MVC模式的結構分為三部分,實體層的Model,檢視層的View,以及控制層的Controller。
• 其中View層其實就是程式的UI介面,用於向用戶展示資料以及接收使用者的輸入
• 而Model層就是JavaBean實體類,用於儲存例項資料
• Controller控制器用於更新UI介面和資料例項
例如,View層接受使用者的輸入,然後通過Controller修改對應的Model例項;同時,當Model例項的資料發生變化的時候,需要修改UI介面,可以通過Controller更新介面。(View層也可以直接更新Model例項的資料,而不用每次都通過Controller,這樣對於一些簡單的資料更新工作會變得方便許多。)
MVP模式
按照MVC的分層,Activity和Fragment(後面只說Activity)應該屬於View層,用於展示UI介面,以及接收使用者的輸入,此外還要承擔一些生命週期的工作。Activity是在Android開發中充當非常重要的角色,特別是它的生命週期的功能,所以開發的時候我們經常把一些業務邏輯直接寫在Activity裡面,這非常直觀方便,代價就是Activity會越來越臃腫。因此,Activity不僅承擔了View的角色,還承擔了一部分的Controller角色,這樣一來V和C就耦合在一起了,雖然這樣寫方便,但是如果業務調整的話,要維護起來就難了,而且在一個臃腫的Activity類查詢業務邏輯的程式碼也會非常麻煩,所以看起來有必要在Activity中,把View和Controller抽離開來,而這就是MVP模式的工作了。
MVP模式的核心思想:
MVP把Activity中的UI邏輯抽象成View介面,把業務邏輯抽象成Presenter介面,Model類還是原來的Model。
這就是MVP模式,現在這樣的話,Activity的工作的簡單了,只用來響應生命週期,其他工作都丟到Presenter中去完成。從上圖可以看出,Presenter是Model和View之間的橋樑,為了讓結構變得更加簡單,View並不能直接對Model進行操作,這也是MVP與MVC最大的不同之處。
MVP模式的作用
• 分離了檢視邏輯和業務邏輯,降低了耦合 • Activity只處理生命週期的任務,程式碼變得更加簡潔
• 檢視邏輯和業務邏輯分別抽象到了View和Presenter的介面中去,提高程式碼的可閱讀性
• Presenter被抽象成介面,可以有多種具體的實現,所以方便進行單元測試。需要的時候可以隨意切換presenter改變實
現方式。當presenter程式碼過於龐大時,還可以整理出多個presenter 互相呼叫。
• 把業務邏輯抽到Presenter中去,避免後臺執行緒引用著Activity導致Activity的資源無法被系統回收從而引起記憶體洩露和OOM
MVP模式程式碼例項結構分析
從上面圖可以看到,MVP package下包含domian,model,presenter,ui 三個package.
Domian下放各個模組的Contract 功能實現的函式宣告。方便呼叫檢視。
Model 下放各個模組的Model 層。Model層的主要功能為 獲取資料。
Presenter 下放各個模組的 Presenter層。Presenter 層的主要功能 為 使用者的一些邏輯操作。
Ui 下放各個模組的Ui層。Ui層的主要功能為使用者的主activity。主執行緒使用者介面顯示。
這裡以Music 功能模組為例分析以上三個結構模組之間的呼叫關係。
MusicActivity
public class MusicActivity extends BaseActivity implements SpeechInputContract.IMusicView {
@BindView(R.id.recyclerview)
RecyclerView recyclerview;
View headView1;
TextView tvSongName;
TextView tvSingerAndAlbum;
ImageView imgCover;
ImageView btnPlay;
/* TextView btnLyricsControl;
TextView tvLyrics;*/
private PlayListAdapter playListAdapter;
MusicPlayService musicPlayService;
private void initMusic(final MusicPlayBean musicPlayBean){…..}
@Override
protected int setRootViewId() {
return R.layout.activity_music;
}
@Override
protected void initData(){
…資料的初始化..
playListAdapter = new PlayListAdapter();
initView();
…
}
private void initView(){
…
tvSongName = (TextView) headView1.findViewById(R.id.tv_song_name);
btnPlay.setOnClickListener
…
}
@Override
public void getMusicDataSuccess(List<MusicPlayBean> musicPlayBeanList) {
// playListAdapter.addData(musicPlayBeanList);
}
@Override
public void controlMusic(String playState, int index) {
if (null != musicPlayService) {
musicPlayService.changePlayState(playState);
}
}
@Override
protected void onResume() {…}
@Override
protected void onPause(){…}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}
SpeechInputContract
在musicAcitivity 類繼承SpeechInputContract.IMusicView
interface IMusicView extends IBaseView {
/**
* 獲取音樂源成功
*
* @param musicPlayBeanList 獲取到的音樂源
*/
void getMusicDataSuccess(List<MusicPlayBean> musicPlayBeanList);
/**
* 音樂控制
*
* @param playState 不用列舉是因為 1、離線識別返回就是STRING 方便 2、列舉消耗記憶體
*/
void controlMusic(String playState, int index);
}
這些介面在Activity 中實現。在presenter 中呼叫,以實現在presenter中控制ui。
MusicPresenter
public class MusicPresenter {
IMusicView musicView;
MusicModel musicModel;
public MusicPresenter(IMusicView musicView) {
this.musicView = musicView;
musicModel = new MusicModel();
}
這裡傳入MusicView 就可以通過介面呼叫直接控制介面顯示,直接控制MusicView.
例項化 MusicModel ,獲取Music模組相關的資料
public void getMusicData(String requirement) {
if (null != musicView) {
musicModel.getMusicData(requirement, new <KugouMusicBean>(musicView) {
@Override
public void onSuccess(KugouMusicBean kugouMusicBean, Call call, Response response) {
…
musicView.getMusicDataSuccess(dateList);
…
}
}
public void setMusicState(String musicState) {
if (null != musicView) {
musicView.controlMusic(musicState, -1);
}
}
public void release() {
if (null != musicModel) {
musicModel.release();
}
musicModel = null;
musicView = null;
}
}
從以上可以看出 在MusicPresenter 中直接呼叫musicModel 和MusicView 中的相關介面,
MusicActivity 中只做了控制元件的初始化等簡單的操作。使Activity的程式碼結構簡單。
MusicModel
public class MusicModel extends BaseModel implements SpeechInputContract.IMusicModel {
@Override
public void getMusicData(String requirement, AbsCallback callback){…}
@Override
public void release(){…}
}
再看看MusicPresenter 初始化和引用的地方
SpeechInputPresenter
public class SpeechInputPresenter extends SpeechInputContract.ISpeechInputPresenter {
MusicPresenter musicPresenter;
musicPresenter = new MusicPresenter((SpeechInputContract.IMusicView) getContext());
}
當錄音並識別完成時,NLI 返回值 recognizeBean 。對這個結果進行分析處理並進行相應的邏輯和ui顯示。
private String dealRecognizeBean(RecognizeBean recognizeBean)
{…
if (recognizeBean.getApiType().equals(MUSIC.getName())) {
{
switch (semantic.getGlobalModifiers()[0]) {
case MUSIC_PLAY:
if (semantic.hasSlots()) {
musicPresenter.getMusicData(semantic.getSlots()[0].getValue());
} else {
musicPresenter.getMusicData(getContext().getString(R.string.recommend_music));
}
tempShowResult = getContext().getString(R.string.go_to_play);
break;
case MUSIC_PAUSE:
musicPresenter.setMusicState(PAUSE);
tempShowResult = getContext().getString(R.string.go_to_pause);
break;
case MUSIC_PRE:
musicPresenter.setMusicState(PRE_SONG);
tempShowResult = getContext().getString(R.string.go_to_play);
break;
case MUSIC_NEXT:
musicPresenter.setMusicState(NEXT_SONG);
tempShowResult = getContext().getString(R.string.go_to_play);
break;
}
…}