Markdown版本筆記 | 個人GitHub首頁 | 個人博客 | 個人微信 | 個人郵箱 |
---|---|---|---|---|
MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
其實Android中只有MVandroid
Android下MVC中的控制層是由Activity來承擔的,Activity原本主要是做爲初始化頁面,展現數據的操做,可是由於XML視圖功能太弱,因此Activity既要負責視圖的顯示又要加入控制邏輯,承擔的功能過多。git
好比對於登陸頁面,MVC的基本流程爲:github
用戶與View交互,View接收並反饋用戶的動做,View把用戶的請求傳給相應的控制器,由控制器決定調用哪一個模型,而後由模型調用相應的業務邏輯對用戶請求進行加工處理,若是須要返回數據,模型會把相應的數據返回給控制器,由控制器調用相應的視圖,最終由視圖渲染返回的數據。算法
在Android開發中,Activity並非一個標準的MVC模式中的Controller,它的首要職責是加載應用的佈局和初始化用戶界面,接受並處理來自用戶的操做請求,進而做出響應。隨着界面及其邏輯的複雜度不斷提高,Activity類的職責不斷增長,以至變得龐大臃腫。數據庫
好比在Android中,對於登陸頁面,典型的交互過程是這樣的:後端
用戶點擊登陸按鈕 → Activity中註冊的監聽器檢測到點擊事件 → Activity經過轉動View中的ProgressBar來響應用戶點擊事件 → Activity經過View中的EditText獲取用戶輸入的帳戶密碼 → Activity將數據交由業務邏輯層(Model層)處理 → Model層處理完成後經過回調將數據返回給Activity → Activity更新UI反饋給用戶設計模式
由上面的案例能夠看出,其實這個View對應於佈局文件能作的事情特別少,實際上關於該佈局文件中的數據綁定、事件處理的代碼都在Activity中,形成了Activity既像View又像Controller,這可能也就是爲什麼:緩存
Most of the modern Android applications just use View-Model
architecture,everything is connected with Activity.微信
BaseModel顧名思義就是全部業務邏輯model的父類,這裏的onDestroy()
方法用於跟activity或者fragment生命週期同步,在destroy作一些銷燬操做網絡
public interface BaseModel { void onDestroy(); }
Callback是根據View或者Controller調用Model時回調的參數個數選擇使用
public interface Callback1<T> { void onCallBack(T t); }
public interface Callback2<T, P> { void onCallBack(T t, P p); }
SampleModel是咱們業務邏輯的具體實現
public class SampleModel implements BaseModel { public void getUserInfo(String uid, Callback1<UserInfo> callback) { UserInfo userInfo = new UserInfo(); //...從網絡或數據庫等獲取數據 callback.onCallBack(userInfo); } public void getUserInfo2(String uid, Callback2<UserInfo, String> callback) { UserInfo userInfo = new UserInfo(); //...從網絡或數據庫等獲取數據 callback.onCallBack(userInfo, "其餘數據"); } @Override public void onDestroy() { } public class UserInfo { private int age; private String name; //... } }
public class SampleActivity extends AppCompatActivity { private SampleModel sampleModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sample); sampleModel = new SampleModel(); String uid = "123456"; findViewById(R.id.button).setOnClickListener(view -> getUserInfo(uid)); } @Override protected void onDestroy() { super.onDestroy(); sampleModel.onDestroy(); } //獲取用戶信息 private void getUserInfo(String uid) { sampleModel.getUserInfo(uid, userInfo -> { //...設置用戶信息到view }); } }
事件的流向
總結
MVP的核心就是:讓M和V徹底解耦,經過Presenter統一調度管理
MVP的基本流程:Presenter從View中獲取須要的參數,交給Model去處理,Model執行過程當中的反饋以及結果告訴Presenter,Presenter再讓View作對應的顯示。
MVP跟MVC很相像,根據MVC的發展來看,咱們把MVP當成MVC來看也不爲過,由於MVP也是三層,惟一的差異是Model和View之間不進行通信
,都是經過Presenter完成。
前面介紹MVC的時候提到了算是致命缺點吧,在android中因爲activity(god object)的存在,Controller和View很難作到徹底解耦
。但在MVP中就能夠很好的解決這個問題
讓Model和View徹底解耦,由Presenter負責完成View與Model的交互
將Actvity視爲View層
,減小了Activity的職責,將複雜的邏輯代碼提取到了Presenter中
Presenter與View之間的交互徹底是經過接口的
分析這個模塊須要哪些業務邏輯,或者說有哪些複雜的功能,以此定義Presenter接口。
對於登陸模塊,主要的就是登陸功能。爲了增長接口的複雜度,這裏我又添加了一個退出前清理功能。
public interface Login_Presenter_I { void login(String username, String password);//登陸過程可能涉及到很工做,因此把它抽出來。此過程須要與View交互 void onFinishActivity();//退出前可能要作不少清理工做,因此也把它抽出來。此過程不須要與View交互 }
分析上述Presenter層中的功能在被Model層處理過程當中,Model須要通知Presenter哪些內容,以此定義Model接口。
Model層在執行過程當中,是經過接口通知Presenter執行過程當中的狀態以及執行完畢後的結果的,爲了邏輯更清晰,建議此此接口定義爲Model層接口的內部接口。
public interface Login_Model_I { //參數 listener:Model層經過此接口通知Presenter執行過程當中的狀態以及執行完畢後的結果 void login(String username, String password, OnLoginListener listener);//執行過程當中須要通知Presenter void clearBeforeFinishActivity(boolean clearSp, boolean deleteCache);//執行過程當中不須要通知Presenter //將Model層須要通知Presenter的內容,按照類別,定義在不一樣的接口中 interface OnLoginListener { void onUsernameError(); void onPasswordError(); void onSuccess(); } }
分析全部可能會操做UI的最基礎邏輯,以此定義View接口。
Presenter層是經過接口來操做View層的。
public interface Login_Activity_I { void showProgress(); void hideProgress(); void setUsernameError(); void setPasswordError(); void showToast(String msg, int duration); }
public class Login_Presenter_Impl implements Login_Presenter_I, Login_Model_I.OnLoginListener { private Login_Activity_I loginActivityI; //拿到的是接口,整個Presenter中沒有導入任何View和Activity private Login_Model_I loginIModel; //拿到的是Model層的實現類 public Login_Presenter_Impl(Login_Activity_I loginActivityI) { this.loginActivityI = loginActivityI;//View層的實現類,由Activity傳過來 this.loginIModel = new Login_Model_Impl();//Model層的實現類,由Activity傳過來或本身建立都可 } @Override public void login(String username, String password) { if (loginActivityI != null) loginActivityI.showProgress();//通知View更新UI loginIModel.login(username, password, this);//交給Model層處理 } @Override public void onFinishActivity() { if (loginActivityI != null) loginActivityI.showProgress();//通知View更新UI boolean clearSp = new Random().nextBoolean(); boolean deleteCache = new Random().nextBoolean(); loginIModel.clearBeforeFinishActivity(clearSp, deleteCache);//交給Model層處理 if (loginActivityI != null) loginActivityI.hideProgress();//通知View更新UI } @Override public void onUsernameError() { if (loginActivityI != null) { loginActivityI.setUsernameError(); loginActivityI.hideProgress(); } } @Override public void onPasswordError() { if (loginActivityI != null) { loginActivityI.setPasswordError(); loginActivityI.hideProgress(); } } @Override public void onSuccess() { if (loginActivityI != null) loginActivityI.showToast("登陸成功", Toast.LENGTH_SHORT); } }
Model層的實現類,只處理邏輯,徹底不操做View,對邏輯處理的狀態反饋給Presenter
public class Login_Model_Impl implements Login_Model_I { @Override public void login(String username, String password, OnLoginListener listener) { if (TextUtils.isEmpty(username)) listener.onUsernameError(); else if (TextUtils.isEmpty(password)) listener.onPasswordError(); else listener.onSuccess(); } @Override public void clearBeforeFinishActivity(boolean clearSp, boolean deleteCache) { if (clearSp) Log.i("bqt", "【清理SP】"); if (deleteCache) Log.i("bqt", "【清理緩存】"); } }
public class Login_Activity extends Activity implements Login_Activity_I, View.OnClickListener { private ProgressBar progressBar; private EditText username; private EditText password; private Login_Presenter_I presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); progressBar = (ProgressBar) findViewById(R.id.progress); username = (EditText) findViewById(R.id.username); password = (EditText) findViewById(R.id.password); findViewById(R.id.button).setOnClickListener(this); presenter = new Login_Presenter_Impl(this);//new一個Presenter的實現類,把本身傳過去。實際上接收的只是LoginView接口的實例 } @Override public void finish() { presenter.onFinishActivity(); super.finish(); } @Override public void showProgress() { progressBar.setVisibility(View.VISIBLE); } @Override public void hideProgress() { progressBar.setVisibility(View.GONE); } @Override public void setUsernameError() { username.setError(getString(R.string.username_error)); } @Override public void setPasswordError() { password.setError(getString(R.string.password_error)); } @Override public void showToast(String msg, int duration) { Toast.makeText(this, msg, duration).show(); } @Override public void onClick(View v) { //點擊登陸時,View把數據傳給presenter,presenter處理完數據後通知View處理事件 presenter.login(username.getText().toString(), password.getText().toString()); } }
public interface LoginView { void showProgress(); void hideProgress(); void setUsernameError(); void setPasswordError(); void navigateToHome(); }
public class LoginActivity extends AppCompatActivity implements LoginView { private ProgressBar progressBar; private EditText username; private EditText password; private LoginPresenter presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); progressBar = findViewById(R.id.progress); username = findViewById(R.id.username); password = findViewById(R.id.password); findViewById(R.id.button).setOnClickListener(v -> login()); presenter = new LoginPresenter(this, new LoginModel()); } @Override protected void onDestroy() { presenter.onDestroy(); super.onDestroy(); } @Override public void showProgress() { progressBar.setVisibility(View.VISIBLE); } @Override public void hideProgress() { progressBar.setVisibility(View.GONE); } @Override public void setUsernameError() { username.setError("用戶名錯誤"); } @Override public void setPasswordError() { password.setError("密碼錯誤"); } @Override public void navigateToHome() { startActivity(new Intent(this, MainActivity.class)); finish(); } private void login() { presenter.login(username.getText().toString(), password.getText().toString()); } }
public class LoginPresenter implements LoginModel.OnLoginFinishedListener { private LoginView LoginView; private LoginModel loginModel; LoginPresenter(LoginView LoginView, LoginModel loginModel) { this.LoginView = LoginView; this.loginModel = loginModel; } public void login(String username, String password) { if (LoginView != null) { LoginView.showProgress(); } loginModel.login(username, password, this); } public void onDestroy() { LoginView = null; } @Override public void onUsernameError() { if (LoginView != null) { LoginView.setUsernameError(); LoginView.hideProgress(); } } @Override public void onPasswordError() { if (LoginView != null) { LoginView.setPasswordError(); LoginView.hideProgress(); } } @Override public void onSuccess() { if (LoginView != null) { LoginView.navigateToHome(); } } }
public class LoginModel { interface OnLoginFinishedListener { void onUsernameError(); void onPasswordError(); void onSuccess(); } public void login(final String username, final String password, final OnLoginFinishedListener listener) { new Handler().postDelayed(() -> { if (TextUtils.isEmpty(username)) { listener.onUsernameError(); return; } if (TextUtils.isEmpty(password)) { listener.onPasswordError(); return; } listener.onSuccess(); }, 2000); } }
MVP中,隨着業務邏輯的增長,UI的改變多的狀況下,會有很是多的跟UI相關的case,這樣就會形成View的接口會很龐大。而MVVM就解決了這個問題,經過雙向綁定
的機制,實現數據和UI內容,只要想改其中一方,另外一方都可以及時更新的一種設計理念,這樣就省去了不少在View層中寫不少case的狀況,只須要改變數據就行。
先看下MVVM設計圖:
這看起來跟MVP好像沒啥差異,其實區別仍是挺大的,在MVP中,View和presenter要相互持有,方便調用對方,而在MVP中,View和ViewModel經過Binding進行關聯,他們以前的關聯處理經過DataBinding
完成。
MVVM與DataBinding的關係用一句話表述就是,MVVM是一種思想,DataBinding是谷歌推出的方便實現MVVM的工具
。
在google推出DataBinding以前,由於xml layout功能較弱,想實現MVVM很是困難。而DataBinding的出現可讓咱們很方便的實現MVVM。
看起來MVVM很好的解決了MVC和MVP的不足,可是因爲數據和視圖的雙向綁定,致使出現問題時不太好定位來源
,有可能數據問題致使,也有可能業務邏輯中對視圖屬性的修改致使。若是項目中打算用MVVM的話能夠考慮使用官方的架構組件ViewModel、LiveData、DataBinding
去實現MVVM。
關於ViewModel、LiveData、DataBindin這些類或框架的使用,由於涉及到的內容比較多,這裏不詳細介紹。
前面在介紹MVC、MVP、MVVM時並無去詳細列出他們的優缺點,主要緣由是:關於架構,設計,模塊化等等,它們的優缺點沒有絕對的,主要看實現者如何去作
好比在mvp中咱們要實現根據業務邏輯和頁面邏輯作不少Present和View的具體實現,若是這些case太多,會致使代碼的可讀性變差。可是經過引入contract契約類,會讓業務邏輯變得清晰許多。所以不論是用哪一種設計模式,只要運用得當,均可以達到想要的結果。
若是非要說怎麼選的話,通常的建議以下:
項目簡單
,沒什麼複雜性,將來改動也不大的話,那就不要用設計模式或者架構方法,只須要將每一個模塊封裝好,方便調用便可,不要爲了使用設計模式或架構方法而使用。展現型
的app,絕大多數業務邏輯都在後端,app主要功能就是展現數據,交互等,建議使用mvvm
。業務邏輯
app,使用mvp或者mvvm均可。MVC
而後在此基礎上慢慢挖掘改進。最後你可能發現,改進的最終結果可能就變成了mvp,mvvm。2019-5-9