android MVP——mvp架构的应用和优化
MVP架構(gòu)在android還是很好用的。我也在試著將mvp用在項(xiàng)目中。
下面我就來說說mvp模式的應(yīng)用和優(yōu)化。
mvp模式的概念
MVP 是從經(jīng)典的模式MVC演變而來,它們的基本思想有相通的地方:Controller/Presenter負(fù)責(zé)邏輯的處理,Model提供數(shù)據(jù),View負(fù)責(zé)顯示。
比較
mvc:
1,在MVC里,View是可以直接訪問Model的,View里會包含Model信息,不可避免的還要包括一些業(yè)務(wù)邏輯。
2,Model不依賴于View,但是View是依賴于Model。
3,有一些業(yè)務(wù)邏輯在View里實(shí)現(xiàn)了,導(dǎo)致要更改View也是比較困難的,至少那些業(yè)務(wù)邏輯是無法重用的。
mvp:
1,在MVP里,Presenter完全把Model和View進(jìn)行了分離,主要的程序邏輯在Presenter里實(shí)現(xiàn)。
2,Presenter與具體的View是沒有直接關(guān)聯(lián)的,而是通過定義好的接口進(jìn)行交互。從而使得在變更View時候可以保持Presenter的不變,即重用
3,應(yīng)用程序的邏輯主要在Presenter來實(shí)現(xiàn),其中的View是很薄的一層。這樣一來就編寫測試用的View,模擬用戶的各種操作,從而實(shí)現(xiàn)對Presenter的測試
mvp的系統(tǒng)設(shè)計(jì)
我們先來個mvp系統(tǒng)的設(shè)計(jì)。(我們在這里模仿一個登陸的請求)
概要設(shè)計(jì)圖
1,mobel層
mobel接口:規(guī)定操作數(shù)據(jù)的接口。
接口:IUserLoginMobel
/*** 用戶操作接口 Model 業(yè)務(wù)層接口*/ public interface IUserLoginMobel{/*** 用戶登錄** @param name* @param pwd* @param loginListener*/void login(String name, String pwd, OnLoginListener loginListener); }登錄狀態(tài)回調(diào)接口:
/*** 登陸狀態(tài)接口*/ public interface OnLoginListener {void loginSuccess(UserInfoBean user);void loginFailed(String message); }類:用戶登陸操作類 UserLoginModel
/*** 用戶登陸操作類,Model 業(yè)務(wù)層(接收數(shù)據(jù),處理出局)*/ public class UserLoginModel implements IUserLoginMobel {@Overridepublic void login(final String name, final String pwd, final OnLoginListener loginListener) {new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(2000);if ("admin".equals(name) && "admin".equals(pwd)) {UserInfoBean userInfoBean = new UserInfoBean();userInfoBean.setUserId(System.currentTimeMillis());userInfoBean.setUserName(name);userInfoBean.setUserPwd(pwd);loginListener.loginSuccess(userInfoBean);} else {loginListener.loginFailed("密碼錯誤");}} catch (InterruptedException e) {e.printStackTrace();}}}).start();} }2,Presenter層
定義 UserLoginPresenter類
/*** 用戶登錄的任命者。Presenter (用于接收模型發(fā)出的結(jié)果,給view層發(fā)送命令)*/ public class UserLoginPresenter {protected IUserLoginView mvcView;//view的接口private IUserLoginMobel userLoginMobel;//mobel的接口private Handler mHandler = new Handler();public UserLoginPresenter(IUserLoginView userLoginView) {this.mvcView = userLoginView;this.userLoginMobel = new UserLoginModel();//實(shí)例化用戶登錄業(yè)務(wù)層}public void login() {mvcView.showLoading();userLoginMobel.login(mvcView.getUserName(), mvcView.getPassword(), new OnLoginListener() {@Overridepublic void loginSuccess(final UserInfoBean user) {//需要在UI線程執(zhí)行mHandler.post(new Runnable() {@Overridepublic void run() {mvcView.toMainActivity(user);mvcView.hideLoading();}});}@Overridepublic void loginFailed(final String message) {//需要在UI線程執(zhí)行mHandler.post(new Runnable() {@Overridepublic void run() {mvcView.showFailedError(message);mvcView.hideLoading();}});}});}public void clear() {mvcView.clearPassword();mvcView.clearUserName();} }3,view 層
首相定義一個view接口 IUserLoginView
接口規(guī)定view層去實(shí)現(xiàn)的方法
我們定義一個MVCActivity來繼承 IUserLoginView
/*** 這時候的activity相當(dāng)于view (只負(fù)責(zé)顯示數(shù)據(jù))* Presenter與View交互是通過接口*/ public class MVCActivity extends AppCompatActivity implements IUserLoginView {private EditText user_name_edit, user_pwd_edit;private Button user_login_btn, user_clear_btn;private ProgressBar user_login_bar;private UserLoginPresenter presenter;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);presenter=new UserLoginPresenter(this);initView();}private void initView() {user_name_edit = (EditText) findViewById(R.id.user_name_edit);user_pwd_edit = (EditText) findViewById(R.id.user_pwd_edit);user_login_btn = (Button) findViewById(R.id.user_login_btn);user_clear_btn = (Button) findViewById(R.id.user_clear_btn);user_login_bar = (ProgressBar) findViewById(R.id.user_login_bar);user_login_btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {presenter.login();}});user_clear_btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {presenter.clear();}});}@Overridepublic String getUserName() {return user_name_edit.getText().toString();}@Overridepublic String getPassword() {return user_pwd_edit.getText().toString();}@Overridepublic void clearUserName() {user_name_edit.setText("");}@Overridepublic void clearPassword() {user_pwd_edit.setText("");}@Overridepublic void showLoading() {user_login_bar.setVisibility(View.VISIBLE);}@Overridepublic void hideLoading() {user_login_bar.setVisibility(View.INVISIBLE);}@Overridepublic void toMainActivity(UserInfoBean user) {Toast.makeText(this, user.getUserName(), Toast.LENGTH_SHORT).show();}@Overridepublic void showFailedError(String message) {Toast.makeText(this, message, Toast.LENGTH_SHORT).show();}}還有l(wèi)ayout.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><LinearLayout android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><TextView android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="用戶名" /><EditText android:id="@+id/user_name_edit"android:layout_width="match_parent"android:layout_height="wrap_content" /></LinearLayout><LinearLayout android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><TextView android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="密碼" /><EditText android:id="@+id/user_pwd_edit"android:layout_width="match_parent"android:layout_height="wrap_content" /></LinearLayout><LinearLayout android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center_horizontal"android:orientation="horizontal"><Button android:id="@+id/user_login_btn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="登陸" /><Button android:id="@+id/user_clear_btn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="清除" /></LinearLayout><ProgressBar android:id="@+id/user_login_bar"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:visibility="invisible" /> </LinearLayout>我們先來看看效果圖。
可以看出,和我們原來用mvc設(shè)計(jì)的登陸達(dá)到了一樣的目的。但是可以看出我們的activity再也沒有像原來一樣多了很多邏輯處理。
而我們的邏輯處理都放在了Presenter層。而activity成了一個真正的view。
這就是mvp架構(gòu)最基本的應(yīng)用??梢钥闯鍪褂胢vp去設(shè)計(jì)app可以很好的將activity當(dāng)作一個view層分離出來。
那么從上面的結(jié)構(gòu)圖可以看出我們還是有很多可以優(yōu)化的地方的。
優(yōu)化
mobel層 接口都繼承IBaseMobel接口
我們可以在IBaseMobel這個元接口中定義一些整個mobel都會做的工作,例如初始化數(shù)據(jù)
同理:view層 接口都繼承 IBaseView 接口
我們可以在IBaseView 這個元接口中定義一些整個mobel都會做的工作,例如初始化數(shù)據(jù)。
/*** view層基類接口*/ public interface IBaseView {void initView();//view初始化view的一個基本接口 }presenter層繼承一個基類來收集重復(fù)方法或者屬性
定義一個 BasePresenter類
我們來看看如何讓原來的UserLoginPresenter使用
修改后的UserLoginPresenter類
這樣我們就把Presenter的初始化工作和關(guān)于activity在onDestory的時候手動置空Presenter中view對象(這時候的view對象其實(shí)就是activity)的方法給提取到基類中。
因?yàn)檫@兩個方法很多地方會用到,所以我們不必每次都去寫它們。
我們再將view層的activity封裝。view層例如:fragment也是可以封裝的。
我們建立一個抽象類BaseActivity。
/*** mvc模式的view層基類<繼承 presenter 具體>*/ public abstract class BaseActivity<T extends BasePresenter> extends AppCompatActivity {//必須實(shí)例化presenter對象public abstract T initPresenter();public T presenter;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(setMvcView());presenter = initPresenter();initView();}protected abstract int setMvcView();protected abstract void initView();@Overrideprotected void onDestroy() {presenter.onDestroy();super.onDestroy();} }接下來修改MVCActivity類
/*** 這時候的activity相當(dāng)于view (只負(fù)責(zé)顯示數(shù)據(jù))* Presenter與View交互是通過接口*/ public class MVCActivity extends BaseActivity<UserLoginPresenter> implements IUserLoginView {private EditText user_name_edit, user_pwd_edit;private Button user_login_btn, user_clear_btn;private ProgressBar user_login_bar;@Overridepublic UserLoginPresenter initPresenter() {return new UserLoginPresenter(this);}@Overrideprotected int setMvcView() {return R.layout.activity_main;}@Overrideprotected void initView() {user_name_edit = (EditText) findViewById(R.id.user_name_edit);user_pwd_edit = (EditText) findViewById(R.id.user_pwd_edit);user_login_btn = (Button) findViewById(R.id.user_login_btn);user_clear_btn = (Button) findViewById(R.id.user_clear_btn);user_login_bar = (ProgressBar) findViewById(R.id.user_login_bar);user_login_btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {presenter.login();}});user_clear_btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {presenter.clear();}});}@Overridepublic String getUserName() {return user_name_edit.getText().toString();}@Overridepublic String getPassword() {return user_pwd_edit.getText().toString();}@Overridepublic void clearUserName() {user_name_edit.setText("");}@Overridepublic void clearPassword() {user_pwd_edit.setText("");}@Overridepublic void showLoading() {user_login_bar.setVisibility(View.VISIBLE);}@Overridepublic void hideLoading() {user_login_bar.setVisibility(View.INVISIBLE);}@Overridepublic void toMainActivity(UserInfoBean user) {Toast.makeText(this, user.getUserName(), Toast.LENGTH_SHORT).show();}@Overridepublic void showFailedError(String message) {Toast.makeText(this, message, Toast.LENGTH_SHORT).show();}}我們把a(bǔ)ctivity的onCreate和onDestory交給基類來處理,我們也可以在基類中處理onResume等方法。所有的生命周期由基類來管理。
另外,我們將Presenter對象也交給基類來管理,讓基類來處理Presenter對象中的公有方法。
mvp架構(gòu)的應(yīng)用和優(yōu)化已經(jīng)寫完了,我們可以根據(jù)自己項(xiàng)目的實(shí)際情況作進(jìn)一步的優(yōu)化。
mvp其實(shí)就是一種設(shè)計(jì)思想。它不光用于對model、presenter和view的處理。這只是一種思想,可以用到很多地方。
更多的mvp模式可以去參照:
Google在Github開源的一個項(xiàng)目:Android Architecture Blueprints
簡書:Android官方MVP架構(gòu)項(xiàng)目解析
獻(xiàn)上這篇博客的demo:
demo下載
總結(jié)
以上是生活随笔為你收集整理的android MVP——mvp架构的应用和优化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 几行代码实现用Python输出表情包
- 下一篇: 视频或音频数据存储的2种格式packed