日常笔记 - MVP

2019-02-22  本文已影响35人  HongGang

1 前言

MVP模式是MVC模式在Android上的一种变体,要介绍MVP就得先介绍MVC。在MVC模式中,Activity应该是属于View这一层。而实质上,它既承担了View,同时也包含一些Controller的东西在里面。这对于开发与维护来说不太友好,耦合度大高了。把Activity的View和Controller抽离出来就变成了View和Presenter,这就是MVP模式。

2 基本实现

在实现MVP模式的过程中,会将Model、View、Presenter三者的行为(方法)抽象成接口,三者之间通过接口来相互调用,可以有效的降低耦合度

(1)Model(数据模型):Model就是数据模型,为View提供填充的数据,我们一般使用容器类来存储从网络或者本地的数据

public class IModel {
    // 定义接口方法,提供给 Presenter 来调用
    List<String> getData(boolean isNetWork);
}

public class ModelImpl implements IModel {
    @Override
    public List<String> getData(boolean isNetWork) {
        List<String> data = new ArrayList<>();
        if (!isNetWork) {
            for (int i = 0; i < 10; i++) {
                data.add("item" + i);
            }
        } else {
            // 从网络获取数据转换成 List<String> 的数据类型就可以了
        }
        return data;
    }
}

(2)Presenter(逻辑控制器):Presenter其实就是MVC中的控制器,用来将Model与View进行连接的 “房屋中介”,要当好中介就得与View和Model都有联系,即拥有其余两者的引用,来调用两者的方法

public interface IPresenter {
    // 定义 Presenter 的接口方法,提供给 View 来调用
    void start();
    void refresh();
}

public class PresenterImpl implements IPresenter {

    private IView iView;
    private IModel iModel;

    /**
     * 我们将 View 从 Presenter 的构造方法中传入,来将 Presenter 所持有的 IView 进行实例化,以便调用 View 中定义的方法
     *
     * 除此之外,我们还将 IModel 进行实例化,以便调用 ModelImpl 中实现的方法
     *
     * @param iView
     */
    public PresenterImpl(IView iView) {
        this.iView = iView;
        this.iModel = new ModelImpl();
    }

    @Override
    public void start() {
        // 此处调用 View 的初始化方法
        iView.init();
    }

    @Override
    public void refresh() {
        // 此处调用 View 的刷新方法
        iView.refresh(iModel.getData(false));
    }
}

(3)View(视图):View就是我们在Android设备中所看到的界面了,但是我们在View中不会处理一点逻辑,所有的逻辑都放在Presenter中进行调用,所以View中必须持有Presenter的对象,来调用PresenterImpl中实现的方法

public interface IView {
    // 从 View 中抽象出来的数据方法,提供给 Presenter 来调用
    void init();
    void refresh(List<String> data);
}

// 我们一般使用 Activity 或者 Fragment 来作为 IView 的实现类
public class MainActivity extends AppCompatActivity implements IView {

    private IPresenter presenter;
    private ListView lv;
    private List<String> data;
    private ArrayAdapter<String> adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 在此处将 IPresenter 接口的对象实例化为 PresenterImpl 类的对象,以调用 PresenterImpl 中实现的方法
        presenter = new PresenterImpl(this);
        // 进行初始化
        presenter.start();
    }

    @Override
    protected void onResume() {
        super.onResume();
        // 进行刷新
        presenter.refresh();
    }

    @Override
    public void init() {
        initView();
        loadData();
    }

    public void initView() {
        lv = findViewById(R.id.list);
    }

    public void loadData() {
        data = new ArrayList<>();
        adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, data);
        lv.setAdapter(adapter);
    }

    @Override
    public void refresh(List<String> data) {
        this.data.addAll(data);
        this.adapter.notifyDataSetChanged();
    }
}

3 进阶使用

从上面的分析可以得知,MVP将数据与视图进行分离,通过Presenter来进行逻辑控制,这样充分的进行解耦,在进行修改的时候,不会再牵一发而动全身了,优点十分明显,但是还是存在一些缺陷,比如类过多,比如内存泄漏,下面针对问题进行解决

(1)使用MVP我们会发现定义了大量的接口,并不是很好地进行管理,Google爸爸提出了契约类的方式来对定义的接口进行管理

// 具体写法就是将上面定义的接口放在 Contract 中进行统一管理,这样我们就不用再管理大量接口
public interface Contract {

    interface IModel {
        List<String> getData(boolean isNetWork);
    }

    interface IPresenter {
        void start();

        void refresh();
    }

    interface IView {
        void init();

        void refresh(List<String> data);
    }
}

(2)当我们网络数据的时候,会执行工作线程来获取数据,若是View突然关闭,但是工作线程还未执行完毕,就会导致内存泄漏,针对这种情况,可以使用软引用将View的强引用进行包装使用,除此之外,还可以将Presenter与View的生命周期进行绑定,当View销毁的同时,也将Presenter进行销毁

public interface Contract {

    interface IModel {
        List<String> getData(boolean isNetWork);
    }

    interface IPresenter {
        // 绑定
        void attachView(IView view);
        // 解绑
        void detachView();
        void start();
        void refresh();
    }

    interface IView {
        void init();
        void refresh(List<String> data);
    }
}

public class PresenterImpl implements Contract.IPresenter {

    private Contract.IModel iModel;
    // 使用软引用确保在进行垃圾回收的时候将内存进行回收
    private SoftReference<Contract.IView> softReference;

    @Override
    public void attachView(Contract.IView view) {
        this.iModel = new ModelImpl();
        this.softReference = new SoftReference<>(view);
    }

    @Override
    public void detachView() {
        if (softReference != null) {
            softReference.clear();
        }
    }

    private Contract.IView getView() {
        return softReference.get();
    }

    @Override
    public void start() {
        getView().init();
    }

    @Override
    public void refresh() {
        getView().refresh(iModel.getData(false));
    }
}

public class ModelImpl implements Contract.IModel {
    @Override
    public List<String> getData(boolean isNetWork) {
        List<String> data = new ArrayList<>();
        if (!isNetWork) {
            for (int i = 0; i < 10; i++) {
                data.add("item" + i);
            }
        } else {
            // 从网络获取数据转换成 List<String> 的数据类型就可以了
        }
        return data;
    }
}

public class MainActivity extends AppCompatActivity implements Contract.IView {

    private ListView lv;   
    private List<String> data;
    private ArrayAdapter<String> adapter;

    private Contract.IPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        presenter = new PresenterImpl();
        presenter.attachView(this);
        presenter.start();
    }

    @Override
    protected void onResume() {
        super.onResume();
        presenter.refresh();
    }

    @Override
    public void init() {
        initView();
        loadData();
    }

    public void initView() {
        lv = findViewById(R.id.list);
    }

    public void loadData() {
        data = new ArrayList<>();
        adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, data);
        lv.setAdapter(adapter);
    }

    @Override
    public void refresh(List<String> data) {
        this.data.addAll(data);
        this.adapter.notifyDataSetChanged();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        presenter.detachView();
    }
}

4 注意事项

在使用MVP模式进行开发的时候需要记得进行内存泄漏处理

上一篇下一篇

猜你喜欢

热点阅读