使用架構(gòu)的目的是為使程序模塊化,做到模塊內(nèi)的高聚合和模塊間的低耦合战秋,架構(gòu)和模式并不是讓你的代碼減少,可能往往會增大讨韭,但是它幫你在邏輯上更簡潔了脂信,很好的定義了單一性原則,提供了更好的擴展性透硝,方便定位問題以及后續(xù)開發(fā)中需求變跟時不至于滿篇的去改一大堆東西 狰闪。(一下圖片均來自網(wǎng)絡(luò),侵刪)
MVC簡述
首先熟悉下在Android開發(fā)中濒生,傳統(tǒng)的MVC模式(view埋泵、model、controller)。
Activity丽声、Fragment礁蔗、布局xml就相當(dāng)于View層。業(yè)務(wù)模型雁社,建立的數(shù)據(jù)結(jié)構(gòu)浴井,以及網(wǎng)絡(luò)請求、數(shù)據(jù)庫處理霉撵、I/O操作相當(dāng)于model層磺浙。至于controller層就有點模糊了,我們大家都將Activity喊巍、Fragment視作controller層屠缭,也就是把view和controller都揉和在Activity和Fragment中箍鼓,視圖顯示和控制邏輯錯綜復(fù)雜崭参。
這樣經(jīng)過幾次需求變跟、代碼迭代后一個Activity動輒上千行代碼款咖,最主要的修改某個頁面時控制邏輯也得跟著變動何暮,當(dāng)然了可能會出現(xiàn)的bug以及后續(xù)開發(fā)人員閱讀代碼的難度也就增加了很多,這就違背了架構(gòu)的低耦合铐殃。
MVP簡述
這樣更優(yōu)的架構(gòu)模式MVP也就得到更多開發(fā)者的青睞海洼。
MVP中每個單詞具體代表的含義是什么?
M層 和MVC中的model一樣富腊,都是模型層坏逢,負(fù)責(zé)處理存儲、檢索赘被、操作數(shù)據(jù)是整,包括網(wǎng)絡(luò)請求、數(shù)據(jù)庫處理以及IO操作民假。
V層 和MVC的view一樣浮入,都是視圖層,負(fù)責(zé)繪制UI元素以及與用戶的交互羊异,對應(yīng)的是activity事秀、Fragment、adapter野舶、xml
P層 (Presenter)是真?zhèn)€MVP的控制中心易迹,作為View和Model的中間樞紐,處理View和Model間的交互和業(yè)務(wù)邏輯
這樣通過Presenter將View與Model進(jìn)行隔離平道,使得View和Model之間不存在耦合睹欲,同時也將業(yè)務(wù)邏輯從View中抽離 ,視圖層專注View的展示和用戶的交互巢掺。
這樣做的優(yōu)勢也就是顯而易見了
Model層與View層完全分離句伶,修改view層不會影響Model層劲蜻,降低了耦合
復(fù)雜的邏輯處理放在presenter進(jìn)行處理,減少了Activity的臃腫
可以將一個Presenter用于多個視圖考余,而不需要改變Presenter的邏輯先嬉, 提高了代碼復(fù)用 ,代碼靈活性
模塊職責(zé)劃分明顯 楚堤, 具有良好的可擴展性
Presenter層與View層的交互是通過接口來進(jìn)行的疫蔓,便于單元測試。
當(dāng)然MVP也是有缺點的身冬。
視圖和Presenter的交互會過于頻繁衅胀,使得他們的聯(lián)系過于緊密。也就是說酥筝,一旦視圖變更了滚躯,presenter也要變更。
MVP 存在的問題
1嘿歌、接口過多問題
使用MVP模式去構(gòu)建項目掸掏,會造成類文件和接口文件的過多,進(jìn)而增大包的體積宙帝。
解決方式:寫一個Contract接口丧凤,然后把與MVP相關(guān)接口全部列入到里面去。
2步脓、內(nèi)存泄漏
當(dāng)用戶關(guān)閉了View層愿待,但這時Model層如果仍然在進(jìn)行耗時操作,因為Presenter層也持有View層的引用靴患,所以造成垃圾回收器無法對View層進(jìn)行回收仍侥,這樣一來,就造成了內(nèi)存泄漏蚁廓。
解決方式:可以重寫onDestroy()方法访圃,在View銷毀時強制回收掉Presenter;或是采用弱引用的方式
MVP例子
基類
從上面說明中相嵌,我們知道MVP有三次View層腿时、Presenter層、Model層構(gòu)成饭宾,在構(gòu)建MVP架構(gòu)的時候需考慮到代碼的復(fù)用性已經(jīng)以后的可擴展性批糟,這樣我們先設(shè)計基類。
基類BaseActivity看铆、基類BaseFragment徽鼎,所有activity和fragment的基類,
基類BasePresenter,所有Presenter的基類
接口IBaseView否淤, 說明了每一個View基本需要的一些操作
這樣主要的一些基類就設(shè)計好了
BaseView
定義每一個View基本需要的一些操作
public interface IBaseView {
void showLoading();
void hideLoading();
void showError(String msg);
}
BasePresenter
因為Presenter將View與Model進(jìn)行隔離悄但,也將業(yè)務(wù)邏輯從View中抽離 。
1石抡、Presenter處理的數(shù)據(jù)需要回顯到View上時檐嚣,就需要持有View的引用,也就是Presenter需要 綁定 View 和解綁 View (onAttachView(V view)
onDetachView()
)啰扛。所有的View都是IBaseView的子類嚎京, 所以必須要傳入一個泛型的 View 層接口 。
2隐解、View展現(xiàn)的數(shù)據(jù)需要從Model層獲取鞍帝,而Presenter是他們的橋梁,所有Presenter也需要持有Model層引用煞茫。
public class BasePresenter<V extends IBaseView> {
//用于及時取消訂閱帕涌,以防止內(nèi)存泄漏
private CompositeDisposable mDisposable;
protected ApiServer mApiServer = ApiServer.getInstance();
private SoftReference <V> mReferenceView;
/**
* activity 創(chuàng)建的時關(guān)聯(lián)presenter
* @param view
*/
public void onAttachView(V view) {
this.mReferenceView = new SoftReference<>(view);
}
/**
* activity 關(guān)閉時解除綁定,防止內(nèi)存泄漏
*/
public void onDetachView() {
this.mReferenceView.clear();
this.mReferenceView = null;
removeDisposable();
}
public V getMvpView() {
return this.mReferenceView.get();
}
protected void addDisposable(Disposable disposable) {
if (mDisposable == null) {
mDisposable = new CompositeDisposable();
}
if (!mDisposable.isDisposed()) {
mDisposable.add(disposable);
}
}
protected void removeDisposable() {
if (mDisposable != null) {
mDisposable.dispose();
mDisposable = null;
}
}
}
在BasePresenter中用到了SoftReference,這樣就更加嚴(yán)謹(jǐn)溜嗜,防止內(nèi)存泄漏宵膨。
BaseActivity
BaseActivity
public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(initLayoutID());
initViews();
initViewListener();
}
/**
* 初始化布局文件
* @return 返回布局文件的xml
*/
protected abstract int initLayoutID();
/**
* 初始化View控件
*/
protected abstract void initViews();
/**
* 初始化View的事件
*/
protected abstract void initViewListener();
上面的BaseActivity就是我們在MVC里面用到的基類,但在MVP架構(gòu)中炸宵,需要實現(xiàn)IBaseView,同時要初始化Presenter以及綁定Presenter谷扣。
public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity implements IBaseView {
private MaterialDialog mDialog;
protected P mPresenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(initLayoutID());
initPresenter();
initViews();
initViewListener();
}
/**
* 初始化Presenter
*/
protected void initPresenter() {
mPresenter = createrPresenter();
if (mPresenter != null) {
mPresenter.onAttachView(this);
}
}
protected abstract P createrPresenter();
/**
* 初始化布局文件
*
* @return 返回布局文件的xml
*/
protected abstract int initLayoutID();
/**
* 初始化View控件
*/
protected abstract void initViews();
/**
* 初始化View的事件
*/
protected abstract void initViewListener();
@Override
public void showLoading(String msg) {
if (mDialog != null) {
mDialog = mDialog.getBuilder().title(msg).build();
mDialog.show();
} else {
MaterialDialog.Builder builder = MaterialDialogUtils.showIndeterminateProgressDialog(this, msg, true);
mDialog = builder.show();
}
}
@Override
public void hideLoading() {
if (mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
}
}
@Override
public void showError(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mPresenter != null) {
mPresenter.onDetachView();
}
}
這樣簡單的基類代碼就完成了土全。
具體的用法
那到底具體怎么用呢?定義一個簡單的按鈕会涎,然后點擊請求網(wǎng)絡(luò)數(shù)據(jù)裹匙,然后顯示。
定義一個Activity 繼承BaseActivity末秃,實現(xiàn)IMainView接口概页,對應(yīng)的MainPresenter里面處理請求網(wǎng)絡(luò),結(jié)果回顯給頁面练慕。
MainActivity:
public class MainActivity extends BaseActivity<MainPresenter> implements IMainView {
TextView textView;
Button btnSuccess;
@Override
protected MainPresenter createrPresenter() {
return new MainPresenter();
}
@Override
protected int initLayoutID() {
return R.layout.activity_main;
}
@Override
protected void initViews() {
textView = findViewById(R.id.textView);
btnSuccess = findViewById(R.id.btn_success);
}
@Override
protected void initViewListener() {
btnSuccess.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPresenter.requestData();
}
});
}
@Override
public void showData(List<Banner> banners) {
textView.setText(new Gson().toJson(banners));
}
}
IMainView:
public interface IMainView extends IBaseView {
void showData(List<Banner> banners);
}
MainPresenter:
public class MainPresenter extends BasePresenter<IMainView> {
void requestData() {
if (getView() == null) {
return;
}
addDisposable(mApiServer.toSubscribe(mApiServer.getApi().getBanner(),
new BaseObserver<List<Banner>>(getView(), "加載中...") {
@Override
protected void onSuccess(List<Banner> banners) {
if (getView() != null) {
getView().showData(banners);
}
}
@Override
protected void onError(ApiException ex) {
if (getView() != null) {
getView().showError(ex.getMessage());
}
}
}));
}
}
這樣顯示整個例子就完成了惰匙,效果如下:
項目地址
歡迎轉(zhuǎn)載,但是請注明出處