美文网首页
日常笔记 - MVP

日常笔记 - MVP

作者: HongGang | 来源:发表于2019-02-22 22:59 被阅读35次

    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模式进行开发的时候需要记得进行内存泄漏处理

    相关文章

      网友评论

          本文标题:日常笔记 - MVP

          本文链接:https://www.haomeiwen.com/subject/oyoyyqtx.html