簡(jiǎn)介
關(guān)于Android程序的構(gòu)架, 主流的不外乎以下幾種:MVC议泵、MVP和MVVM驮配。
MVC:相對(duì)于較為落后,耦合度太高同诫、職責(zé)不明確粤策,不易于維護(hù)。
MVVM:使用DataBinding误窖,普及性不如MVP叮盘。
此外,Google官方提供了Sample代碼來(lái)展示MVP模式的用法霹俺,因此主流還是選擇MVP架構(gòu)柔吼。
因本文主要講的是MVP模式的優(yōu)雅封裝,MVVM模式在此就不作贅述丙唧,后續(xù)文章會(huì)講到愈魏。
MVC:
提到MVP就不得不提到MVC,關(guān)于MVC架構(gòu)想际,可以看下面這張圖
MVC工作原理:
MVC即Model View Controller培漏,簡(jiǎn)單來(lái)說(shuō)就是通過(guò)controller的控制去操作model層的數(shù)據(jù),并且返回給view層展示胡本,具體見(jiàn)上圖牌柄。當(dāng)用戶出發(fā)事件的時(shí)候,view層會(huì)發(fā)送指令到controller層侧甫,接著controller去通知model層更新數(shù)據(jù)珊佣,model層更新完數(shù)據(jù)以后直接顯示在view層上,這就是MVC的工作原理披粟。
這種原理就會(huì)造成一個(gè)一個(gè)致命的缺陷:當(dāng)我們把很多業(yè)務(wù)邏輯寫(xiě)在activity中時(shí)咒锻,activity既充當(dāng)了View層,又充當(dāng)了Controller層僻爽。因此虫碉,耦合性極高,各種業(yè)務(wù)邏輯代碼和View代碼混合在一起你中有我我中有你胸梆,如果要修改一個(gè)需求,改動(dòng)的地方可能相當(dāng)多,維護(hù)起來(lái)十分不便坤次。
作為一個(gè)追求優(yōu)雅的程序猿取董,這種架構(gòu)必然要被拋棄。
MVP:
概念
MVP即Model、View、Presenter
View:負(fù)責(zé)視圖部分展示、視圖事件處理甜奄。Activity、Fragment窃款、Dialog课兄、ViewGroup等呈現(xiàn)視圖的組件都可以承擔(dān)該角色。
Model:負(fù)責(zé)數(shù)據(jù)的請(qǐng)求晨继、解析烟阐、過(guò)濾等數(shù)據(jù)層操作。
Presenter:View和Model交互的橋梁紊扬。
優(yōu)勢(shì)
單一職責(zé)
Model蜒茄、View、Presenter只處理某一類(lèi)邏輯
解耦
Model層修改和View層修改互不影響
面向接口編程餐屎,依賴(lài)抽象
Presenter和View互相持有抽象引用檀葛,對(duì)外隱藏內(nèi)部實(shí)現(xiàn)細(xì)節(jié)
可能存在的問(wèn)題
1、Model進(jìn)行異步操作腹缩,獲取結(jié)果通過(guò)Presenter回傳到View時(shí)屿聋,出現(xiàn)View引用的空指針異常
2、Presenter和View互相持有引用庆聘,解除不及時(shí)造成的內(nèi)存泄漏胜臊。
因此勺卢,在進(jìn)行MVP架構(gòu)設(shè)計(jì)時(shí)需要考慮Presenter對(duì)View進(jìn)行回傳時(shí)伙判,View是否為空?
Presenter與View何時(shí)解除引用即Presenter能否和View層進(jìn)行生命周期同步?
好了黑忱,說(shuō)了這么多廢話宴抚,總之一句話,MVP好甫煞。下面我們來(lái)看看具體如何優(yōu)雅的實(shí)現(xiàn)MVP的封裝菇曲。
MVP架構(gòu)優(yōu)雅的封裝
1、首先抚吠,我們定義一個(gè)BaseView
/**
* 視圖基類(lèi)
*/
public interface BaseView {
}
上面說(shuō)過(guò)常潮,
如果Presenter與View不及時(shí)解除引用關(guān)系,那么內(nèi)存泄漏乃至內(nèi)存溢出就是必然楷力。
具體來(lái)說(shuō)喊式,
當(dāng)Presenter對(duì)象持有一個(gè)或多個(gè)大型Activity的引用孵户,如果該對(duì)象(P)不能被系統(tǒng)回收,那么當(dāng)這些Activity不再使用時(shí)岔留,這個(gè)Activity也不會(huì)被系統(tǒng)回收夏哭,這樣一來(lái)便出現(xiàn)了內(nèi)存泄漏的情況。在應(yīng)用中內(nèi)出現(xiàn)一次兩次的內(nèi)存泄漏或許不會(huì)出現(xiàn)什么影響献联,但是在應(yīng)用長(zhǎng)時(shí)間使用以后竖配,若是這些占據(jù)大量?jī)?nèi)存的Activity無(wú)法被GC回收的話,最終會(huì)導(dǎo)致OOM的出現(xiàn)里逆,就會(huì)直接Crash應(yīng)用进胯。
我們當(dāng)然不會(huì)坐視這種情況的發(fā)生,解決的思路就是原押,
我們將Presenter的生命周期和View層的生命周期綁定在一起龄减,給Presenter定義兩個(gè)方法,一個(gè)綁定View層班眯,一個(gè)解綁View層希停,在需要的時(shí)候進(jìn)行綁定,不需要的時(shí)候進(jìn)行解綁就可以了署隘。
于是就有了下面這個(gè)定義宠能。
2、將Presenter的生命周期和View層的生命周期綁定
/**
* 控制器接口:
* 定義P層生命周期與 V層同步
*/
public interface IPresenter<V extends BaseView> {
void onMvpAttachView(V view, Bundle savedInstanceState);
void onMvpStart();
void onMvpResume();
void onMvpPause();
void onMvpStop();
void onMvpSaveInstanceState(Bundle savedInstanceState);
void onMvpDetachView(boolean retainInstance);
void onMvpDestroy();
}
為了代碼的優(yōu)雅性磁餐,我們對(duì)它進(jìn)行一次封裝
/**
* 控制器基類(lèi):
* Presenter生命周期包裝违崇、View的綁定和解除,P層實(shí)現(xiàn)的基類(lèi)
*/
public class BasePresenter<V extends BaseView> implements IPresenter<V> {
private WeakReference<V> viewRef;
protected V getView() {
return viewRef.get();
}
protected boolean isViewAttached() {
return viewRef != null && viewRef.get() != null;
}
private void _attach(V view, Bundle savedInstanceState) {
viewRef = new WeakReference<V>(view);
}
@Override
public void onMvpAttachView(V view, Bundle savedInstanceState) {
_attach(view, savedInstanceState);
}
@Override
public void onMvpStart() {
}
@Override
public void onMvpResume() {
}
@Override
public void onMvpPause() {
}
@Override
public void onMvpStop() {
}
@Override
public void onMvpSaveInstanceState(Bundle savedInstanceState) {
}
private void _detach(boolean retainInstance) {
if (viewRef != null) {
viewRef.clear();
viewRef = null;
}
}
@Override
public void onMvpDetachView(boolean retainInstance) {
_detach(retainInstance);
}
@Override
public void onMvpDestroy() {
}
}
3诊霹、對(duì)于View層羞延,我們一般都會(huì)寫(xiě)一個(gè)BaseActivity
/**
* @description 在此類(lèi)中添加自己的基類(lèi)功能
*/
public class BaseActivity extends FragmentActivity {
protected void openActivity(String action) {
openActivity(action, null);
}
public void showEnsureDialog(String message) {
}
}
4、我們?cè)賹?xiě)一個(gè)綁定生命周期的BaseMvpActivity包裝類(lèi)
/**
* MVP的Activity基類(lèi):
* 純粹的 MVP 包裝脾还,不要增加任何View層基礎(chǔ)功能
* 如果要添加基類(lèi)功能伴箩,請(qǐng)?jiān)趝@link BaseActivity} 中添加
*/
public abstract class BaseMvpActivity<P extends IPresenter> extends BaseActivity implements BaseView {
protected P mPresenter;
protected abstract P createPresenter();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter = createPresenter();
if (mPresenter == null) {
throw new NullPointerException("Presenter is null! Do you return null in createPresenter()?");
}
mPresenter.onMvpAttachView(this, savedInstanceState);
}
@Override
protected void onStart() {
super.onStart();
if (mPresenter != null) {
mPresenter.onMvpStart();
}
}
@Override
protected void onResume() {
super.onResume();
if (mPresenter != null) {
mPresenter.onMvpResume();
}
}
@Override
protected void onPause() {
super.onPause();
if (mPresenter != null) {
mPresenter.onMvpPause();
}
}
@Override
protected void onStop() {
super.onStop();
if (mPresenter != null) {
mPresenter.onMvpStop();
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mPresenter != null) {
mPresenter.onMvpSaveInstanceState(outState);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mPresenter != null) {
mPresenter.onMvpDetachView(false);
mPresenter.onMvpDestroy();
}
}
}
5、我們以登錄為例鄙漏,定義一個(gè)契約類(lèi)
/**
* 契約接口類(lèi):
* P層與 V層接口定義
*/
public class LoginContract {
public interface ILoginView extends BaseView {
/**
* 登錄成功
*/
void LoginSuccess();
/**
* 登錄失敗
*
* @param msg
*/
void LoginFailed(String msg);
}
public interface ILoginPresenter extends IPresenter<ILoginView> {
/**
* 登錄
*/
void login(String username, String password);
}
}
6嗤谚、我們?cè)俣x一個(gè)登錄的Presenter的實(shí)現(xiàn)類(lèi),在這個(gè)類(lèi)中怔蚌,完成互相訪問(wèn)巩步。
/**
* 控制器實(shí)現(xiàn)類(lèi)
*/
public class LoginPresenterImpl extends BasePresenter<LoginContract.ILoginView> implements LoginContract.ILoginPresenter {
@Override
public void login(String username, String password) {
//先進(jìn)行非空判斷
if (isViewAttached()) {
handleLogin(getView(), username, password);
}
}
private void handleLogin(LoginContract.ILoginView view, String username, String password) {
if (username.isEmpty() || password.isEmpty()) {
view.LoginFailed("賬號(hào)和密碼不能為空");
} else if (password.length() < 6 || password.length() > 20) {
view.LoginFailed("密碼須在6-20位之間");
} else {
if (username.equals("mvp")) {
if (password.equals("123456")) {
view.LoginSuccess();
} else {
view.LoginFailed("密碼錯(cuò)誤");
}
} else {
view.LoginFailed("用戶名錯(cuò)誤");
}
}
}
@Override
public void onMvpAttachView(LoginContract.ILoginView view, Bundle savedInstanceState) {
super.onMvpAttachView(view, savedInstanceState);
}
/**
* 重寫(xiě)P層需要的生命周期,進(jìn)行相關(guān)邏輯操作
*/
@Override
public void onMvpResume() {
super.onMvpResume();
}
}
7桦踊、到這里椅野,我們的封裝基本完成,我們現(xiàn)在來(lái)看看我們的LoginActivity是怎么樣的。
public class LoginActivity extends BaseMvpActivity<LoginContract.ILoginPresenter> implements LoginContract.ILoginView {
@BindView(R.id.et_username)
EditText etUsername;
@BindView(R.id.et_password)
EditText etPassword;
@BindView(R.id.btn_login)
Button btnLogin;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mvp);
ButterKnife.bind(this);
}
@Override
protected LoginContract.ILoginPresenter createPresenter() {
return new LoginPresenterImpl();
}
@OnClick({R.id.btn_login})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.btn_login:
String username = etUsername.getText().toString().trim();
String password = etPassword.getText().toString().trim();
mPresenter.login(username, password);
break;
}
}
@Override
public void LoginSuccess() {
Toast.makeText(this, "LoginSuccess", Toast.LENGTH_SHORT).show();
}
@Override
public void LoginFailed(String msg) {
Toast.makeText(this, "LoginFailed", Toast.LENGTH_SHORT).show();
}
}
這樣竟闪,是不是看起來(lái)声离,清爽多了。