前言:Android中使用MVP開發(fā)已經(jīng)很普遍了,網(wǎng)上也已經(jīng)有好多種MVP模式的文章和設(shè)計(jì)台颠。但是一直沒能達(dá)到想要的效果褐望。之前一直采用的 一個(gè)頁面對(duì)應(yīng)一個(gè)presenter(class)和一個(gè)view(interface)的模式,但是這種模式遇到頁面業(yè)務(wù)邏輯過多的時(shí)候串前,p層和v層的代碼量會(huì)特別大√崩铮現(xiàn)在說一下根據(jù)這個(gè)模式改進(jìn)后,達(dá)到單個(gè)頁面實(shí)現(xiàn)多個(gè)presenter荡碾。方便多種業(yè)務(wù)解耦谨读,讓單個(gè)文件代碼量不至于過大的方法。
防止看代碼太費(fèi)勁坛吁。
直接放鏈接:https://github.com/hanswook/mvpdemo
一劳殖、按照google的sample中contract(契約類)模式會(huì)存在一些內(nèi)存泄漏問題(長(zhǎng)時(shí)間的操作在頁面銷毀后,p層持有v層導(dǎo)致頁面無法被回收)拨脉。變種產(chǎn)生了basepresenter中進(jìn)行view和presenter的綁定解綁操作哆姻。如下代碼所示
public abstract class BasePresenter<V> {
public WeakReference<V> mViewRef;
protected Reference<Context> mContextRef;
public void attachModelView(V pView,Context context) {
mViewRef = new WeakReference<V>(pView);
this.mContextRef = new WeakReference<>(context);
}
//...省略中間部分
public void onDettach() {
if (null != mViewRef) {
mViewRef.clear();
mViewRef = null;
}
if (mContextRef != null) {
mContextRef.clear();
mContextRef = null;
}
}
BaseView
public interface BaseView {
//做一些統(tǒng)一的方法處理
}
BaseActivity
public abstract class BaseActivity<V, T extends BasePresenter<V>> extends AppCompatActivity implements BaseView{
protected T mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
mPresenter = createPresenter();
if (mPresenter != null) {
mPresenter.attachModelView((V) this, this);
}
mActivity = this;
init();
}
protected abstract int getLayoutId();
protected abstract void init();
/**
* 使用MVP模式時(shí)需要重寫該方法
* @return
*/
protected T createPresenter() {
return null;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mPresenter != null) {
mPresenter.onDettach();
}
}
}
頁面代碼(偽)
public class HomeActivity extends BaseActivity<IHomeView, HomePresenter>
implements IHomeView{
}
通過泛型,指明類型玫膀。一個(gè)頁面綁定一個(gè)presenter 實(shí)現(xiàn)一個(gè)view矛缨。統(tǒng)一在base里面處理presenter的綁定解綁,避免內(nèi)存泄漏,達(dá)到mvp的效果箕昭。
二灵妨、改進(jìn),上面的模式雖然可以實(shí)現(xiàn)mvp落竹,但是當(dāng)頁面邏輯過多時(shí)泌霍,P層代碼增長(zhǎng)速度會(huì)急劇上升。所以考慮之后筋量,決定一個(gè)頁面可以有多個(gè)presenter烹吵,實(shí)現(xiàn)多個(gè)view。將不同的業(yè)務(wù)解耦桨武,放在不同的presenter中肋拔,如果粒度合理,甚至可以同一套presenter復(fù)用到多個(gè)需要這種業(yè)務(wù)的頁面上呀酸。
話不多所凉蜂,直接放代碼。
base類 性誉,這是三個(gè)文件窿吩。
public interface IBaseView {
}
public interface IBasePresenter<V extends IBaseView> {
/**
* 綁定view到presenter
*
* @param pView
* @param context
*/
void onAttach(V pView, Context context);
/**
* 解綁view
*/
void onDettach();
}
public abstract class BasePresenter<V extends IBaseView> implements IBasePresenter<V> {
public WeakReference<V> mViewRef;
protected Reference<Context> mContextRef;
@Override
public void onAttach(V pView, Context context) {
mViewRef = new WeakReference<V>(pView);
this.mContextRef = new WeakReference<>(context);
}
// ....省略部分代碼
@Override
public void onDettach() {
if (null != mViewRef) {
mViewRef.clear();
mViewRef = null;
}
if (mContextRef != null) {
mContextRef.clear();
mContextRef = null;
}
}
}
BaseActivity 省略了部分代碼。完整的可以看鏈接错览。
public abstract class BaseActivity extends AppCompatActivity implements IBaseView {
protected List<IBasePresenter> mPresenters;
protected AppCompatActivity mActivity;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
mPresenters = new ArrayList<>();
mActivity = this;
addPresenters();
for (IBasePresenter presenter : mPresenters) {
presenter.onAttach(this, mActivity);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
for (IBasePresenter presenter : mPresenters) {
presenter.onDettach();
}
}
protected abstract void addPresenters();
protected void addToPresenters(IBasePresenter child) {
mPresenters.add(child);
}
這里我們模擬兩個(gè)業(yè)務(wù)纫雁,一個(gè)登錄模塊的獲取名稱,一個(gè)新聞模塊的獲取新聞列表倾哺。
public interface LoginContract {
interface IView extends IBaseView {
void showName(String name);
}
interface Presenter extends IBasePresenter<IView> {
void getName();
}
}
public interface NewsContract {
interface IView extends IBaseView {
void showNews(List<String> news);
}
interface Presenter extends IBasePresenter<IView> {
void getNews();
}
}
public class LoginPresenter extends BasePresenter<LoginContract.IView> implements LoginContract.Presenter {
@Override
public void getName() {
if (isAttach()) {
getView().showName("小剛");
}
}
}
public class NewsPresenter extends BasePresenter<NewsContract.IView> implements NewsContract.Presenter {
@Override
public void getNews() {
if (isAttach()) {
List<String> news = mockNews(); //模擬一些數(shù)據(jù)返回轧邪,方法省略
getView().showNews(news);
}
}
}
測(cè)試頁面代碼
public class MainActivity extends BaseActivity implements LoginContract.IView, NewsContract.IView {
private LoginContract.Presenter loginPresenter;
private NewsContract.Presenter newsPresenter;
@Override
protected void addPresenters() {
loginPresenter = new LoginPresenter();
newsPresenter = new NewsPresenter();
addToPresenters(loginPresenter);
addToPresenters(newsPresenter);
}
@Override
protected int getLayoutId() {
return R.layout.activity_main;
}
@Override
protected void init() {
}
public void testLogin(View view) {
loginPresenter.getName();
}
public void testNews(View view) {
newsPresenter.getNews();
}
@Override
public void showName(String name) {
Toast.makeText(mActivity, "name:" + name, Toast.LENGTH_SHORT).show();
}
@Override
public void showNews(List<String> news) {
Toast.makeText(mActivity, "news一共有" + news.size() + "條消息", Toast.LENGTH_SHORT).show();
}
}
在基類中統(tǒng)一處理presenter,和上面邏輯類似羞海,只是將單個(gè)presenter換成list忌愚,挨個(gè)綁定和解綁。basepresenter抽象類實(shí)現(xiàn)presenter接口却邓。采用契約類的模式約束業(yè)務(wù)硕糊。業(yè)務(wù)與業(yè)務(wù)之間解耦,有幾種業(yè)務(wù)腊徙,就產(chǎn)生幾種presenter简十。
三、數(shù)據(jù)層做了統(tǒng)一的封裝撬腾,全局單例獲取統(tǒng)一的實(shí)例勺远。無論是網(wǎng)絡(luò) 內(nèi)存 數(shù)據(jù)庫,統(tǒng)一將數(shù)據(jù)分業(yè)務(wù)放到一起去處理时鸵。這里p層中并沒有模擬出model的代碼,看似mvp中只有vp,沒有了m饰潜。 只是因?yàn)檫@篇記錄主要記錄關(guān)于vp的改動(dòng)初坠。model層并沒有太大修改。經(jīng)過統(tǒng)一封裝了之后彭雾,在presenter的init方法中獲取實(shí)例并操作碟刺。 故此處省略的model層的代碼。
僅此記錄開發(fā)中的一些改進(jìn)薯酝。歡迎勘誤半沽。多謝~