
前言
在Android日常开发中或者在面试过程中总会涉及到“设计模式”这个词。听起来很厉害。实际上在开发中很常见又很难用准确的言语表达出来。随口说出的设计模式有:单例模式,中介者模式,观察者模式等等这些都属于java设计模式,这将会以单独的系列篇在以后的文章中总结。此设计模式系列仅含有应用架构设计模式,这里我就MVC,MVP,MVVM这3个最常见的架构设计模式来总结。
MVP设计模式
上一篇中总结了著名的MVC架构,本篇来学习MVC的一个演化版本——MVP模式,MVP全称Model View Presenter。目前在Android应用开发中越来越重要。MVP能够有效地降低View的复杂性,避免业务逻辑被塞进View中,使View变成一个混乱的“深坑”。MVP模式会解除View与Model的耦合,同时又带来了良好的扩展性、可测试性,保证了系统的整洁性、灵活性。MVP结构图如下图所示。
MVP定义
- M:依然是业务逻辑和实体模型
- V:对应于Activity,负责View的绘制以及与用户交互
- P:负责完成View与Model间的交互
MVP实现原理
MVP模式可以分离显示层和逻辑层,他们之间通过接口进行通信,降低耦合。理想化的MVP模式可以实现同一份逻辑代码搭配不同的显示界面,因为它们之间并不依赖于具体,而是依赖于抽象,这使得Presenter可以运用于任何实现了View逻辑接口的UI,这样就具有更广泛的适用性以及灵活性。
MVP的实现
模拟一个常用场景:加载耗时的列表中数据并显示。实现效果图如下。
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日常开发遇到中的问题、常用框架的介绍以及需要熟记的概念等等。
微信扫一扫下方的二维码即可关注:
![]()
网友评论