最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

Android開發(fā)學(xué)習(xí)教程(33)- Android MVP模式簡(jiǎn)單易懂的介紹方式

2023-01-30 01:15 作者:考研保研直通車  | 我要投稿

—— 你要悄悄拔尖,然后一鳴驚人。

Android MVP介紹?MVP 就得先介紹 MVC。在 MVC 模式中,Activity 應(yīng)該是屬于 View 這一層。而實(shí)質(zhì)上,它既承擔(dān)了 View,同時(shí)也包含一些 Controller 的東西在里面。這對(duì)于簡(jiǎn)單的數(shù)據(jù)更新工作會(huì)變得方便許多。)

舉個(gè)簡(jiǎn)單的例子,現(xiàn)在要實(shí)現(xiàn)一個(gè)飄雪的動(dòng)態(tài)壁紙,可以給雪花定義一個(gè)實(shí)體類 Snow,里面存放 XY 軸坐標(biāo)數(shù)據(jù),View 層當(dāng)然就是 SurfaceView(或者其他視圖),為了實(shí)現(xiàn)雪花飄的效果,可以啟動(dòng)一個(gè)后臺(tái)線程,在線程里不斷更新 Snow 實(shí)例里的坐標(biāo)值,這部分就是 Controller 的工作了,Controller 里還要定時(shí)更新 SurfaceView 上面的雪花。進(jìn)一步的話,可以在 SurfaceView 上監(jiān)聽用戶的點(diǎn)擊,如果用戶點(diǎn)擊,只通過 Controller 對(duì)觸摸點(diǎn)周圍的 Snow 的坐標(biāo)值進(jìn)行調(diào)整,從而實(shí)現(xiàn)雪花在用戶點(diǎn)擊后出現(xiàn)彈開等效果。具體的 MVC 模式請(qǐng)自行 Google。

MVP 模式

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

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

MVP 模式的核心思想:

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

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

MVP 模式的作用

  • 分離了視圖邏輯和業(yè)務(wù)邏輯,降低了耦合

  • Activity 只處理生命周期的任務(wù),代碼變得更加簡(jiǎn)潔

  • 視圖邏輯和業(yè)務(wù)邏輯分別抽象到了 View 和 Presenter 的接口中去,提高代碼的可閱讀性

  • Presenter 被抽象成接口,可以有多種具體的實(shí)現(xiàn),所以方便進(jìn)行單元測(cè)試

  • 把業(yè)務(wù)邏輯抽到 Presenter 中去,避免后臺(tái)線程引用著 Activity 導(dǎo)致 Activity 的資源無法被系統(tǒng)回收從而引起內(nèi)存泄露和 OOM

其中最重要的有三點(diǎn):

Activity 代碼變得更加簡(jiǎn)潔

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

避免 Activity 的內(nèi)存泄露

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

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

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

采用傳統(tǒng)的 MV 模式,一大堆異步任務(wù)和對(duì) UI 的操作都放在 Activity 里面,比如你可能從網(wǎng)絡(luò)下載一張圖片,在下載成功的回調(diào)里把圖片加載到 Activity 的 ImageView 里面,所以異步任務(wù)保留著對(duì) Activity 的引用。這樣一來,即使 Activity 已經(jīng)被切換到后臺(tái)(onDestroy 已經(jīng)執(zhí)行),這些異步任務(wù)仍然保留著對(duì) Activity 實(shí)例的引用,所以系統(tǒng)就無法回收這個(gè) Activity 實(shí)例了,結(jié)果就是 Activity Leak。

Android 的組件中,Activity 對(duì)象往往是在堆(Java Heap)里占最多內(nèi)存的,所以系統(tǒng)會(huì)優(yōu)先回收 Activity 對(duì)象,如果有 Activity Leak,APP 很容易因?yàn)閮?nèi)存不夠而 OOM。

采用 MVP 模式,只要在當(dāng)前的 Activity 的 onDestroy 里,分離異步任務(wù)對(duì) Activity 的引用,就能避免 Activity Leak。

方便進(jìn)行單元測(cè)試

一般單元測(cè)試都是用來測(cè)試某些新加的業(yè)務(wù)邏輯有沒有問題,如果采用傳統(tǒng)的代碼風(fēng)格(習(xí)慣性上叫做 MV 模式,少了 P),我們可能要先在 Activity 里寫一段測(cè)試代碼,測(cè)試完了再把測(cè)試代碼刪掉換成正式代碼,這時(shí)如果發(fā)現(xiàn)業(yè)務(wù)有問題又得換回測(cè)試代碼,咦,測(cè)試代碼已經(jīng)刪掉了!好吧重新寫吧……MVP 中,由于業(yè)務(wù)邏輯都在 Presenter 里,我們完全可以寫一個(gè) PresenterTest 的實(shí)現(xiàn)類繼承 Presenter 的接口,現(xiàn)在只要在 Activity 里把 Presenter 的創(chuàng)建換成 PresenterTest,就能進(jìn)行單元測(cè)試了,測(cè)試完再換回來即可。萬一發(fā)現(xiàn)還得進(jìn)行測(cè)試,那就再換成 PresenterTest 吧。

MVP 模式的使用

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

  • 1. 創(chuàng)建 IPresenter 接口,把所有業(yè)務(wù)邏輯的接口都放在這里,并創(chuàng)建它的實(shí)現(xiàn) PresenterCompl(在這里可以方便地查看業(yè)務(wù)功能,由于接口可以有多種實(shí)現(xiàn)所以也方便寫單元測(cè)試)

  • 2. 創(chuàng)建 IView 接口,把所有視圖邏輯的接口都放在這里,其實(shí)現(xiàn)類是當(dāng)前的 Activity/Fragment

  • 3. 由 UML 圖可以看出,Activity 里包含了一個(gè) IPresenter,而 PresenterCompl 里又包含了一個(gè) IView 并且依賴了 Model。Activity 里只保留對(duì) IPresenter 的調(diào)用,其它工作全部留到 PresenterCompl 中實(shí)現(xiàn)

  • 4. Model 并不是必須有的,但是一定會(huì)有 View 和 Presenter

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

MVP 模式簡(jiǎn)單實(shí)例

一個(gè)簡(jiǎn)單的登錄界面(實(shí)在想不到別的了╮( ̄▽ ̄”)╭),點(diǎn)擊 LOGIN 則進(jìn)行賬號(hào)密碼驗(yàn)證,點(diǎn)擊 CLEAR 則重置輸入。

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

首先來看看 LoginActivity:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
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 的工作,而且包含了一個(gè) ILoginPresenter,所有業(yè)務(wù)邏輯都是通過調(diào)用 ILoginPresenter 的具體接口來完成。所以 LoginActivity 的代碼看起來很舒爽,甚至有點(diǎn)愉♂悅呢 (/ω\*)。視力不錯(cuò)的你可能還看到了 ILoginView 接口的實(shí)現(xiàn),如果不懂為什么要這樣寫的話,可以先往下看,這里只要記住 “LoginActivity 實(shí)現(xiàn)了 ILoginView 接口”。再來看看 ILoginPresenter

1
2
3
4
5
6
public?interface?ILoginPresenter {
????void?clear();
????void?doLogin(String name, String passwd);
????void?setProgressBarVisiblity(int?visiblity);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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("<a href="https://yunjunet.cn/tag/mvp" mvp"target="_blank" style="color: rgb(102, 102, 102); cursor: pointer; transition: all 0.3s ease 0s;">mvp","mvp");
????}
}

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

通過 IVIew 和 IPresenter,把 Activity 的 UI Logic 和 Business Logic 分離開來,Activity just does its basic job! 至于 Model 嘛,還是原來 MVC 里的 Model。

再來看看 ILoginView,至于 ILoginView 的實(shí)現(xiàn)類呢,翻到上面看看 LoginActivity 吧:

1
2
3
4
5
6
public?interface?ILoginView {
????public?void?onClearText();
????public?void?onLoginResult(Boolean result,?int?code);
????public?void?onSetProgressBarVisibility(int?visibility);


Android開發(fā)學(xué)習(xí)教程(33)- Android MVP模式簡(jiǎn)單易懂的介紹方式的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
石台县| 关岭| 波密县| 布尔津县| 扬中市| 阳东县| 永泰县| 买车| 北安市| 梁山县| 拜城县| 寻乌县| 华池县| 井冈山市| 白水县| 永宁县| 抚顺县| 广州市| 德格县| 武定县| 涞水县| 诸城市| 巴林右旗| 莆田市| 徐闻县| 鄂伦春自治旗| 外汇| 延川县| 田林县| 连云港市| 苏尼特右旗| 翁牛特旗| 阿城市| 宝坻区| 德化县| 绿春县| 沂南县| 手机| 措勤县| 武城县| 峡江县|