国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發(fā)設計 > 正文

開發(fā)模式——MVP框架開發(fā)

2019-11-09 19:05:52
字體:
供稿:網(wǎng)友

前言

MVP作為一種MVC的演化版本在Android開發(fā)中受到了越來越多的關注,但在項目開發(fā)中選擇一種這樣的軟件設計模式需保持慎重心態(tài),一旦確定 使用MVP作為你App的開發(fā)模式那么你就最好堅持做下去,如果在使用MVP模式開發(fā)過程中發(fā)現(xiàn)問題而且坑越來越大,這時你想用MVC等來重新設計的話基 本上就等于推倒重來了。要知道在Android上MVP在現(xiàn)在為止并沒有統(tǒng)一的標準或者框架,不像SSH這三個成熟穩(wěn)重強而有力的三劍客支持推動著 java EE的開發(fā),所以在運用MVP時一定要做好自己的理解,并且盡量預知自己App各模塊的需求(客戶說改改改,我們就改改改 :-( )以便提前做好充分的設計工作。當然MVP既然能出現(xiàn)那么必然有它的優(yōu)點的,不然誰會理會這個冒出來的東西,下面就對Android中MVP做一些闡述。

MVP簡介

相信大家對MVC都是比較熟悉了:M-Model-模型、V-View-視圖、C-Controller-控制器,MVP作為MVC的演化版本,那么類似的MVP所對應的意義:M-Model-模型、V-View-視圖、P-PResenter-表示器。 從MVC和MVP兩者結合來看,Controlller/Presenter在MVC/MVP中都起著邏輯控制處理的角色,起著控制各業(yè)務流程的作用。而 MVP與MVC最不同的一點是M與V是不直接關聯(lián)的也是就Model與View不存在直接關系,這兩者之間間隔著的是Presenter層,其負責調(diào)控 View與Model之間的間接交互,MVP的結構圖如下所示,對于這個圖理解即可而不必限于其中的條條框框,畢竟在不同的場景下多少會有些出入的。在 Android中很重要的一點就是對UI的操作基本上需要異步進行也就是在MainThread中才能操作UI,所以對View與Model的切斷分離是 合理的。此外Presenter與View、Model的交互使用接口定義交互操作可以進一步達到松耦合也可以通過接口更加方便地進行單元測試。

模型(Model):負責處理數(shù)據(jù)的加載或者存儲,比如從網(wǎng)絡或本地數(shù)據(jù)庫獲取數(shù)據(jù)等;

視圖(View):負責界面數(shù)據(jù)的展示,與用戶進行交互;

協(xié)調(diào)者(Presenter):相當于協(xié)調(diào)者,是模型與視圖之間的橋梁,將模型與視圖分離開來。

這里寫圖片描述

Model

模型這一層之中做的工作是具體業(yè)務邏輯處理的實現(xiàn),都伴隨著程序中各種數(shù)據(jù)的處理,復雜一些的就明顯需要實現(xiàn)一個Interface來松耦合了。

View

視圖這一層體現(xiàn)的很輕薄,負責顯示數(shù)據(jù)、提供友好界面跟用戶交互就行。MVP下Activity和Fragment體現(xiàn)在了這一 層,Activity一般也就做加載UI視圖、設置監(jiān)聽再交由Presenter處理的一些工作,所以也就需要持有相應Presenter的引用。例 如,Activity上滾動列表時隱藏或者顯示Acionbar(Toolbar),這樣的UI邏輯時也應該在這一層。另外在View上輸入的數(shù)據(jù)做一些 判斷時,例如,EditText的輸入數(shù)據(jù),假如是簡單的非空判斷則可以作為View層的邏輯,而當需要對EditText的數(shù)據(jù)進行更復雜的比較時,如 從數(shù)據(jù)庫獲取本地數(shù)據(jù)進行判斷時明顯需要經(jīng)過Model層才能返回了,所以這些細節(jié)需要自己掂量。

Presenter

Presenter這一層處理著程序各種邏輯的分發(fā),收到View層UI上的反饋命令、定時命令、系統(tǒng)命令等指令后分發(fā)處理邏輯交由Model層做具體的業(yè)務操作。

在Android項目中,Activity和Fragment占據(jù)了大部分的開發(fā)工作。如果有一種設計模式(或者說代碼結構)專門是為優(yōu)化Activity和Fragment的代碼而產(chǎn)生的,你說這種模式重要不?這就是MVP設計模式。

按照MVC的分層,Activity和Fragment(后面只說Activity)應該屬于View層,用于展示UI界面,以及接收用戶的輸入,此外還要承擔一些生命周期的工作。Activity是在Android開發(fā)中充當非常重要的角色,特別是TA的生命周期的功能,所以開發(fā)的時候我們經(jīng)常把一些業(yè)務邏輯直接寫在Activity里面,這非常直觀方便,代價就是Activity會越來越臃腫,超過1000行代碼是常有的事,而且如果是一些可以通用的業(yè)務邏輯(比如用戶登錄),寫在具體的Activity里就意味著這個邏輯不能復用了。如果有進行代碼重構經(jīng)驗的人,看到1000+行的類肯定會有所顧慮。因此,Activity不僅承擔了View的角色,還承擔了一部分的Controller角色,這樣一來V和C就耦合在一起了,雖然這樣寫方便,但是如果業(yè)務調(diào)整的話,要維護起來就難了,而且在一個臃腫的Activity類查找業(yè)務邏輯的代碼也會非常蛋疼,所以看起來有必要在Activity中,把View和Controller抽離開來,而這就是MVP模式的工作了。

這里寫圖片描述

MVP模式的核心思想:

MVP把Activity中的UI邏輯抽象成View接口,把業(yè)務邏輯抽象成Presenter接口,Model類還是原來的Model。

這就是MVP模式,現(xiàn)在這樣的話,Activity的工作的簡單了,只用來響應生命周期,其他工作都丟到Presenter中去完成。從上圖可以看出,Presenter是Model和View之間的橋梁,為了讓結構變得更加簡單,View并不能直接對Model進行操作,這也是MVP與MVC最大的不同之處。

MVP模式的作用

分離了視圖邏輯和業(yè)務邏輯,降低了耦合Activity只處理生命周期的任務,代碼變得更加簡潔視圖邏輯和業(yè)務邏輯分別抽象到了View和Presenter的接口中去,提高代碼的可閱讀性Presenter被抽象成接口,可以有多種具體的實現(xiàn),所以方便進行單元測試把業(yè)務邏輯抽到Presenter中去,避免后臺線程引用著Activity導致Activity的資源無法被系統(tǒng)回收從而引起內(nèi)存泄露和OOM 其中最重要的有三點:

Activity 代碼變得更加簡潔 相信很多人閱讀代碼的時候,都是從Activity開始的,對著一個1000+行代碼的Activity,看了都覺得難受。 使用MVP之后,Activity就能瘦身許多了,基本上只有FindView、SetListener以及Init的代碼。其他的就是對Presenter的調(diào)用,還有對View接口的實現(xiàn)。這種情形下閱讀代碼就容易多了,而且你只要看Presenter的接口,就能明白這個模塊都有哪些業(yè)務,很快就能定位到具體代碼。Activity變得容易看懂,容易維護,以后要調(diào)整業(yè)務、刪減功能也就變得簡單許多。

方便進行單元測試 一般單元測試都是用來測試某些新加的業(yè)務邏輯有沒有問題,如果采用傳統(tǒng)的代碼風格(習慣性上叫做MV模式,少了P),我們可能要先在Activity里寫一段測試代碼,測試完了再把測試代碼刪掉換成正式代碼,這時如果發(fā)現(xiàn)業(yè)務有問題又得換回測試代碼,咦,測試代碼已經(jīng)刪掉了!好吧重新寫吧……

MVP中,由于業(yè)務邏輯都在Presenter里,我們完全可以寫一個PresenterTest的實現(xiàn)類繼承Presenter的接口,現(xiàn)在只要在Activity里把Presenter的創(chuàng)建換成PresenterTest,就能進行單元測試了,測試完再換回來即可。萬一發(fā)現(xiàn)還得進行測試,那就再換成PresenterTest吧。

避免 Activity 的內(nèi)存泄露 Android APP 發(fā)生OOM的最大原因就是出現(xiàn)內(nèi)存泄露造成APP的內(nèi)存不夠用,而造成內(nèi)存泄露的兩大原因之一就是Activity泄露(Activity Leak)(另一個原因是Bitmap泄露(Bitmap Leak))。

Java一個強大的功能就是其虛擬機的內(nèi)存回收機制,這個功能使得Java用戶在設計代碼的時候,不用像C++用戶那樣考慮對象的回收問題。然而,Java用戶總是喜歡隨便寫一大堆對象,然后幻想著虛擬機能幫他們處理好內(nèi)存的回收工作。可是虛擬機在回收內(nèi)存的時候,只會回收那些沒有被引用的對象,被引用著的對象因為還可能會被調(diào)用,所以不能回收。

Activity是有生命周期的,用戶隨時可能切換Activity,當APP的內(nèi)存不夠用的時候,系統(tǒng)會回收處于后臺的Activity的資源以避免OOM。

采用傳統(tǒng)的MV模式,一大堆異步任務和對UI的操作都放在Activity里面,比如你可能從網(wǎng)絡下載一張圖片,在下載成功的回調(diào)里把圖片加載到 Activity 的 ImageView 里面,所以異步任務保留著對Activity的引用。這樣一來,即使Activity已經(jīng)被切換到后臺(onDestroy已經(jīng)執(zhí)行),這些異步任務仍然保留著對Activity實例的引用,所以系統(tǒng)就無法回收這個Activity實例了,結果就是Activity Leak。Android的組件中,Activity對象往往是在堆(Java Heap)里占最多內(nèi)存的,所以系統(tǒng)會優(yōu)先回收Activity對象,如果有Activity Leak,APP很容易因為內(nèi)存不夠而OOM。

采用MVP模式,只要在當前的Activity的onDestroy里,分離異步任務對Activity的引用,就能避免 Activity Leak。

MVP模式的使用 這里寫圖片描述

上面一張簡單的MVP模式的UML圖,從圖中可以看出,使用MVP,至少需要經(jīng)歷以下步驟:

創(chuàng)建ipresenter接口,把所有業(yè)務邏輯的接口都放在這里,并創(chuàng)建它的實現(xiàn)PresenterCompl(在這里可以方便地查看業(yè)務功能,由于接口可以有多種實現(xiàn)所以也方便寫單元測試)創(chuàng)建IView接口,把所有視圖邏輯的接口都放在這里,其實現(xiàn)類是當前的Activity/Fragment由UML圖可以看出,Activity里包含了一個IPresenter,而PresenterCompl里又包含了一個IView并且依賴了Model。Activity里只保留對IPresenter的調(diào)用,其它工作全部留到PresenterCompl中實現(xiàn)Model并不是必須有的,但是一定會有View和Presenter

通過上面的介紹,MVP的主要特點就是把Activity里的許多邏輯都抽離到View和Presenter接口中去,并由具體的實現(xiàn)類來完成。這種寫法多了許多IView和IPresenter的接口,在某種程度上加大了開發(fā)的工作量,剛開始使用MVP的小伙伴可能會覺得這種寫法比較別扭,而且難以記住。只要在具體項目中多寫幾次,就能熟悉MVP模式的寫法,理解TA的意圖,以及享受其帶來的好處。

MVP模式簡單實例1

一個簡單的登錄界面,點擊LOGIN則進行賬號密碼驗證,點擊CLEAR則重置輸入。

項目結構看起來像是這個樣子的,MVP的分層還是很清晰的。在模塊下面再去創(chuàng)建model、view、presenter的子Package,當然也可以用model、view、presenter作為頂級的Package,然后把所有的模塊的model、view、presenter類都到這三個頂級Package中,就好像有人喜歡把項目里所有的Activity、Fragment、Adapter都放在一起一樣。

首先來看看LoginActivity

public class LoginActivity extends ActionBarActivity implements ILoginView, View.OnClickListener { private EditText editUser; private EditText editPass; private Button btnLogin; private Button btnClear; ILoginPresenter loginPresenter; private ProgressBar progressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //find view editUser = (EditText) this.findViewById(R.id.et_login_username); editPass = (EditText) this.findViewById(R.id.et_login_passWord); btnLogin = (Button) this.findViewById(R.id.btn_login_login); btnClear = (Button) this.findViewById(R.id.btn_login_clear); progressBar = (ProgressBar) this.findViewById(R.id.progress_login); //set listener btnLogin.setOnClickListener(this); btnClear.setOnClickListener(this); //init loginPresenter = new LoginPresenterCompl(this); loginPresenter.setProgressBarVisiblity(View.INVISIBLE); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.btn_login_clear: loginPresenter.clear(); break; case R.id.btn_login_login: loginPresenter.setProgressBarVisiblity(View.VISIBLE); btnLogin.setEnabled(false); btnClear.setEnabled(false); loginPresenter.doLogin(editUser.getText().toString(), editPass.getText().toString()); break; } } @Override public void onClearText() { editUser.setText(""); editPass.setText(""); } @Override public void onLoginResult(Boolean result, int code) { loginPresenter.setProgressBarVisiblity(View.INVISIBLE); btnLogin.setEnabled(true); btnClear.setEnabled(true); if (result){ Toast.makeText(this,"Login Success",Toast.LENGTH_SHORT).show(); startActivity(new Intent(this, HomeActivity.class)); } else Toast.makeText(this,"Login Fail, code = " + code,Toast.LENGTH_SHORT).show(); } @Override public void onSetProgressBarVisibility(int visibility) { progressBar.setVisibility(visibility); }}

從代碼可以看出LoginActivity只做了findView以及setListener的工作,而且包含了一個ILoginPresenter,所有業(yè)務邏輯都是通過調(diào)用ILoginPresenter的具體接口來完成。所以LoginActivity的代碼看起來很舒爽,甚至有點愉悅呢 。視力不錯的你可能還看到了ILoginView接口的實現(xiàn),如果不懂為什么要這樣寫的話,可以先往下看,這里只要記住LoginActivity實現(xiàn)了ILoginView接口創(chuàng)建接口ILoginPresenter

public interface ILoginPresenter { void clear(); void doLogin(String name, String passwd); void setProgressBarVisiblity(int visiblity);}

實現(xiàn)類LoginPresenterCompl

public class LoginPresenterCompl implements ILoginPresenter { ILoginView iLoginView; IUser user; Handler handler; public LoginPresenterCompl(ILoginView iLoginView) { this.iLoginView = iLoginView; initUser(); handler = new Handler(Looper.getMainLooper()); } @Override public void clear() { iLoginView.onClearText(); } @Override public void doLogin(String name, String passwd) { Boolean isLoginSuccess = true; final int code = user.checkUserValidity(name,passwd); if (code!=0) isLoginSuccess = false; final Boolean result = isLoginSuccess; handler.postDelayed(new Runnable() { @Override public void run() { iLoginView.onLoginResult(result, code); } }, 3000); } @Override public void setProgressBarVisiblity(int visiblity){ iLoginView.onSetProgressBarVisibility(visiblity); } private void initUser(){ user = new UserModel("mvp","mvp"); }}

從代碼可以看出,LoginPresenterCompl保留了ILoginView的引用,因此在LoginPresenterCompl里就可以直接進行UI操作了,而不用在Activity里完成。這里使用了ILoginView引用,而不是直接使用Activity,這樣一來,如果在別的Activity里也需要用到相同的業(yè)務邏輯,就可以直接復用LoginPresenterCompl類了(一個Activity可以包含一個以上的Presenter,總之,需要什么業(yè)務就new什么樣的Presenter),這也是MVP的核心思想

ILoginView

public interface ILoginView { public void onClearText(); public void onLoginResult(Boolean result, int code); public void onSetProgressBarVisibility(int visibility);}

MVP模式簡單實例2(也是登錄)

MVP登錄實例(無butterKnife) mvp:也是一種開發(fā)方式, 也是分三層 M:模型model javaBean V:視圖view layout+View集合 P:presenter 協(xié)調(diào)者 線程 邏輯 調(diào)用 低耦低:引入三個接口來獲得 比mvc更低的耦合度 分包:使用分層更加清析) 【 耦合度】 :類的引用關系的強弱. (接口引用會比實現(xiàn)類引用耦合度低) 結論:增加接口引用可以降低耦合度

1.分包

modelviewpresenter

2.編寫P presenter 包含 邏輯與線程 用來處理用戶對界面的操作

創(chuàng)建ILoginPresenter

public interface ILoginPresenter { //處理登錄數(shù)據(jù) void login(IUser user);}

接口實現(xiàn)類LoginPresenter

public class LoginPresenter implements ILoginPresenter { private ILoginView mView; public LoginPresenter(ILoginView view) { this.mView = view; } //處理登錄數(shù)據(jù) @Override public void login(IUser user) { //2.1.格式校驗 if (user.isAccountPwdNotEmpty()) { //進行登錄業(yè)務邏輯 //2.2.顯示Loading mView.showLoading(true); //2.3.延時處理 new Handler().postDelayed(new Runnable() { @Override public void run() { //2.4.發(fā)送參數(shù)給服務端 獲取校驗結果 int code = new Random().nextInt(2); if (code == 0) { //2.5.調(diào)用界面顯示登錄成功 mView.showToast("登錄成功"); } else { //2.6.調(diào)用界面顯示登錄失敗 mView.showToast("賬號密碼出錯"); } //2.7調(diào)用界面隱藏loading mView.showLoading(false); } }, 3000); } else { //調(diào)用界面進行吐司提示 mView.showToast("賬號密碼不能為空"); } }}

3.編寫 V 界面顯示

ILoginView

public interface ILoginView { public void showLoading(boolean show) ; public void showToast(String msg) ;}

MainActivity

public class MainActivity extends AppCompatActivity implements ILoginView { private ILoginPresenter loginPresenter; private ProgressBar pbar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); pbar = (ProgressBar) findViewById(R.id.pbar); loginPresenter = new LoginPresenter(this); } public void login(View view) { EditText accountEt= (EditText) findViewById(R.id.account_et); EditText pwdEt= (EditText) findViewById(R.id.pwd_et); String inputAccount=accountEt.getText().toString(); String inputPwd=pwdEt.getText().toString(); //調(diào)用p進行登錄 loginPresenter.login(new User(inputAccount,inputPwd)); } @Override public void showLoading(boolean show) { pbar.setVisibility(show?View.VISIBLE:View.INVISIBLE); } @Override public void showToast(String msg) { Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); }}

4.編寫 M數(shù)據(jù)校驗

IUser

public interface IUser { //格式校驗交給Model boolean isAccountPwdNotEmpty();}

實現(xiàn)類User

public class User implements IUser { public String username; public String pwd; public User(String username, String pwd) { this.username = username; this.pwd = pwd; } //格式校驗交給Model @Override public boolean isAccountPwdNotEmpty() { if (!TextUtils.isEmpty(username) && !TextUtils.isEmpty(pwd)) { return true; } else { return false; } }}

總結:因為代碼主要邏輯寫到P里面 得到一個Activity/Fragmnet代碼行數(shù)精簡

參考資料博客


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 阆中市| 苏尼特左旗| 阳东县| 洱源县| 赞皇县| 阳朔县| 焉耆| 旬邑县| 江孜县| 靖安县| 广河县| 岑溪市| 太原市| 杨浦区| 沙洋县| 璧山县| 青神县| 丰顺县| 内乡县| 焦作市| 宁国市| 海林市| 辛集市| 武冈市| 九江市| 东城区| 崇仁县| 万山特区| 汝阳县| 甘肃省| 淳化县| 印江| 仙游县| 稻城县| 乐都县| 游戏| 石门县| 科尔| 长汀县| 蓝田县| 盘锦市|