詳解Android MVP開(kāi)發(fā)模式
本文主要講解MVP開(kāi)發(fā)模式以及具體實(shí)例。
一、簡(jiǎn)介
MVP(Model View Presenter)模式是著名的MVC(Model View Controller)模式的一個(gè)演化版本,目前它在Android應(yīng)用開(kāi)發(fā)中越來(lái)越重要了。初看起來(lái)我們會(huì)感覺(jué)增加了很多類(lèi)接口代碼看起來(lái)更加清晰。
MVP模式可以分離顯示層和邏輯層,所以功能接口如何工作與功能的展示可以實(shí)現(xiàn)分離,MVP模式理想化地可以實(shí)現(xiàn)同一份邏輯代碼搭配不同的顯示界面。不過(guò)MVP不是一個(gè)結(jié)構(gòu)化的模式,它只是負(fù)責(zé)顯示層而已,任何時(shí)候都可以在自己的項(xiàng)目結(jié)構(gòu)中使用MVP模式。(不局限于Android項(xiàng)目開(kāi)發(fā))
因?yàn)镸VP其實(shí)就是從MVC模式演化產(chǎn)生的,那么我們先看一下著名的MVC模式:
- View:對(duì)應(yīng)于布局文件
- Model:業(yè)務(wù)邏輯和實(shí)體模型
- Controller:控制器,Android中對(duì)應(yīng)于Activity
對(duì)應(yīng)的交互圖如下:
雖然Android系統(tǒng)應(yīng)用開(kāi)發(fā)本身是遵循MVC開(kāi)發(fā)模式的,但是我們仔細(xì)看一下View層和Activity,具體view布局文件中的數(shù)據(jù)綁定和事件處理的方法代碼都是冗余在Activity中的,所以我們經(jīng)??纯梢钥吹紸ctivity類(lèi)動(dòng)不動(dòng)就是少則九百行,多則上千甚至幾千行。那么現(xiàn)在的演化升級(jí)版本的MVP的模式又是怎么樣的呢?MVP模式會(huì)引入 Presenter層,該機(jī)型復(fù)雜完成View層和Model層的交互,那么具體MVP對(duì)應(yīng)如下:
- View:View通常來(lái)說(shuō)是由Activity實(shí)現(xiàn)的,它會(huì)包含一個(gè)Presenter的引用,View要做的就只是在每次有接口調(diào)用的時(shí)候(比如按鈕點(diǎn)擊后)調(diào)用Presenter的方法。
- Model:業(yè)務(wù)邏輯和實(shí)體模型
- Presenter:主要作為溝通View和Model的橋梁,它從Model層檢索數(shù)據(jù)后,返回給View層,但是不像MVC結(jié)構(gòu),因?yàn)樗部梢詻Q定與View層的交互操作。
數(shù)據(jù)交互圖如下:
觀察上面兩個(gè)模式的交互圖,是不是MVP模式更加清晰簡(jiǎn)單啊!
二、MVC和MVP區(qū)別
我們來(lái)具體看一下下面兩張對(duì)比,就可以看來(lái)具體區(qū)別了:
觀察上圖我們可以發(fā)現(xiàn)MVP模式中,View 和Model的交互是通過(guò)Presenter來(lái)進(jìn)行完成,這樣統(tǒng)一管理,邏輯會(huì)更加清晰。
三、MVP模式例子講解
3.1.具體實(shí)現(xiàn)功能需求:我們是用MVP模式來(lái)進(jìn)行實(shí)現(xiàn)用戶登錄操作.
3.2.例子實(shí)例如下:
3.3.項(xiàng)目代碼框架如下:
3.4.代碼具體實(shí)現(xiàn):
3.4.1.Model層:Bean類(lèi)(Entity),PersonBean類(lèi),然后在業(yè)務(wù)邏輯類(lèi)中有登錄方法,同時(shí)把登錄成功狀態(tài)回調(diào)接口傳入進(jìn)入,具體如下:
package com.chinaztt.fda.entity; /** * 當(dāng)前類(lèi)注釋:用戶信息實(shí)體類(lèi) * 項(xiàng)目名:FastDev4Android * 包名:com.chinaztt.fda.entity */ public class PersonBean { private String username; private String password; public PersonBean() { } public PersonBean(String username, String password) { this.username = username; this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "PersonBean{" + "username='" + username + '\'' + ", password='" + password + '\'' + '}'; } }
public interface IPersonBiz { void login(String username,String password,LoginRequestCallBack valueCallBack);
package com.chinaztt.fda.biz.imp; import com.chinaztt.fda.biz.IPersonBiz; import com.chinaztt.fda.biz.LoginRequestCallBack; import com.chinaztt.fda.entity.PersonBean; import com.chinaztt.fda.utils.Log; /** * 當(dāng)前類(lèi)注釋:用戶相關(guān)業(yè)務(wù)邏輯實(shí)現(xiàn)類(lèi) * 項(xiàng)目名:FastDev4Android * 包名:com.chinaztt.fda.biz.imp */ public class PersonBizImp implements IPersonBiz{ private static final String TAG="PersonBizImp"; @Override public void login(final String username, final String password, final LoginRequestCallBack valueCallBack) { Log.d(TAG,"username:"+username+",password:"+password); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(4500); } catch (InterruptedException e) { e.printStackTrace(); } //進(jìn)行開(kāi)始登錄,這邊應(yīng)該進(jìn)行請(qǐng)求服務(wù)器,進(jìn)行數(shù)據(jù)驗(yàn)證 if(username.equals("jiangqq")&&password.equals("12345")){ valueCallBack.loginSuccess(new PersonBean(username,password)); }else{ valueCallBack.loginFailed(); } } }).start(); } }
package com.chinaztt.fda.biz; import com.chinaztt.fda.entity.PersonBean; /** * 當(dāng)前類(lèi)注釋:登錄請(qǐng)求結(jié)果回調(diào) * 項(xiàng)目名:FastDev4Android * 包名:com.chinaztt.fda.biz */ public interface LoginRequestCallBack { //登錄成功回調(diào)方法 void loginSuccess(PersonBean personBean); //登錄失敗回調(diào)方法 void loginFailed(); }
3.4.2.View層:該通過(guò)Presenter與View進(jìn)行交互,這邊需要定義一個(gè)接口ILoginView:
package com.chinaztt.fda.ui.view; import com.chinaztt.fda.entity.PersonBean; /** * 當(dāng)前類(lèi)注釋:登錄頁(yè)面 相關(guān)操作 功能接口 * 項(xiàng)目名:FastDev4Android * 包名:com.chinaztt.fda.ui.view */ public interface ILoginView { //獲取用戶名 String getUserName(); //獲取密碼 String getPassword(); void showSuccessInfo(PersonBean personBean); void showFailedInfo(); }
有了上面的接口之后,我們就需要寫(xiě)我們的實(shí)現(xiàn)類(lèi)Activity了,就非常簡(jiǎn)單了
package com.chinaztt.fda.test; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import com.chinaztt.fda.entity.PersonBean; import com.chinaztt.fda.presenter.LoginPresenter; import com.chinaztt.fda.ui.R; import com.chinaztt.fda.ui.base.BaseActivity; import com.chinaztt.fda.ui.view.ILoginView; import com.chinaztt.fda.utils.Log; import org.androidannotations.annotations.EActivity; /** * 當(dāng)前類(lèi)注釋:MVP開(kāi)發(fā)模式實(shí)例 * 項(xiàng)目名:FastDev4Android * 包名:com.chinaztt.fda.test */ @EActivity public class MVPTestActivity extends BaseActivity implements ILoginView{ private static final String TAG="MVPTestActivity"; private EditText ed_username; private EditText ed_password; private Button btn_login; private LoginPresenter mLoginPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.mvp_test_layout); ed_username=(EditText)this.findViewById(R.id.ed_username); ed_password=(EditText)this.findViewById(R.id.ed_password); btn_login=(Button)this.findViewById(R.id.btn_login); mLoginPresenter=new LoginPresenter(this); btn_login.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mLoginPresenter.loginSystem(); } }); } /** * 進(jìn)行返回用戶名信息 * @return */ @Override public String getUserName() { return ed_username.getText().toString().trim(); } /** * 進(jìn)行返回用戶密碼信息 * @return */ @Override public String getPassword() { return ed_password.getText().toString().trim(); } /** * 登錄成功 回調(diào) * @param personBean */ @Override public void showSuccessInfo(PersonBean personBean) { Log.d(TAG,"showSuccessInfo:"+personBean.toString()); showToastMsgShort("登錄成功:"+personBean.toString()); } /** * 登錄失敗 回調(diào) */ @Override public void showFailedInfo() { Log.d(TAG,"showFailedInfo..."); showToastMsgShort("登錄失敗..."); } }
最后還少一個(gè)交互橋梁Presenter:
3.4.3.Presenter層:作為Model和View之間的交互橋梁,在本例中進(jìn)行執(zhí)行登錄操作,然后去Model業(yè)務(wù)中執(zhí)行登錄,最后把登錄結(jié)果信息返回給View層,就是這么簡(jiǎn)單:
package com.chinaztt.fda.presenter; import android.os.Handler; import com.chinaztt.fda.biz.IPersonBiz; import com.chinaztt.fda.biz.LoginRequestCallBack; import com.chinaztt.fda.biz.imp.PersonBizImp; import com.chinaztt.fda.entity.PersonBean; import com.chinaztt.fda.ui.view.ILoginView; import com.chinaztt.fda.utils.Log; /** * 當(dāng)前類(lèi)注釋:負(fù)責(zé)完成登錄界面View于Model(IPersonBiz)間的交互 * 項(xiàng)目名:FastDev4Android * 包名:com.chinaztt.fda.presenter */ public class LoginPresenter { private static final String TAG="LoginPresenter"; private ILoginView mLoginView; private IPersonBiz mPersonBiz; private Handler mHandler=new Handler(); public LoginPresenter(ILoginView view) { mLoginView = view; mPersonBiz = new PersonBizImp(); } public void loginSystem(){ mPersonBiz.login(mLoginView.getUserName(), mLoginView.getPassword(), new LoginRequestCallBack() { /** * 登錄成功 * @param personBean */ @Override public void loginSuccess(final PersonBean personBean) { Log.d(TAG, "登錄成功:" + personBean.toString()); mHandler.post(new Runnable() { @Override public void run() { mLoginView.showSuccessInfo(personBean); } }); } /** * 登錄失敗 */ @Override public void loginFailed() { Log.d(TAG,"登錄失敗..."); mHandler.post(new Runnable() { @Override public void run() { mLoginView.showFailedInfo();; } }); } }); } }
到此我們的MVP模式的例子就大體完成了,看一下上面的效果演示就OK了。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家學(xué)習(xí)Android軟件編程有所幫助。
相關(guān)文章
Android 實(shí)現(xiàn)定時(shí)任務(wù)的過(guò)程詳解
這篇文章主要介紹了Android 定時(shí)任務(wù)過(guò)程詳解的相關(guān)資料,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11Android懸浮窗按鈕實(shí)現(xiàn)點(diǎn)擊并顯示/隱藏多功能列表
這篇文章主要為大家詳細(xì)介紹了Android懸浮窗按鈕實(shí)現(xiàn)點(diǎn)擊并顯示/隱藏多功能列表,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07剖析Android Activity側(cè)滑返回的實(shí)現(xiàn)原理
在很多的App中,都會(huì)發(fā)現(xiàn)利用手指滑動(dòng)事件,進(jìn)行高效且人性化的交互非常有必要,那么它是怎么實(shí)現(xiàn)的呢,本文給大家解析實(shí)現(xiàn)原理,對(duì)Activity側(cè)滑返回實(shí)現(xiàn)代碼感興趣的朋友一起看看吧2021-06-06Android使用CountDownTimer類(lèi)實(shí)現(xiàn)倒計(jì)時(shí)鬧鐘
這篇文章主要為大家詳細(xì)介紹了Android使用CountDownTimer類(lèi)實(shí)現(xiàn)倒計(jì)時(shí)鬧鐘,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01android 傳感器(OnSensorChanged)使用介紹
當(dāng)傳感器的值發(fā)生變化時(shí),例如磁阻傳感器方向改變時(shí)會(huì)調(diào)用OnSensorChanged(). 當(dāng)傳感器的精度發(fā)生變化時(shí)會(huì)調(diào)用OnAccuracyChanged()方法2014-11-11Input系統(tǒng)按鍵事件的分發(fā)處理示例詳解
這篇文章主要為大家介紹了Input系統(tǒng)按鍵事件的分發(fā)處理示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01Kotlin與Java相互調(diào)用的完整實(shí)例
Kotlin的設(shè)計(jì)過(guò)程中就考慮到了與Java的互操作性,在Kotlin中可以直接調(diào)用既有的Java代碼,反過(guò)來(lái)在Java中也可以很流暢地使用Kotlin代碼,這篇文章主要給大家介紹了關(guān)于Kotlin與Java相互調(diào)用的相關(guān)資料,需要的朋友可以參考下2021-12-12Android系統(tǒng)檢測(cè)程序內(nèi)存占用各種方法
這篇文章主要介紹了Android系統(tǒng)檢測(cè)程序內(nèi)存占用各種方法,本文講解了檢查系統(tǒng)總內(nèi)存、檢查某個(gè)程序的各類(lèi)型內(nèi)存占用、檢查程序狀態(tài)、檢查程序各部分的內(nèi)存占用等內(nèi)容,需要的朋友可以參考下2015-03-03Flutter應(yīng)用框架搭建實(shí)現(xiàn)屏幕適配方案詳解
移動(dòng)設(shè)備多樣性,特別是Android的碎片化嚴(yán)重,存在各種各樣的分辨率,flutter跨平臺(tái)開(kāi)發(fā)又需要同時(shí)支持Android和IOS,為盡可能的還原設(shè)計(jì)圖效果提升用戶的體驗(yàn),根據(jù)設(shè)計(jì)稿設(shè)計(jì)屏幕ui的時(shí)候我們需要考慮到屏幕適配的問(wèn)題2022-11-11利用DrawerLayout和觸摸事件分發(fā)實(shí)現(xiàn)抽屜側(cè)滑效果
這篇文章主要為大家詳細(xì)介紹了利用DrawerLayout和觸摸事件分發(fā)實(shí)現(xiàn)抽屜側(cè)滑效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10