Android架构设计模式(二)

前言

在Android日常开发中或者在面试过程中总会涉及到“设计模式”这个词。听起来很厉害。实际上在开发中很常见又很难用准确的言语表达出来。随口说出的设计模式有:单例模式,中介者模式,观察者模式等等这些都属于java设计模式,这将会以单独的系列篇在以后的文章中总结。此设计模式系列仅含有应用架构设计模式,这里我就MVC,MVP,MVVM这3个最常见的架构设计模式来总结。

MVP设计模式

上一篇中总结了著名的MVC架构,本篇来学习MVC的一个演化版本——MVP模式,MVP全称Model View Presenter。目前在Android应用开发中越来越重要。MVP能够有效地降低View的复杂性,避免业务逻辑被塞进View中,使View变成一个混乱的“深坑”。MVP模式会解除View与Model的耦合,同时又带来了良好的扩展性、可测试性,保证了系统的整洁性、灵活性。MVP结构图如下图所示。
《Android架构设计模式(二)》

MVP定义

  1. M:依然是业务逻辑和实体模型
  2. V:对应于Activity,负责View的绘制以及与用户交互
  3. P:负责完成View与Model间的交互

MVP实现原理

MVP模式可以分离显示层和逻辑层,他们之间通过接口进行通信,降低耦合。理想化的MVP模式可以实现同一份逻辑代码搭配不同的显示界面,因为它们之间并不依赖于具体,而是依赖于抽象,这使得Presenter可以运用于任何实现了View逻辑接口的UI,这样就具有更广泛的适用性以及灵活性。

MVP的实现

模拟一个常用场景:加载耗时的列表中数据并显示。实现效果图如下。
《Android架构设计模式(二)》

MainActivity.java

public class MainActivity extends AppCompatActivity implements HeroViewInterface {
    private ListView lv_heroes;
    private ProgressBar mProgressBar;
    private Button bt_add;
    private List<Hero> heroes = new LinkedList<>();
    private HeroAdapter mAdapter;
    private HeroPresenter mPresenter;
    private List<Hero> all_heroes;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
        mPresenter = new HeroPresenter(this);
        all_heroes = getAllHeroes();
        mPresenter.showHeroes(generateHeroList(all_heroes));
        bt_add.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mPresenter.showHeroes(generateHeroList(all_heroes));
            }
        });
    }

    private void initViews() {
        lv_heroes = findViewById(R.id.lv_heroes);
        mProgressBar = findViewById(R.id.progress);
        bt_add = findViewById(R.id.bt_add);
        mAdapter = new HeroAdapter(this,R.layout.item_hero,heroes);
        lv_heroes.setAdapter(mAdapter);
    }

    @Override
    public void showHeroList(List<Hero> heroList) {
        heroes.addAll(heroList);
        mAdapter.notifyDataSetChanged();
        lv_heroes.setSelection(heroes.size()-1);
    }

    @Override
    public void showLoading() {
        mProgressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideLoading() {
        mProgressBar.setVisibility(View.GONE);
    }

    private List<Hero> getAllHeroes(){
        List<Hero> all_heroes = new ArrayList<>();
        all_heroes.add(new Hero("变体精灵",R.mipmap.btjl));
        all_heroes.add(new Hero("地穴编织者",R.mipmap.dxbzz));
        all_heroes.add(new Hero("恶魔巫师",R.mipmap.emws));
        all_heroes.add(new Hero("复仇之魂",R.mipmap.fczh));
        all_heroes.add(new Hero("发条地精",R.mipmap.ftdj));
        all_heroes.add(new Hero("风行者",R.mipmap.fxz));
        all_heroes.add(new Hero("撼地神牛",R.mipmap.hdsn));
        all_heroes.add(new Hero("剧毒术士",R.mipmap.jdss));
        all_heroes.add(new Hero("极寒幽魂",R.mipmap.jhyh));
        all_heroes.add(new Hero("谜团",R.mipmap.mt));
        all_heroes.add(new Hero("影魔",R.mipmap.sf));
        all_heroes.add(new Hero("水晶室女",R.mipmap.sjsn));
        all_heroes.add(new Hero("幽鬼",R.mipmap.ug));
        all_heroes.add(new Hero("巫妖",R.mipmap.wy));
        all_heroes.add(new Hero("遗忘法师",R.mipmap.ywfs));
        all_heroes.add(new Hero("潮汐猎人",R.mipmap.cxlr));
        all_heroes.add(new Hero("巫医",R.mipmap.wd));
        all_heroes.add(new Hero("末日使者",R.mipmap.mrsz));
        all_heroes.add(new Hero("秀逗魔导士",R.mipmap.xdmds));
        all_heroes.add(new Hero(" 魅惑魔女",R.mipmap.mhmn));
        return all_heroes;
    }

    private List<Hero> generateHeroList(List<Hero> all_heroes){
        List<Hero> heroes = new LinkedList<>();
        for (int i = 0; i < 3; i++) {
            int pos = new Random().nextInt(20) ;
            heroes.add(all_heroes.get(pos));
        }
        return heroes;
    }
}

HeroViewInterface.java

public interface HeroViewInterface {
    void showHeroList(List<Hero> heroes);//展示数据
    void showLoading();//显示进度条
    void hideLoading();//隐藏进度条
}

HeroPresenter.java

//Presenter,作为View和Model的中间人
public class HeroPresenter {
    //代表View的接口角色
    HeroViewInterface mHeroView;
    //代表model角色
    HeroModel mHeroModel = new HeroModelImpl();

    public HeroPresenter(HeroViewInterface viewInterface) {
        this.mHeroView = viewInterface;
    }

    //显示列表,也就是我们的业务逻辑
    public void showHeroes(List<Hero> heroes){
        mHeroView.showLoading();
        mHeroModel.setDataListener(new HeroModel.DataListener() {
            @Override
            public void onComplete(List<Hero> result) {
                mHeroView.showHeroList(result);
                mHeroView.hideLoading();
            }
        });
        mHeroModel.add(heroes);
    }

}

HeroModelImpl.java

public class HeroModelImpl implements HeroModel {
    private DataListener listener;

    public HeroModelImpl() {
    }

    @Override
    public void add(final List<Hero> heroes) {

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                if (listener != null) {
                    listener.onComplete(heroes);
                }
            }
        },2000);//模拟耗时操作

    }

    @Override
    public void setDataListener(DataListener listener) {
        this.listener =listener;
    }

}

HeroModel.java

public interface HeroModel {
    void add(List<Hero> heroes);

    void setDataListener(DataListener listener);

    interface DataListener{
        void onComplete(List<Hero> result);
    }
}

HeroAdapter.java

public class HeroAdapter extends ArrayAdapter<Hero> {
    private Context mContext;
    private int layoutId;
    private List<Hero> heroes;

    public HeroAdapter(@NonNull Context context, int resource, @NonNull List<Hero> objects) {
        super(context, resource,objects);
        this.mContext = context;
        this.layoutId = resource;
        this.heroes = objects;
    }

    @NonNull
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view;
        ViewHolder holder;
        if (convertView == null){
            view = LayoutInflater.from(mContext).inflate(layoutId,parent,false);
            holder = new ViewHolder();
            holder.iv_icon = view.findViewById(R.id.iv_icon);
            holder.tv_name = view.findViewById(R.id.tv_name);
            view.setTag(holder);
        }else {
            view = convertView;
            holder = (ViewHolder) view.getTag();
        }
        holder.iv_icon.setImageResource(heroes.get(position).getResId());
        holder.tv_name.setText(heroes.get(position).getName());
        return view;
    }

    class ViewHolder {
        ImageView iv_icon;
        TextView tv_name;
    }

}

Hero.java

public class Hero {
    private String name;
    private int resId;

    public Hero(String name, int resId) {
        this.name = name;
        this.resId = resId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getResId() {
        return resId;
    }

    public void setResId(int resId) {
        this.resId = resId;
    }
}

MVP总结

综上而看,MVP有很多优点,例如易于维护、测试、松耦合、复用性高、健壮稳定,易于扩展等,但是,由于Presenter经常性的需要执行一些耗时操作,例如我们模拟的耗时加载hero数据。而Presenter持有了MainActivity的强引用,如果在加载之前Activity被销毁了,那么由于回调还没有结束,导致Presenter一直持有MainActivity对象,造成内存泄露。此处应该着重注意优化代码,这个问题可以解决。但本例并没有实现。
还有一点非常显而易见的就是明明一个简单的延时显示代码却coding了这么多字。也就是说逻辑清楚,进一步解耦的代价就是如此。若正在开发的项目并不是大型项目,而强行套用mvp模式开发就有点大材小用,多此一举了。

《Android架构设计模式(二)》

关注我的技术公众号,我会不定期推送关于安卓的文章,内容包含Android日常开发遇到中的问题、常用框架的介绍以及需要熟记的概念等等。
微信扫一扫下方的二维码即可关注:
《Android架构设计模式(二)》

    原文作者:fengpeitian
    原文地址: https://blog.csdn.net/weixin_37309888/article/details/79871920
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞