Android:“萬(wàn)能”Activity重構(gòu)篇
前言
寫(xiě)Android:如何編寫(xiě)“萬(wàn)能”的Activity的這篇文章到現(xiàn)在已經(jīng)好久了,但是由于最近事情較多,寫(xiě)重構(gòu)篇的計(jì)劃就一直被無(wú)情的耽擱下來(lái)了,借這幾天還算有點(diǎn)空余時(shí)間,把自己這樁心事了解下。
其實(shí)大家也知道Android:如何編寫(xiě)“萬(wàn)能”的Activity的這篇文章只是個(gè)引子,其實(shí)我真正想引出的是mvp設(shè)計(jì)模式,因?yàn)樽罱约鹤罱谟胢vp做項(xiàng)目,自己對(duì)mvp有一些感悟,因此我將用mvp進(jìn)行“萬(wàn)能”activity的重構(gòu)。
同時(shí)也有一些朋友與我交流mvp,他們會(huì)被mvp中的m,v,p都應(yīng)該放什么邏輯而困惑?會(huì)被mvp中的m應(yīng)該怎么寫(xiě)而困惑?會(huì)被一個(gè)界面,怎么按照mvp來(lái)進(jìn)行重構(gòu)感到困惑?會(huì)被listview的adapter應(yīng)該放在m層還是p層困惑?
我希望通過(guò)本文的講解能幫助大家對(duì)mvp有一個(gè)更深的了解,現(xiàn)在進(jìn)入主題內(nèi)容。
正文內(nèi)容
本文內(nèi)容分為2部分:
- 帶你了解mvp
- 使用mvp對(duì)“萬(wàn)能”Activity進(jìn)行重構(gòu)
第一部分會(huì)深入了解mvp到底是什么,它的好處等知識(shí)。第二部分會(huì)講解如何利用mvp來(lái)重構(gòu)“萬(wàn)能”Activity。
帶你了解mvp
任何軟件都是以數(shù)據(jù)為中心,為了能與用戶進(jìn)行交互,就需要提供界面支持用戶對(duì)數(shù)據(jù)進(jìn)行增刪改查操作。
不管是mvc,mvp還是mvvm始終都在做一件事情:怎么樣能更好的解決數(shù)據(jù)與界面之間的關(guān)系,以達(dá)到數(shù)據(jù)與界面之間的耦合更低,代碼的復(fù)用性更高,代碼的可測(cè)性更好。
本文的重點(diǎn)是講解mvp,因此讓我們開(kāi)始了解下mvp是怎么組織數(shù)據(jù)與界面之間的關(guān)系的。我們先從mvp的結(jié)構(gòu)圖說(shuō)起。
mvp的面容
網(wǎng)上有些關(guān)于mvp的結(jié)構(gòu)圖基本是以下樣子
mvp結(jié)構(gòu)圖.png
我覺(jué)得這張圖是有問(wèn)題的,問(wèn)題在于presenter把請(qǐng)求轉(zhuǎn)交給model,model應(yīng)該把處理結(jié)果返回給presenter,這張圖是沒(méi)有反映這個(gè)過(guò)程的。
正確的mvp的結(jié)構(gòu)圖是這樣子的
mvp結(jié)構(gòu)
我們先看下能從這張圖中得到哪些信息?
- mvp的分層結(jié)構(gòu)特別類(lèi)似于網(wǎng)絡(luò)的七層協(xié)議,每層只知道自己依賴(lài)層的細(xì)節(jié)。
- 這種分層的好處是:層與層之間的耦合性低,模塊的復(fù)用性高,可維護(hù)性更好,每層可以單獨(dú)存在,這樣可測(cè)性更好。
- 數(shù)據(jù)流的走向可以是:view-->presenter-->model-->presenter-->view,這種數(shù)據(jù)流一般出現(xiàn)的場(chǎng)景是用戶在界面觸發(fā)了一個(gè)事件的情形下
- 數(shù)據(jù)流的走向也可以是:model-->presenter-->view,這種數(shù)據(jù)流一般出現(xiàn)于比如通過(guò)長(zhǎng)鏈接接收消息的場(chǎng)景。
- 不管數(shù)據(jù)流是怎樣的一個(gè)流動(dòng)走向,始終有一個(gè)原則是:數(shù)據(jù)流不能跨層流動(dòng),即層與層不能跨層通信。
看了mvp的整體結(jié)構(gòu)圖,我們以從底層到上層的順序依次來(lái)介紹model,presenter,view。
model
先說(shuō)下一些關(guān)于model的錯(cuò)誤理解:
- model是實(shí)體類(lèi)的集合
- 比如從json中解析數(shù)據(jù)的代碼應(yīng)該放在presenter中
- model與mvc中的model是一樣的
關(guān)于model的正確理解我們會(huì)在文中看到。
數(shù)據(jù)加工處理廠
通過(guò)應(yīng)用mvp后的感受,我個(gè)人的感覺(jué)model是最難寫(xiě)的一層,并且也是最難懂的,因?yàn)閙odel是整個(gè)應(yīng)用或界面的數(shù)據(jù)加工處理廠,所謂數(shù)據(jù)加工廠就是對(duì)數(shù)據(jù)的獲取,數(shù)據(jù)的解析,數(shù)據(jù)的存儲(chǔ),數(shù)據(jù)的分發(fā),數(shù)據(jù)的增刪改查等操作。意思就是凡是涉及到數(shù)據(jù)操作都是在model進(jìn)行的,所以model不僅僅只是實(shí)體類(lèi)的集合,同時(shí)還包含關(guān)于數(shù)據(jù)的各種處理操作。
三種數(shù)據(jù)源
數(shù)據(jù)的數(shù)據(jù)源有三種:內(nèi)存,磁盤(pán)(文件或數(shù)據(jù)庫(kù)等),網(wǎng)絡(luò)。為了提升app的性能,有必要把經(jīng)常訪問(wèn)的數(shù)據(jù)臨時(shí)存入內(nèi)存中;同時(shí)也為了提升app性能和為用戶省流量省電,有必要把數(shù)據(jù)存入磁盤(pán)中;還有的數(shù)據(jù)是有必要從網(wǎng)絡(luò)讀取的。三個(gè)數(shù)據(jù)源不一定同時(shí)存在,比如不與網(wǎng)絡(luò)交互的app,不存在網(wǎng)絡(luò)數(shù)據(jù)源。所以凡是涉及到關(guān)于數(shù)據(jù)發(fā)生于三個(gè)數(shù)據(jù)源加工處理的操作的代碼都要放在model中。
model為上層提供的服務(wù)
model從黑盒的角度來(lái)看為上層(指依賴(lài)于model的層比如present)提供的服務(wù)無(wú)非就2種:model為上層提供數(shù)據(jù),model處理上層傳遞的數(shù)據(jù)
model為上層提供數(shù)據(jù)
上層會(huì)從model中去數(shù)據(jù),那model會(huì)從三數(shù)據(jù)源中取數(shù)據(jù),取的順序是
- 先內(nèi)存,內(nèi)存取到數(shù)據(jù)返回
- 其次磁盤(pán),磁盤(pán)取到數(shù)據(jù),如有必要把數(shù)據(jù)存儲(chǔ)在內(nèi)存中,則需要進(jìn)行存儲(chǔ),返回?cái)?shù)據(jù)
- 最后網(wǎng)絡(luò),網(wǎng)絡(luò)取到數(shù)據(jù),如有必要在磁盤(pán)或內(nèi)存中存儲(chǔ),則進(jìn)行存儲(chǔ),返回?cái)?shù)據(jù)
上面的取數(shù)據(jù)過(guò)程是最簡(jiǎn)單的情況,復(fù)雜些還會(huì)涉及到從內(nèi)存或磁盤(pán)中取到的數(shù)據(jù)是否過(guò)期,過(guò)期的話就應(yīng)該從網(wǎng)絡(luò)獲取。從網(wǎng)絡(luò)取得數(shù)據(jù)后需要把內(nèi)存或磁盤(pán)的數(shù)據(jù)更新。
model處理上層傳遞的數(shù)據(jù)
model接收到上層傳遞的數(shù)據(jù)后,model會(huì)依次把數(shù)據(jù)扔給三個(gè)數(shù)據(jù)源去處理,有可能三個(gè)數(shù)據(jù)源都會(huì)處理數(shù)據(jù),有可能只是其中一個(gè)處理,model會(huì)把處理的結(jié)果返回。
所以model會(huì)把解析好的數(shù)據(jù)提供給上層,上層對(duì)于數(shù)據(jù)的來(lái)源完全是透明的,上層完全不需要關(guān)心數(shù)據(jù)到底是來(lái)自?xún)?nèi)存,還是磁盤(pán)甚至是網(wǎng)絡(luò)。同理上層只需要的把數(shù)據(jù)扔給model,上層唯一做的事情就是愉快的等待處理結(jié)果。
tip
mvc中的model是要和view進(jìn)行交互的,而mvp中的model不會(huì)知道任何view的細(xì)節(jié)。
model中的所有操作都發(fā)生于普通線程。
關(guān)于model的介紹先到此,我們?cè)趤?lái)看下presenter。
presenter
presenter翻譯成漢語(yǔ)的意思是主持人,提出者。從它的意思可以看出它有控制全場(chǎng)的作用。首先presenter是處于mvp的中間層,在view和model中起一個(gè)承上啟下的作用。presenter會(huì)把view交給自己的命令進(jìn)行一定的校驗(yàn)等操作交給model處理,會(huì)把model處理的結(jié)果交給view。
presenter封裝業(yè)務(wù)
presenter不僅起一個(gè)橋梁的作用,它還會(huì)把業(yè)務(wù)邏輯代碼給包攬下來(lái)。這樣就可以減輕Activity的負(fù)擔(dān)了,讓Activity全心全意做它的view工作。那估計(jì)就有朋友犯迷糊了,哪些代碼屬于業(yè)務(wù)邏輯呢?比如一些校驗(yàn)代碼?;蛘呖梢赃@樣想只要是不屬于view和model的代碼基本都可以放在presenter中。
presenter負(fù)責(zé)刷新view
mvc或以前的關(guān)于view的寫(xiě)法一般都是這樣,view在接收到數(shù)據(jù)后,自己來(lái)進(jìn)行view的刷新或其他操作。但是mvp中presenter負(fù)責(zé)對(duì)view進(jìn)行刷新,比如從model獲取的數(shù)據(jù),presenter會(huì)根據(jù)獲取的數(shù)據(jù)成功與否來(lái)通知view應(yīng)該是顯示成功界面還是失敗界面。這樣就讓Activity變的更輕了,變成了聽(tīng)別人指揮的傻白甜了。這時(shí)候的presenter就有點(diǎn)主持人,掌控者的味道了。
presenter持有的線程
Android中view的操作需要在ui線程里執(zhí)行,其他耗時(shí)操作需要在普通線程執(zhí)行。presenter會(huì)持有這2種線程:ui線程,普通線程。刷新view時(shí),它切換為ui線程進(jìn)行刷新,從model取數(shù)據(jù)切換為普通線程。假如使用rxjava的話,就特別簡(jiǎn)單了關(guān)于線程切換的事情。
tip
presenter從model中獲取的數(shù)據(jù)就是解析好的數(shù)據(jù),不需要出現(xiàn)解析數(shù)據(jù)的代碼。
接著我們來(lái)看下view。
view
view層就很好理解了,就是用戶直接看到的界面,mvp中的view是很省心的,比如更新view,接收數(shù)據(jù)。這些操作它都不需要操心,也不需要知道數(shù)據(jù)到底來(lái)自哪里,給我啥我顯示啥就可以了。
一個(gè)view可以同時(shí)擁有多個(gè)presenter,也可以只有一個(gè)presenter。
Android中的Activity,F(xiàn)ragment在mvp中是作為view來(lái)使用的,這些Activity,F(xiàn)ragment的責(zé)任就小了,只關(guān)心界面相關(guān)的事情足矣。
各種Adapter是放在view層的。
總結(jié)
我們初步認(rèn)識(shí)了mvp,mvp中的model,present,view到底是什么,他們之間的關(guān)系是什么樣的,這只是初步認(rèn)識(shí)mvp,關(guān)于mvp中還有很多細(xì)節(jié)需要介紹,比如android clean architecture 中model和presenter之間多了一層interactor,多的這層interactor是用來(lái)做什么的,model層是怎么架構(gòu)的。google mvpmodel層要比android clean architecture 簡(jiǎn)單等,希望能在我后面的章節(jié)看到相關(guān)關(guān)于每層的詳細(xì)介紹。我們開(kāi)始進(jìn)入我們的重構(gòu)"萬(wàn)能"Activity的部分。
使用mvp設(shè)計(jì)模式對(duì)"萬(wàn)能"Activity進(jìn)行重構(gòu)
回憶下“萬(wàn)能”Activity的樣子
我在上篇文章的“萬(wàn)能”的LoginActivity基礎(chǔ)上增加了登錄對(duì)話框的功能,“萬(wàn)能”LoginActivity的代碼如下:
public LoginActivity extends Activity{ private EditText mUserNameView, mPasswordView; private Button mLoginView; public void initViews(){ ....... 各種findViewById.....代碼 //給登陸按鈕加監(jiān)聽(tīng)器 mLoginView.OnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String userName = mUserNameView.getText(); String password = mPasswordView.getText(); //驗(yàn)證用戶輸入的密碼是否合法 if(!validate(userName) || !validate(password)){ 告訴用戶輸入的用戶名或密碼不合法 } else{ //開(kāi)始登陸 login(userName,password); } } }); } //登陸方法,用偽代碼來(lái)寫(xiě)下網(wǎng)絡(luò)請(qǐng)求 private void login(String userName,String password){ //增加登錄進(jìn)度對(duì)話框給用戶友好用戶體驗(yàn) 顯示登錄進(jìn)度對(duì)話框... HttpClient.getInstance().login(userName,password, new ResponseListener(){ public void failed(Failed failed){ 把登錄進(jìn)度對(duì)話框消失... 做失敗相關(guān)的處理工作,比如給用戶提示 把密碼輸入框清空,還比如登陸次數(shù)限制等 } public void success(Response response){ 把登錄進(jìn)度對(duì)話框消失... 做成功相關(guān)的處理工作 //暫且把用戶信息的類(lèi)叫做UserInfo,從json中解析數(shù)據(jù),假設(shè)response.getContent()存在 String jsonContent = response.getContent(); JsonObject jsonObject = new JsonObject(jsonContent); UserInfo userInfo = new UserInfo(); userInfo.name = jsonObject.optString("name"); userInfo.userId = jsonObject.optString("userId"); 其他字段的解析...... //保存userInfo信息到數(shù)據(jù)表中,假設(shè)userDatabase已經(jīng)存在 userDatabase.save(userInfo); 跳到app的主頁(yè) } }); } //驗(yàn)證給定的字符串是否合法,true 合法,false 不合法 private boolean validate(String str){ } }
我們回憶了“萬(wàn)能”LoginActivity的代碼后,開(kāi)始重構(gòu)。
開(kāi)始重構(gòu)
model
在使用mvp時(shí),我一般有個(gè)習(xí)慣就是首先從model->presenter->view的順序?qū)懘a,所以重構(gòu)“萬(wàn)能”LoginActivity也先從model開(kāi)始。前半部分關(guān)于model介紹過(guò),model從黑盒的角度來(lái)說(shuō)只有2個(gè)功能:一個(gè)是輸出數(shù)據(jù),一個(gè)是輸入數(shù)據(jù)。因此登錄中presenter只需要把賬號(hào),密碼交給model,presenter唯一做的事情就是監(jiān)聽(tīng)登錄狀態(tài)即可。model會(huì)把presenter傳遞的賬號(hào),密碼交給服務(wù)器,model在把服務(wù)器返回的數(shù)據(jù)進(jìn)行解析,存儲(chǔ)在磁盤(pán)或內(nèi)存中,把解析好的數(shù)據(jù)傳遞給presenter。那我們看下偽代碼:
//管理登錄的類(lèi),它是單例的,這就不寫(xiě)單例方法了 public class LoginManager{ //登錄的監(jiān)聽(tīng)器 public static interface LoginListener{ //登錄成功 void onSuccessLogin(UserEntity user); //登錄失敗 void onFailedLogin(Failed failed); } //登錄方法 public void login(String name,String password,final LoginListener loginListener){ //假設(shè)HttpClient是一個(gè)請(qǐng)求網(wǎng)絡(luò)服務(wù)的類(lèi) HttpClient.getInstance().login(userName,password, new ResponseListener(){ public void failed(Failed failed){ loginListener.onFailedLogin(failed); } public void success(Response response){ //假設(shè)UserParse類(lèi)已經(jīng)存在,主要用來(lái)從response中解析UserEntity UserEntity userEntity = UserParse(response.getContent()); //假設(shè)userDatabase是數(shù)據(jù)庫(kù)存儲(chǔ)類(lèi) userDatabase.store(userEntity); //還可以把userEntity存入內(nèi)存中,這得根據(jù)業(yè)務(wù)需求進(jìn)行處理 loginListener.onSuccessLogin(userEntity); } }); } }
登錄的model層我們沒(méi)有做的那么復(fù)雜,比如把服務(wù)器返回的用戶信息存儲(chǔ)在內(nèi)存中,把服務(wù)器返回的token存儲(chǔ)在磁盤(pán)中,實(shí)現(xiàn)自動(dòng)登錄功能等,本例子只是一個(gè)特別簡(jiǎn)單的登錄功能,實(shí)際應(yīng)用中登錄需要考慮很多的東西,登錄的modle層到此重構(gòu)完畢。
presenter
上文中提到過(guò)presenter,presenter起連接view與model的作用,presenter封裝業(yè)務(wù)作用,presenter有負(fù)責(zé)刷新view的作用。
我們梳理下presenter都應(yīng)該包含哪些功能:
- 驗(yàn)證賬號(hào),密碼的合法性.
- 把驗(yàn)證成功的賬號(hào),密碼交給model層
- 把登錄的狀態(tài)傳遞給view層,并根據(jù)不同的登錄狀態(tài)通知view顯示不同的界面
那讓我們開(kāi)始寫(xiě)代碼,presenter層的類(lèi)組織結(jié)構(gòu)我是參照google mvp的presenter類(lèi)組織結(jié)構(gòu)來(lái)進(jìn)行的,因?yàn)槲艺J(rèn)為google mvp presenter類(lèi)結(jié)構(gòu)更清晰,看下偽代碼:
//登錄的條約接口,分別定義了登錄view的一些方法,和登錄presenter的一些方法 public interface LoginContract{ //需要view層來(lái)實(shí)現(xiàn)的登錄view接口,IView是所有view的基類(lèi) interface ILoginView extends IView{ void onShowSuccessLoginView(UserInfo userInfo); void onShowFailedLoginView(int failed); void showLoginingView(); void dissLoginingView(); } //定義了登錄presenter的一些方法,IPresenter是所有Presenter的基類(lèi) interface ILoginPresenter extends IPresenter<ILoginView>{ void login(String name,String password); } } public interface IView{ void initView(); } //IPresenter提供了一些基礎(chǔ)方法,其實(shí)這些方法是對(duì)應(yīng)Activity或Fragment的生命周期方法 public interface IPresenter<V extends IVew>{ void onStop(); void onResume(); void onDestroy(); void onPause(); void onStart(); void init(V view); } //登錄的presenter public class LoginPresenter implements ILoginPresenter{ private ILoginView mLoginView; private LoginManager mLoginManager = LoginManager.getInstance(); public void init(ILoginView loginView){ mLoginView = loginView; mLoginView.initView(); } public void login(String name,String password){ //驗(yàn)證name,password的合法性, if(validate(name) && validate(password)){ //假設(shè)NormalThread.exe方法可以讓操作在普通線程里執(zhí)行 mLoginView.showLoginingView(); NormalThread.exe(new Runnable(){ public void run(){ mLoginManager.login(name,password, new LoginListener(){ public void onSuccessLogin(UserEntity userEntity){ //UserMapper類(lèi),負(fù)責(zé)把底層的UserEntity轉(zhuǎn)化為view層使用的UserInfo UserInfo userInfo = UserMapper.map(userEntity); //下面的代碼在ui線程中執(zhí)行,這就不寫(xiě)具體的實(shí)現(xiàn)了 mLoginView.onShowSuccessLoginView(userInfo); mLoginView.dissLoginingView(); } public void onFailedLogin(Failed failed){ //下面的代碼在ui線程中執(zhí)行,這就不寫(xiě)具體的實(shí)現(xiàn)了 mLoginView.onShowFailedLoginView(failed.failedState); mLoginView.dissLoginingView(); } }); } } }else{ //假設(shè)1代表賬號(hào),密碼不合法 mLoginView.onShowFailedLoginView(1); } } }
以上登錄的Presenter層的偽代碼都是關(guān)鍵代碼,讓我們看下以上代碼都做了什么?
- LoginContract 把ILoginView和ILoginPresenter組合在一塊,ILoginView定義了提供給ILoginPresenter的方法,ILoginPresenter定義了提供給ILoginView的方法。只需要看LoginContract就可以知道登錄的view層和presenter層之間的約定。我很喜歡XXContract類(lèi),也推薦大家使用IView是一個(gè)基礎(chǔ)接口,提供一些公用方法
- IPresenter是一個(gè)基礎(chǔ)接口,它把Activity或Fragment的生命周期方法集合了起來(lái)。有了這些生命周期方法,presenter就讓Activity一心一意做它的view相關(guān)的工作。
- 到此登錄Present的重構(gòu)結(jié)束,我們重構(gòu)view層。
view
view層就很簡(jiǎn)單了,只是需要把基礎(chǔ)設(shè)施建立好,直接看偽代碼:
public abstract class BaseActivity extends FragmentActivity{ private Set<IPresenter> mAllPresenters = new HashSet<IPresenter>(1); /** * 獲取layout的id,具體由子類(lèi)實(shí)現(xiàn) * @return */ protected abstract int getLayoutResId(); /** *需要子類(lèi)來(lái)實(shí)現(xiàn),獲取子類(lèi)的IPresenter,一個(gè)activity有可能有多個(gè)IPresenter */ protected abstract IPresenter[] getPresenters(); //初始化presenters, protected abstract void onInitPresenters(); /** * 從intent中解析數(shù)據(jù),具體子類(lèi)來(lái)實(shí)現(xiàn) * @param argIntent */ protected void parseArgumentsFromIntent(Intent argIntent){ } private void addPresenters(){ IPresenter[] presenters = getPresenters(); if(presenters != null){ for(int i = 0; i < presenters.length; i++){ mAllPresenters.add(presneters[i]); } } } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(getLayoutResId()); if(getIntent() != null){ parseArgumentsFromIntent(getIntent()); } addPresenters(); onInitPresents(); } @Override protected void onResume() { super.onResume(); //依次調(diào)用IPresenter的onResume方法 for (IPresenter presenter:mAllPresenters ) { if(presenter != null){ presenter.onResume(); } } } ...其他生命周期方法也是類(lèi)似,調(diào)用IPresenter中相應(yīng)的生命周期方法... }
基礎(chǔ)設(shè)施已經(jīng)ok了,這時(shí)候我們就該重構(gòu)"萬(wàn)能“LoginActivity了。
public class LoginActivity extends BaseActivity implements LoginConstract.ILoginView{ private LoginPresenter mLoginPresenter = new LoginPresenter(); protected int getLayoutResId(){ return R.layout.activity_login; } protected IPresenter[] getPresenters(){ return new IPresneter[]{ mLoginPresenter}; } //初始化presenters, protected void onInitPresenters(){ mLoginPresenter.init(this); } public void initView(){ ...初始化view的代碼... // mLoginButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mLoginPresenter.login(name,password); } }); } public void onShowSuccessLoginView(UserInfo userInfo){ ....顯示登錄成功界面.... } public void onShowFailedLoginView(int failed){ ...顯示登錄失敗界面... } public void showLoginingView(){ ...顯示登錄進(jìn)度條對(duì)話框... } public void dissLoginingView(){ ...消失登錄進(jìn)度條對(duì)話框... } }
我們著重說(shuō)下基礎(chǔ)設(shè)施BaseActivity:
因?yàn)橐粋€(gè)Activity是有可能包含多個(gè)Presenter的,所以需要在BaseActivity中是有必要把這些Presenter收集起來(lái)。
需要在BaseActivity的生命周期方法里面調(diào)用每個(gè)Presenter的相應(yīng)周期方法。
重構(gòu)以后的LoginActivity是不是很清爽,只保留與view相關(guān)的邏輯。
小結(jié)重構(gòu)后的類(lèi)結(jié)構(gòu)
重構(gòu)“萬(wàn)能”LoginActivity到此結(jié)束,我們小結(jié)下重構(gòu)后的類(lèi)結(jié)構(gòu):
- model層包含:LoginManager(登錄管理類(lèi)),UserEntity(用戶實(shí)體類(lèi),主要使用在model層),UserDatabase(用戶數(shù)據(jù)表)。
- presenter層包含:LoginConstract(登錄約定類(lèi),組合ILoginView和ILoginPresenter),LoginPresenter(登錄presenter類(lèi)),UserMapper(負(fù)責(zé)把model層的UserEntity轉(zhuǎn)化為view層使用的UserInfo)
- view層包含:BaseActivity(基礎(chǔ)類(lèi)),LoginActivity(登錄Activity),UserInfo(用戶信息類(lèi),主要使用在view層)
重構(gòu)感悟
疑惑
估計(jì)會(huì)有細(xì)心的朋友發(fā)現(xiàn)model層是LoginManager而不是像android clean architecture 中model層使用了respository,我個(gè)人覺(jué)得model層也沒(méi)必要這么嚴(yán)格的按respository的架構(gòu)方式來(lái)組織類(lèi)結(jié)構(gòu),因?yàn)楸纠械卿浌δ軐?shí)在是太簡(jiǎn)單了,所以就用最簡(jiǎn)單的一個(gè)LoginManager類(lèi)來(lái)供上層調(diào)用。
優(yōu)缺點(diǎn)
使用mvp重構(gòu)“萬(wàn)能”Activity以后,帶來(lái)了以下好處:
- 類(lèi)的組織結(jié)構(gòu)更清晰
- 每層可以進(jìn)行單獨(dú)的測(cè)試
- 類(lèi)的可復(fù)用性更高
- 層與層的耦合性降低
- 可維護(hù)性更高
- 每個(gè)類(lèi)盡量做到單一職責(zé)
但同時(shí)也帶來(lái)了一些缺點(diǎn),比如創(chuàng)建的類(lèi)多了很多,管理這些類(lèi)的成本會(huì)增加。但是萬(wàn)事萬(wàn)物都有兩面性,就看利與弊的大小了。我個(gè)人覺(jué)得mvp的利肯定是大于弊的,所以有必要采用這種架構(gòu)來(lái)設(shè)計(jì)你的app。
提高生產(chǎn)效率
以上偽代碼中我沒(méi)有使用rxjava,dagger2,假如把它們應(yīng)用于mvp中會(huì)讓你事半功倍,rxjava可以讓你在寫(xiě)presenter層和model層時(shí),可以讓presenter與model交互更簡(jiǎn)單,可以是model層變的尤為的簡(jiǎn)單比如從三大數(shù)據(jù)源取數(shù)據(jù)操作,我們自己用代碼實(shí)現(xiàn)是可以的但是畢竟要花很多時(shí)間,但是用rxjava的一些操作符很容易做到。dagger2可以更好的幫助你進(jìn)行依賴(lài)注入, 還有鼎鼎有名的retrofit也是可以提高效率的。
總結(jié)
本文我們了解了mvp以及每一層,以及使用mvp來(lái)重構(gòu)“萬(wàn)能”Activity,其實(shí)每一層需要注意的東西還有很多,比如model層是最難寫(xiě)的一層。
以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,同時(shí)也希望多多支持腳本之家!
- Android:如何編寫(xiě)“萬(wàn)能”的Activity
- Android中fragment與activity之間的交互(兩種實(shí)現(xiàn)方式)
- Android activity堆棧及管理實(shí)例詳解
- Android實(shí)現(xiàn)Activity水平和垂直滾動(dòng)條的方法
- Android開(kāi)發(fā)中Activity創(chuàng)建跳轉(zhuǎn)及傳值的方法
- Android實(shí)現(xiàn)在子線程中更新Activity中UI的方法
- 詳解Android開(kāi)發(fā)中Activity的四種launchMode
- Android開(kāi)發(fā)中關(guān)于獲取當(dāng)前Activity的一些思考
- Android判斷Activity是否在最上層的方法
- Android編程中activity啟動(dòng)時(shí)出現(xiàn)白屏、黑屏問(wèn)題的解決方法
相關(guān)文章
Android自定義有限制區(qū)域圖例角度自識(shí)別涂鴉工具類(lèi)中篇
這篇文章主要為大家介紹了Android自定義有限制區(qū)域圖例角度自識(shí)別涂鴉工具類(lèi)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02Flutter使用AnimatedOpacity實(shí)現(xiàn)圖片漸現(xiàn)動(dòng)畫(huà)
其實(shí)在Flutter中提供了一些封裝好的動(dòng)畫(huà)組件,以便我們快速應(yīng)用。本文將利用其中的AnimatedOpacity組件實(shí)現(xiàn)圖片漸現(xiàn)動(dòng)畫(huà)效果,需要的可以參考一下2022-03-03Android實(shí)現(xiàn)強(qiáng)制下線功能的示例代碼
這篇文章主要介紹了Android實(shí)現(xiàn)強(qiáng)制下線功能的示例代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07Android使用ViewPager實(shí)現(xiàn)屏幕滑動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了Android使用ViewPager實(shí)現(xiàn)屏幕滑動(dòng)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10Android顯示網(wǎng)絡(luò)圖片實(shí)例
這篇文章主要介紹了Android顯示網(wǎng)絡(luò)圖片的方法,以實(shí)例形式展示了Android程序顯示網(wǎng)絡(luò)圖片的方法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2014-10-10MVVMLight項(xiàng)目之綁定在表單驗(yàn)證上的應(yīng)用示例分析
這篇文章主要為大家介紹了MVVMLight項(xiàng)目中綁定在表單驗(yàn)證上的應(yīng)用示例及源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步除夕快樂(lè),新年快樂(lè)2022-01-01Android實(shí)現(xiàn)Window彈窗效果
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)Window彈窗效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10Android 取消藍(lán)牙配對(duì)框?qū)崿F(xiàn)自動(dòng)配對(duì)功能
這篇文章主要介紹了Android 取消藍(lán)牙配對(duì)框?qū)崿F(xiàn)自動(dòng)配對(duì)功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-02-02