MVP 全稱:Model-View-Presenter
MVP 是從經(jīng)典的模式MVC演變而來(lái)瑞佩,它們的基本思想有相通的地方:Controller/Presenter負(fù)責(zé)邏輯的處理,Model提供數(shù)據(jù),View負(fù)責(zé)顯示。
模式描述
Model-view-presenter (MVP) 是使用者界面 設(shè)計(jì)模式的一種一疯,被廣范用于便捷自動(dòng)化單元測(cè)試和在呈現(xiàn)邏輯中改良分離關(guān)注點(diǎn)(separation of concerns)肘交。
- Model 定義使用者界面所需要被顯示的資料模型,一個(gè)模型包含著相關(guān)的業(yè)務(wù)邏輯饲常。
- View 視圖為呈現(xiàn)使用者界面的終端,用以表現(xiàn)來(lái)自 Model 的資料狼讨,和使用者命令路由再經(jīng)過(guò) Presenter 對(duì)事件處理后的資料贝淤。
- Presenter 包含著元件的事件處理,負(fù)責(zé)檢索 Model 取得資料政供,和將取得的資料經(jīng)過(guò)格式轉(zhuǎn)換與 View 進(jìn)行溝通播聪。
下面一個(gè)圖就能很好展示它們之間的關(guān)系
那我們開(kāi)始實(shí)例講解
現(xiàn)在我們做一個(gè)小Demo朽基。輸入一個(gè)qq號(hào),能查到QQ號(hào)碼吉兇
請(qǐng)求示例:
http://api.jisuapi.com/qqluck/query?qq=22222&appkey=yourappkey
Json返回示例
{
"status": "0",
"msg": "ok",
"result": {
"qq": "22222",
"score": "62",
"luck": "兇",
"content": "煩悶懊惱离陶,事事難展稼虎,自防災(zāi)禍,始免困境",
"character": "要面包不要愛(ài)情",
"characterdetail": "責(zé)任心重招刨,尤其對(duì)工作充滿熱誠(chéng)霎俩,是個(gè)徹頭徹尾工作狂。但往往因?yàn)檫^(guò)分專注職務(wù)沉眶,而忽略身邊的家人及朋友打却,是個(gè)寧要面包不需要愛(ài)情的理性主義者。"
}
}
項(xiàng)目結(jié)構(gòu)
其中api 是接口谎倔。bean 是javabean 用來(lái)作數(shù)據(jù)模型学密。model來(lái)進(jìn)行網(wǎng)絡(luò)請(qǐng)求獲取數(shù)據(jù)。presenter來(lái)進(jìn)行事件處理传藏。把處理好的數(shù)據(jù)通過(guò)一定方式傳給view 腻暮,也就是我們的activity,然后展示給用戶看毯侦。
所以這就結(jié)構(gòu)就看著非常簡(jiǎn)單哭靖。model 只做網(wǎng)絡(luò)請(qǐng)求,presenter把請(qǐng)求的數(shù)據(jù)處理好 傳給view侈离,然后view在展示給用戶看试幽。這中間view和model是沒(méi)有任何的關(guān)聯(lián),只是有個(gè)中間著presenter來(lái)進(jìn)行相應(yīng)的處理
我們接下來(lái)先把a(bǔ)pi 和bean 填上卦碾,代碼如下
網(wǎng)絡(luò)請(qǐng)求用的retrofit框架
compile 'com.squareup.retrofit2:retrofit:2.2.0'
compile 'com.squareup.retrofit2:converter-gson:2.2.0'
MyService類铺坞,簡(jiǎn)單封裝網(wǎng)絡(luò)請(qǐng)求
public class MyService {
public static String baseUrl="http://api.jisuapi.com/qqluck/";
public static Retrofit mRetrofit=new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
public static <T> T getApi(Class<T> mClass){
return mRetrofit.create(mClass);
}
}
DemoApi類, api接口類
public interface DemoApi {
@POST("query")
Call<QqluckData> loadqQuery(@Query("qq") int qq, @Query("appkey") String appkey);
}
接下來(lái)開(kāi)始寫view 洲胖。因?yàn)槲覀円鲆粋€(gè)qq兇吉济榨,那么就要把展示結(jié)果給用戶看。所以先寫一個(gè)接口.只有一個(gè)方法绿映。用來(lái)接收presenter傳過(guò)了的數(shù)據(jù)擒滑,展示給用戶看
public interface IMainView {
void showInfo(String response);
}
然后用Maintivivty來(lái)繼承IMainView接口,實(shí)現(xiàn)其中的方法叉弦,獲得數(shù)據(jù)丐一,展示給用戶看,代碼如下
public class MainActivity extends AppCompatActivity implements IMainView {
private TextView mluck;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mluck= (TextView) findViewById(R.id.luck);
}
public void onClick(View v){
switch (v.getId()){
case R.id.send:
break;
}
}
@Override
public void showInfo(String response) {
mluck.setText(response);
}
}
現(xiàn)在基本完成了一半淹冰,下面接著寫model 的網(wǎng)絡(luò)請(qǐng)求
根據(jù)上面的請(qǐng)求示例库车,我們需要傳一個(gè)qq號(hào),和一個(gè)appkey樱拴。appkey去注冊(cè)申請(qǐng)一個(gè)就好柠衍,在這就不多說(shuō)潘拱,另外需要一個(gè)Callback回調(diào)函數(shù)。需要把請(qǐng)求到的數(shù)據(jù)傳給presenter拧略。model也到這差不多了。
public class MainModel {
public void getData( int qq, String appkey, Callback<QqluckData> mCallback) {
DemoApi api = MyService.getApi(DemoApi.class);
Call<QqluckData> qqluckDataCall = api.loadqQuery(qq, appkey);
qqluckDataCall.enqueue(mCallback);
}
}
現(xiàn)在就開(kāi)始寫presenter瘪弓,代碼如下
public class MainPresenter {
public IMainView mIMainView;//view 的引用
public MainModel mMainModel;//model的引用
public MainPresenter(IMainView IMainView) {
mIMainView = IMainView;
mMainModel=new MainModel();
}
public void load(int qq){//加載數(shù)據(jù)垫蛆。通過(guò)model 來(lái)獲取
String appkey="34865d1e2ff7170f";
mMainModel.getData(qq, appkey, new Callback<QqluckData>() {
@Override
public void onResponse(Call<QqluckData> call, Response<QqluckData> response) {
//獲取到數(shù)據(jù)
QqluckData body = response.body();
//調(diào)用showInfo方法,傳遞數(shù)據(jù)腺怯。展示給用戶看
mIMainView.showInfo("luck:"+body.getResult().getLuck()+"\ncontent:"+body.getResult().getContent());
}
@Override
public void onFailure(Call<QqluckData> call, Throwable t) {
}
});
}
}
presenter 代碼沒(méi)多少袱饭。持有view 和midel的引用。 當(dāng)view初始化presenter時(shí)會(huì)把view 傳過(guò)來(lái)呛占。presenter內(nèi)部在持有Model的引用虑乖。當(dāng)用戶發(fā)起qq測(cè)吉兇功能時(shí),由view 調(diào)用presenter的load方法晾虑。presenter在由model去發(fā)送網(wǎng)絡(luò)請(qǐng)求疹味。把數(shù)據(jù)結(jié)果在傳回給view。下面我在把完整的Maintivit 貼出來(lái)
public class MainActivity extends AppCompatActivity implements IMainView {
private TextView mluck;
private MainPresenter mMainPresenter;
private EditText mEdqq;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mluck= (TextView) findViewById(R.id.luck);
mEdqq= (EditText) findViewById(R.id.ed_qq);
//onCreate時(shí)初始化MainPresenter帜篇,因?yàn)楫?dāng)前已繼承IMainView糙捺,直接傳this即可。這個(gè)時(shí)候MainPresenter就持有IMainView的引用了
mMainPresenter=new MainPresenter(this);
}
public void onClick(View v){
switch (v.getId()){
case R.id.send:
if(TextUtils.isEmpty(mEdqq.getText().toString())){
return;
}
//發(fā)送請(qǐng)求笙隙。調(diào)取MainPresenter.load的方法
mMainPresenter.load(Integer.parseInt(mEdqq.getText().toString()));
break;
}
}
@Override
public void showInfo(String response) {
//展示結(jié)果
mluck.setText(response);
}
}
好了洪灯。到此一個(gè)簡(jiǎn)單的mvp demo已經(jīng)寫完了 。竟痰∏┕常或許有很多人在此覺(jué)得mvp 太麻煩了。一個(gè)簡(jiǎn)單的網(wǎng)絡(luò)請(qǐng)求坏快,寫的這么多類铅檩,這么多代碼。但是我要說(shuō)的是莽鸿,你看我們的activity類柠并。代碼是不是非常少,是不是很簡(jiǎn)潔富拗,其他的也都分工很明確臼予。耦合性也很低。便于后期維護(hù)啃沪,這個(gè)時(shí)候mvp 的優(yōu)點(diǎn)便體現(xiàn)出來(lái)了粘拾。
對(duì)了。展示一下結(jié)果
MVP的優(yōu)點(diǎn)
- 1创千、模型與視圖完全分離缰雇,我們可以修改視圖而不影響模型
- 2入偷、可以更高效地使用模型,因?yàn)樗械慕换ザ及l(fā)生在一個(gè)地方——Presenter內(nèi)部
- 3械哟、我們可以將一個(gè)Presenter用于多個(gè)視圖疏之,而不需要改變Presenter的邏輯。這個(gè)特性非常的有用暇咆,因?yàn)橐晥D的變化總是比模型的變化頻繁锋爪。
- 4、如果我們把邏輯放在Presenter中爸业,那么我們就可以脫離用戶接口來(lái)測(cè)試這些邏輯(單元測(cè)試)
MVP的缺點(diǎn)
- 由于對(duì)視圖的渲染放在了Presenter中其骄,所以視圖和Presenter的交互會(huì)過(guò)于頻繁。還有一點(diǎn)需要明白扯旷,如果Presenter過(guò)多地渲染了視圖拯爽,往往會(huì)使得它與特定的視圖的聯(lián)系過(guò)于緊密。一旦視圖需要變更钧忽,那么Presenter也需要變更了毯炮。比如說(shuō),原本用來(lái)呈現(xiàn)Html的Presenter現(xiàn)在也需要用于呈現(xiàn)Pdf了耸黑,那么視圖很有可能也需要變更否副。
如果有什么問(wèn)題不懂∑榉唬或者我那個(gè)地方寫的有問(wèn)題备禀。歡迎小伙伴提出。最后說(shuō)一下奈揍。MVP只是個(gè)設(shè)計(jì)模式曲尸。不一定非要按部就班。我們要活學(xué)活用男翰。適合自己的才是最好的另患。
源代碼
https://github.com/ccicec/AndroidMvpDemo/tree/master
A New Day