Android設(shè)計(jì)模式之MVP

引言

在Android的架構(gòu)中Activity,fragment,布局的xml相當(dāng)于View。然而在實(shí)際的開發(fā)過程中,Android的View層任務(wù)繁重,將V和C都糅雜在Activity、Fragment中,這就導(dǎo)致了在實(shí)際開發(fā)中View層太過累贅。MVP把Activity中的UI邏輯抽象成View接口,把業(yè)務(wù)邏輯抽象成Presenter接口,Model類還是原來的Model


mvc1.png

mvp1.png

MPV的介紹

  • 優(yōu)點(diǎn)
    1. 分離了視圖邏輯和業(yè)務(wù)邏輯,降低了耦合
    2. Activity只處理生命周期的任務(wù),代碼變得更加簡潔

    使用MVP之后,Activity就能瘦身許多了,基本上只有FindView、SetListener以及Init的代碼。其他的就是對Presenter的調(diào)用,還有對View接口的實(shí)現(xiàn)。這種情形下閱讀代碼就容易多了,而且你只要看Presenter的接口,就能明白這個(gè)模塊都有哪些業(yè)務(wù),很快就能定位到具體代碼。Activity變得容易看懂,容易維護(hù),以后要調(diào)整業(yè)務(wù)、刪減功能也就變得簡單許多

    1. 視圖邏輯和業(yè)務(wù)邏輯分別抽象到了View和Presenter的接口中去,提高代碼的可閱讀性
    2. Presenter被抽象成接口,可以有多種具體的實(shí)現(xiàn),所以方便進(jìn)行單元測試

    MVP中,由于業(yè)務(wù)邏輯都在Presenter里,我們完全可以寫一個(gè)PresenterTest的實(shí)現(xiàn)類繼承Presenter的接口,現(xiàn)在只要在Activity里把Presenter的創(chuàng)建換成PresenterTest,就能進(jìn)行單元測試了,測試完再換回來即可

    1. 把業(yè)務(wù)邏輯抽到Presenter中去,避免后臺線程引用著Activity導(dǎo)致Activity的資源無法被系統(tǒng)回收從而引起內(nèi)存泄露和OOM

    Android APP 發(fā)生OOM的最大原因就是出現(xiàn)內(nèi)存泄露造成APP的內(nèi)存不夠用,而造成內(nèi)存泄露的兩大原因之一就是Activity泄露(Activity Leak)(另一個(gè)原因是Bitmap泄露(Bitmap Leak))Activity是有生命周期的,用戶隨時(shí)可能切換Activity,當(dāng)APP的內(nèi)存不夠用的時(shí)候,系統(tǒng)會回收處于后臺的Activity的資源以避免OOM。采用傳統(tǒng)的MV模式,一大堆異步任務(wù)和對UI的操作都放在Activity里面,比如你可能從網(wǎng)絡(luò)下載一張圖片,在下載成功的回調(diào)里把圖片加載到 Activity 的 ImageView 里面,所以異步任務(wù)保留著對Activity的引用。這樣一來,即使Activity已經(jīng)被切換到后臺(onDestroy已經(jīng)執(zhí)行),這些異步任務(wù)仍然保留著對Activity實(shí)例的引用,所以系統(tǒng)就無法回收這個(gè)Activity實(shí)例了,結(jié)果就是Activity Leak。Android的組件中,Activity對象往往是在堆(Java Heap)里占最多內(nèi)存的,所以系統(tǒng)會優(yōu)先回收Activity對象,如果有Activity Leak,APP很容易因?yàn)閮?nèi)存不夠而OOM。采用MVP模式,只要在當(dāng)前的Activity的onDestroy里,分離異步任務(wù)對Activity的引用,就能避免 Activity Leak

  • 缺點(diǎn)

相對而言,MVP模式的代碼量就多了,類文件也多了,簡單的一個(gè)業(yè)務(wù)邏輯操作就要各方來配合協(xié)作(即是需要presenter 和 view的接口)。但是這個(gè)問題完全在可以接收的范圍。完全符合Java的抽象封裝設(shè)計(jì)原則(接口隔離,開閉原則)

通過實(shí)例介紹MPV的使用

mvp展示.gif
  • 一個(gè)簡單的訂單查詢 點(diǎn)擊查詢顯示 點(diǎn)擊清除取消顯示


    項(xiàng)目結(jié)構(gòu).png
  • 首先來看看MainActivity
package com.rrcc.mvp.mvpdemo.view;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;

import com.rrcc.mvp.mvpdemo.R;
import com.rrcc.mvp.mvpdemo.adapter.DailyPagerAdapter;
import com.rrcc.mvp.mvpdemo.model.OrderListBean;
import com.rrcc.mvp.mvpdemo.model.OrdersModelImpl;
import com.rrcc.mvp.mvpdemo.presenter.IPresenterImpl;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity implements ISearchView, View.OnClickListener{
    private RecyclerView mRecyclerView;
    private DailyPagerAdapter mDailyPagerAdapter = null;
    private List<OrderListBean.DataBean> mList = new ArrayList<OrderListBean.DataBean>();
    private Button mSearchBtn = null;
    private Button mCancelBtn = null;
    private IPresenterImpl mIPresenterImpl = null;
    private OrdersModelImpl mOrdersModelImpl = null;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mOrdersModelImpl = new OrdersModelImpl();
        mIPresenterImpl = new IPresenterImpl(mOrdersModelImpl,this);
        initView();
    }


    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_search:
                mIPresenterImpl.doSearch();
                break;
            case R.id.btn_search_cancel:
                mIPresenterImpl.cancelSearch();
                break;
        }


    }

    @Override
    public void showResult(List<OrderListBean.DataBean> datas) {
       if(null!=datas)
           setView(datas);
    }

    @Override
    public void cancelShow(List<OrderListBean.DataBean> datas) {
        if(null!=datas)
            setView(datas);
    }

    private void initView(){
        mRecyclerView = findViewById(R.id.pager_shopping_recycler);
        mSearchBtn = findViewById(R.id.btn_search);
        mCancelBtn = findViewById(R.id.btn_search_cancel);

        mSearchBtn.setOnClickListener(this);
        mCancelBtn.setOnClickListener(this);

        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        mRecyclerView.setLayoutManager(layoutManager);

    }

    private void setView(List<OrderListBean.DataBean> mList){
        mDailyPagerAdapter = new DailyPagerAdapter(this,mList);
        mRecyclerView.setAdapter(mDailyPagerAdapter);
        mDailyPagerAdapter.notifyDataSetChanged();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mIPresenterImpl = null;
    }
}
  • 再來看看IPresenter
public interface IPresenter {
    void doSearch();
    void cancelSearch();
}
package com.rrcc.mvp.mvpdemo.presenter;
import com.rrcc.mvp.mvpdemo.model.OrderListBean;
import com.rrcc.mvp.mvpdemo.model.OrdersModelImpl;
import com.rrcc.mvp.mvpdemo.view.ISearchView;
import java.util.ArrayList;
import java.util.List;
public class IPresenterImpl implements IPresenter {
    private OrdersModelImpl mOrdersModelImpl = null;
    private ISearchView mSearchView = null;
    private List<OrderListBean.DataBean> mList = null;

    public IPresenterImpl(final OrdersModelImpl ordersModelImpl,final ISearchView searchView) {
        this.mOrdersModelImpl = ordersModelImpl;
        this.mSearchView = searchView;
        mList = new ArrayList<OrderListBean.DataBean>();
    }

    @Override
    public void doSearch() {
        mList = mOrdersModelImpl.getOrderList();
        mSearchView.showResult(mList);
    }

    @Override
    public void cancelSearch() {
        if(mList!=null)
            mList.clear();
        mSearchView.cancelShow(mList);
    }
}

從代碼可以看出,IPresenterImpl保留了ISearchView的引用,因此在IPresenterImpl里就可以直接進(jìn)行UI操作了,而不用在Activity里完成。這里使用了ISearchView引用,而不是直接使用Activity,這樣一來,如果在別的Activity里也需要用到相同的業(yè)務(wù)邏輯,就可以直接復(fù)用IPresenterImpl類了。

  • 再看看ISearchView 其實(shí)現(xiàn)類為 MainActivity
package com.rrcc.mvp.mvpdemo.view;
import com.rrcc.mvp.mvpdemo.model.OrderListBean;
import java.util.List;
public interface ISearchView {
   void showResult(List<OrderListBean.DataBean> data);
   void cancelShow(List<OrderListBean.DataBean> data);
}

項(xiàng)目代碼奉上https://github.com/JLhaoran/mvpdemo.git

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容