zoukankan      html  css  js  c++  java
  • 移动架构之MVP框架

    MVP是在开发中常用的框架,要了解其原理,先要从了解MVC开始,这里就对MVP框架做一个简单的介绍

    MVC

    MVC为Model,View与Controllor的缩写
    Model:业务逻辑和实体模型
    View:对应于布局文件,但是细细的想想这个View对应于布局文件,其实能做的事情特别少,实际上关于该布局文件中的数据绑定的操作,事件处理的代码都在Activity中,造成了Activity既像View又像Controller
    Controllor:对应于Activity

    MVP

    MVC为Model,View与Presenter的缩写
    Model:业务逻辑和实体模型
    View:对应于Activity,负责View的绘制以及与用户交互
    Presenter:负责完成View于Model间的交互

    MVP对比MVC

    • 减少了Activity的职责,简化了Activity中的代码,将复杂的逻辑代码提取到了Presenter中进行处理。与之对应的好处就是,耦合度更低

    • Activity 代码变得更加简洁:使用MVP之后,Activity就能瘦身许多了,基本上只有FindView、SetListener以及Init的代码。其他的就是对Presenter的调用,还有对View接口的实现。这种情形下阅读代码就容易多了,而且你只要看Presenter的接口,就能明白这个模块都有哪些业务,很快就能定位到具体代码。Activity变得容易看懂,容易维护,以后要调整业务、删减功能也就变得简单许多

    • 方便进行单元测试:一般单元测试都是用来测试某些新加的业务逻辑有没有问题,如果采用传统的代码风格(习惯性上叫做MV模式,少了P),我们可能要先在Activity里写一段测试代码,测试完了再把测试代码删掉换成正式代码,这时如果发现业务有问题又得换回测试代码,咦,测试代码已经删掉了!好吧重新写吧……MVP中,由于业务逻辑都在Presenter里,我们完全可以写一个PresenterTest的实现类继承Presenter的接口,现在只要在Activity里把Presenter的创建换成PresenterTest,就能进行单元测试了,测试完再换回来即可。万一发现还得进行测试,那就再换成PresenterTest吧

    • 避免 Activity 的内存泄露:

      • 发生OOM异常的原因:现内存泄露造成APP的内存不够用,而造成内存泄露的两大原因之一就是Activity泄露(Activity Leak)(另一个原因是Bitmap泄露(Bitmap Leak));Java一个强大的功能就是其虚拟机的内存回收机制,这个功能使得Java用户在设计代码的时候,不用像C++用户那样考虑对象的回收问题。然而,Java用户总是喜欢随便写一大堆对象,然后幻想着虚拟机能帮他们处理好内存的回收工作。可是虚拟机在回收内存的时候,只会回收那些没有被引用的对象,被引用着的对象因为还可能会被调用,所以不能回收;Activity是有生命周期的,用户随时可能切换Activity,当APP的内存不够用的时候,系统会回收处于后台的Activity的资源以避免OOM
      • MVC产生内存泄漏异常分析:采用传统的MVC模式,一大堆异步任务和对UI的操作都放在Activity里面,比如你可能从网络下载一张图片,在下载成功的回调里把图片加载到 Activity 的 ImageView 里面,所以异步任务保留着对Activity的引用。这样一来,即使Activity已经被切换到后台(onDestroy已经执行),这些异步任务仍然保留着对Activity实例的引用,所以系统就无法回收这个Activity实例了,结果就是Activity Leak。Android的组件中,Activity对象往往是在堆(Java Heap)里占最多内存的,所以系统会优先回收Activity对象,如果有Activity Leak,APP很容易因为内存不够而OOM
      • MVC模式如何比面内存泄漏:只要在当前的Activity的onDestroy里,分离异步任务对Activity的引用,就能避免 Activity Leak

    MVP角色

    View::负责绘制UI元素、与用户进行交互(在Android中体现为Activity)
    Activity interface:需要View实现的接口,View通过View interface与Presenter进行交互,降低耦合,方便进行单元测试
    Model:负责存储、检索、操纵数据(有时也实现一个Model interface用来降低耦合)
    Presenter:作为View与Model交互的中间纽带,处理与用户交互的负责逻辑

    开源MVP框架

    理解实例

    这里使用一个ListView的设计来说明MVP在实际项目开发过程中运用
    首先准备一个java bean类,用来作为ListView的显示部分

    public class Fruit {
        private int icon;
        private String name;
        private String describe;
    
        public Fruit(int icon, String name, String describe) {
            this.icon = icon;
            this.name = name;
            this.describe = describe;
        }
    
        public int getIcon() {
            return icon;
        }
    
        public void setIcon(int icon) {
            this.icon = icon;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getDescribe() {
            return describe;
        }
    
        public void setDescribe(String describe) {
            this.describe = describe;
        }
    }
    

    另外还需要一个数据类

    public class FruitListAdapter extends BaseAdapter {
        private LayoutInflater inflater;
        private List<Fruit> data;
    
        public FruitListAdapter(Context context, List<Fruit> data) {
            this.inflater = LayoutInflater.from(context);
            this.data = data;
        }
    
        @Override
        public int getCount() {
            return data.size();
        }
    
        @Override
        public Object getItem(int position) {
            return data.get(position);
        }
    
        @Override
        public long getItemId(int position) {
            return position;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder = null;
            if (convertView == null) {
                convertView = inflater.inflate(R.layout.item, null);
                holder = new ViewHolder();
                holder.iv_icon = convertView.findViewById(R.id.iv_icon);
                holder.tv_name = convertView.findViewById(R.id.tv_name);
                holder.tv_describe = convertView.findViewById(R.id.tv_describe);
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }
            holder.iv_icon.setImageResource(data.get(position).getIcon());
            holder.tv_name.setText(data.get(position).getName());
            holder.tv_describe.setText(data.get(position).getDescribe());
            return convertView;
        }
    
        private class ViewHolder {
            ImageView iv_icon;
            TextView tv_name;
            TextView tv_describe;
        }
    }
    

    model层

    public interface IFruitModel {
        void loadFruit(FruitOnLoadListener fruitOnLoadListener);
    
        //监听数据返回
        interface FruitOnLoadListener {
            void onComplete(List<Fruit> fruits);
        }
    }
    
    public class FruitModel implements IFruitModel {
        @Override
        public void loadFruit(FruitOnLoadListener fruitOnLoadListener) {
            List<Fruit> data = new ArrayList<>();
            data.add(new Fruit(R.drawable.p01, "石榴", "性味甘、酸涩、温,具有杀虫、收敛、涩肠、止痢等功效。石榴果实营养丰富,维生素C含量比苹果、梨要高出一二倍"));
            data.add(new Fruit(R.drawable.p02, "荔枝", "荔枝味甘、酸、性温,入心、脾、肝经;可止呃逆,止腹泻,是顽固性呃逆及五更泻者的食疗佳品,同时有补脑健身,开胃益脾,有促进食欲之功效。因性热,多食易上火。荔枝木材坚实,纹理雅致,耐腐,历来为上等名材"));
            data.add(new Fruit(R.drawable.p03, "猕猴桃", "猕猴桃的质地柔软,口感酸甜。味道被描述为草莓、香蕉、菠萝三者的混合。猕猴桃除含有猕猴桃碱、蛋白水解酶、单宁果胶和糖类等有机物,以及钙、钾、硒、锌、锗等微量元素和人体所需17种氨基酸外,还含有丰富的维生素C、葡萄酸、果糖、柠檬酸、苹果酸、脂肪"));
            data.add(new Fruit(R.drawable.p04, "香瓜", "各种香瓜均含有苹果酸、葡萄糖、氨基酸、甜菜茄、维生素C等丰富营养。全国各地广泛栽培,世界温带至热带地区也广泛栽培"));
            data.add(new Fruit(R.drawable.p05, "橘子", "橘子中的维生素A还能够增强人体在黑暗环境中的视力和治疗夜盲症。橘子不宜食用过量,吃太多会患有胡萝卜素血症,皮肤呈深黄色,如同黄疸一般"));
            data.add(new Fruit(R.drawable.p06, "柠檬", "柠檬因其味极酸,肝虚孕妇最喜食,故称益母果或益母子。柠檬中含有丰富的柠檬酸,因此被誉为“柠檬酸仓库”。它的果实汁多肉脆,有浓郁的芳香气。因为味道特酸,故只能作为上等调味料,用来调制饮料菜肴、化妆品和药品"));
            data.add(new Fruit(R.drawable.p07, "西瓜", "西瓜为夏季之水果,果肉味甜,能降温去暑;种子含油,可作消遣食品;果皮药用,有清热、利尿、降血压之效"));
            data.add(new Fruit(R.drawable.p08, "苹果", "苹果是一种低热量食物,每100克只产生60千卡热量。苹果中营养成分可溶性大,易被人体吸收,故有“活水”之称。其有利于溶解硫元素,使皮肤润滑柔嫩"));
            data.add(new Fruit(R.drawable.p09, "葡萄", "葡萄为著名水果,生食或制葡萄干,并酿酒,酿酒后的酒脚可提酒食酸,根和藤药用能止呕、安胎"));
            data.add(new Fruit(R.drawable.p10, "火龙果", "火龙果属凉性,且果肉的葡萄糖不甜,但其糖分却比一般水果的要高一些"));
            data.add(new Fruit(R.drawable.p11, "草莓", "原产南美,中国各地及欧洲等地广为栽培。草莓营养价值高,含有多种营养物质 ,且有保健功效"));
            data.add(new Fruit(R.drawable.p12, "柿子", "柿叶中含有多种活性成分,如维生素 C、 多种黄酮甙类、 二萜类、 胆碱、 β- 胡萝卜素等"));
            data.add(new Fruit(R.drawable.p13, "香蕉", "香蕉是淀粉质丰富的有益水果。味甘性寒,可清热润肠,促进肠胃蠕动,但脾虚泄泻者却不宜。根据“热者寒之”的原理,最适合燥热人士享用。痔疮出血者、因燥热而致胎动不安者,都可生吃蕉肉"));
            data.add(new Fruit(R.drawable.p14, "菠萝", "菠萝性平,味甘、微酸、微涩、性微寒,具有清暑解渴、消食止泻、补脾胃、固元气、益气血、消食、祛湿、养颜瘦身等功效,为夏令医食兼优的时令佳果,不过一次也不宜吃太多"));
            data.add(new Fruit(R.drawable.p15, "梨子", "梨含有大量蛋白质、脂肪、钙、磷、铁和葡萄糖、果糖、苹果酸、胡萝卜素及多种维生素。梨还是治疗疾病的良药,民间常用冰糖蒸梨治疗喘咳,“梨膏糖”更是闻名中外。梨还有降血压、清热镇凉的作用,所以高血压及心脏病患者食梨大有益处"));
            fruitOnLoadListener.onComplete(data);
        }
    }
    

    View层

    public interface IFruitView {
        //UI业务逻辑,加载进度条
        void showLoading();
        //回调给Present
        void showFruit(List<Fruit> fruits);
    }
    

    Present层

    public class FruitPresent {
        //持有视图层引用
        private IFruitView fruitView;
        //持有模型层引用
        private IFruitModel fruitModel = new FruitModel();
    
        public FruitPresent(IFruitView fruitView) {
            this.fruitView = fruitView;
        }
    
        public void fectch() {
            fruitView.showLoading();
            if (fruitModel != null) {
                //回调监听
                fruitModel.loadFruit(new IFruitModel.FruitOnLoadListener() {
                    @Override
                    public void onComplete(List<Fruit> fruits) {
                        fruitView.showFruit(fruits);
                    }
                });
            }
        }
    }
    

    MainActivity调用,这里的MainActivity其实就是View层的实现

    //View层
    public class MainActivity extends AppCompatActivity implements IFruitView {
    
        private ListView listView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            listView = findViewById(R.id.listview);
            //实例化Present并得到数据
            new FruitPresent(this).fectch();
        }
    
        @Override
        public void showLoading() {
    
        }
    
        @Override
        public void showFruit(List<Fruit> fruits) {
            listView.setAdapter(new FruitListAdapter(this,fruits));
        }
    }
    

    这里还应该对其优化,防止内存泄漏
    增加BasePresent类,使其添加绑定和解绑操作

    public abstract class BasePresent<T> {
        //持有UI接口的弱引用
        protected WeakReference<T> viewRef;
    
        //获取数据方法
        public abstract void fectch();
    
        //弱引用绑定
        public void attachView(T view) {
            viewRef = new WeakReference<T>(view);
        }
    
        //解绑
        public void detachView() {
            if (viewRef != null) {
                viewRef.clear();
                viewRef = null;
            }
        }
    }
    

    让其子类继承

    public class FruitPresent<T> extends BasePresent<IFruitView> {
        //持有视图层引用
        private IFruitView fruitView;
        //持有模型层引用
        private IFruitModel fruitModel = new FruitModel();
    
        public FruitPresent(IFruitView fruitView) {
            this.fruitView = fruitView;
        }
    
        @Override
        public void fectch() {
            fruitView.showLoading();
            if (fruitModel != null) {
                //回调监听
                fruitModel.loadFruit(new IFruitModel.FruitOnLoadListener() {
                    @Override
                    public void onComplete(List<Fruit> fruits) {
                        fruitView.showFruit(fruits);
                    }
                });
            }
        }
    }
    

    添加BaseActivity,完成绑定和解绑操作

    public abstract class BaseActivity<V, T extends BasePresent<V>> extends Activity {
        protected T present;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            present = createPresent();
            present.attachView((V) this);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            present.detachView();
        }
    
        //子类实现具体构建过程
        protected abstract T createPresent();
    }
    

    让其子类继承完成

    //View层
    public class MainActivity extends BaseActivity<IFruitView, FruitPresent<IFruitView>> implements IFruitView {
    
        private ListView listView;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main_v1);
            listView = findViewById(R.id.listview);
            present.fectch();
        }
    
        @Override
        protected FruitPresent<IFruitView> createPresent() {
            return new FruitPresent<>(this);
        }
    
        @Override
        public void showLoading() {
            Toast.makeText(this, "loading", Toast.LENGTH_SHORT).show();
        }
    
        @Override
        public void showFruit(List<Fruit> fruits) {
            listView.setAdapter(new FruitListAdapter(this, fruits));
        }
    }
    
  • 相关阅读:
    hihocoder 1049 后序遍历
    hihocoder 1310 岛屿
    Leetcode 63. Unique Paths II
    Leetcode 62. Unique Paths
    Leetcode 70. Climbing Stairs
    poj 3544 Journey with Pigs
    Leetcode 338. Counting Bits
    Leetcode 136. Single Number
    Leetcode 342. Power of Four
    Leetcode 299. Bulls and Cows
  • 原文地址:https://www.cnblogs.com/cj5785/p/10664606.html
Copyright © 2011-2022 走看看