博客主页
MVC:Model-View-Controller,经典模式,很容易理。
但是在Android实际开发中,这个View层对应于布局文件,其实能做的事情特别少,实际上关于该布局文件中的数据绑定操作,事件处理的代码都在Activity中;我们往往也会把具体的业务相关代码放到了Activity中;再加上Activity本身又承担着控制层的责任,这样导致Contrlller层越来越臃肿。
所以说,MVC真实存在的是MC(V),Controller与Model根本就分不开,View和Model严重耦合。
从图中可以看出,Controller是作为媒介,处于Model和View之间。Model和View之间有紧密的联系,耦合性偏强。
MVC主要缺点有两个:
优点:
public interface ImageModel {
// 从网络加载图片
void loadImage(String imagePath, OnImageListener listener);
interface OnImageListener {
void shopImage(Bitmap bitmap);
}
}
public class ImageModelImpl implements ImageModel {
@Override
public void loadImage(String imagePath, OnImageListener listener) {
if (listener != null && !TextUtils.isEmpty(imagePath)) {
// 模拟网络获取图片
if (!TextUtils.isEmpty(imagePath)) {
listener.shopImage(BitmapFactory.decodeFile(imagePath));
} else {
listener.shopImage(null);
}
}
}
}
public class MainActivity extends AppCompatActivity implements ImageModel.OnImageListener {
private static final String TAG = "===>";
// View层
private ImageView mShowImageView;
// Model层
private ImageModel mImageModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mShowImageView = findViewById(R.id.showImageView);
findViewById(R.id.loadImageBtn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 加载图片
mImageModel.loadImage("/mnt/sdcard/Pictures/2.png", MainActivity.this);
}
});
mImageModel = new ImageModelImpl();
}
@Override
public void shopImage(Bitmap bitmap) {
if (bitmap != null) {
// 展示图片
mShowImageView.setImageBitmap(bitmap);
}
}
}
MVP:Model-View-Presenter,MVC的一个演变模式,将Controller换成了Presenter,主要为了解决上述第一个缺点,将View和Model解耦,不过第二个缺点依然没有解决。
1、 MVP接口过多
2、 每一个功能,相对于MVC要多写好几个文件
3、 如果某一个界面中需要请求多个服务器接口,这个界面文件中会实现很多的回调接口,导致代码繁杂
4、 如果更改了数据源和请求中参数,会导致更多的代码修改
5、 额外的代码复杂度及学习成本
1、 减少了Activity的职责,简化了Activity中的代码,将复杂的逻辑代码提取到了Presenter中进行处理。与之对应的好处就是:耦合度更低
2、 Activity代码变得更加简洁:使用MVP之后,Activity就能瘦身许多了,基本上只有findView、setListener以及init的代码。其他的就是对Presenter的调用,还有对View接口的实现。这种情形下阅读代码就容易多了,而且你只要看Presenter的接口,就能明白这个模块都有哪些业务,很快就能定位到具体代码。Activity变得容易看懂,容易维护,以后要调整业务。删减功能也就变得简单许多。
3、 方便进行单元测试
4、 避免Activity的内存泄露
现内存泄露造成APP的内存不够用,而造成内存泄露的两大原因之一就是Activity泄露(Activity Leak)(另一个原因是Bitmap泄露(Bitmap Leak);
Java一个强大的功能就是其虚拟机的内存回收机制,这个功能使得Java用户在设计代码的时候,不用像C++用户那样考虑对象的回收问题。然而,Java用户总是喜欢随便写一大堆对象,然后幻想着虚拟机能帮他们处理好内存的回收工作。可是虚拟机在回收内存的时候,只会回收那些没有被引用的对象,被引用着的对象因为还可能会被调用,所以不能回收
Activity是有生命周期的,用户随时可能切换Activity,当APP的内存不够用的时候,系统会回收处于后台的Activity的资源以避免OOM。
5、 模块职责划分明显,层次清晰,接口功能清晰
6、 Model层和View层分离,解耦;修改View而不影响Model
7、 功能复用度高,方便;一个Presenter可以复用于多个View,而不用更改Presenter的逻辑
8、 如果后台接口还未写好,但已知返回数据类型的情况下,完全可以写出此接口完整的功能
1、View:负责绘制UI元素,与用户进行交互(在Android中体现为Activity)
2、Activity interface:需要View实现的接口,View通过View interface与Presenter进行交互,降低耦合,方便进行单元测试
3、Model:负责存储、检索、操纵数据(有时也实现一个Model interface用来降低耦合)
4、Presenter:作为View与Model交互的中间纽带,处理与用户交互的负责逻辑
从服务器端下拉最新的20篇文章,然后将每一篇文章的简介显示到列表上,当用户点击某项数据进入到另一个页面,该页面加载这篇文章的详细内容。
1、ArticleModel就是Model层接口
public interface ArticleModel {
// 加载文章数据
void loadArticles(OnArticleListener listener);
interface OnArticleListener {
void onLoadComplete(List<Article> data);
}
}
2、ArticleModelImpl实现了ArticleModel接口,用于加载网络数据,为了代码简单,这里睡眠2秒模拟从网络获取数据
public class ArticleModelImpl implements ArticleModel {
@Override
public void loadArticles(OnArticleListener listener) {
new LoadArticleTask(listener).execute();
}
private static class LoadArticleTask extends AsyncTask<Void, Void, List<Article>> {
private final OnArticleListener listener;
LoadArticleTask(OnArticleListener listener) {
this.listener = listener;
}
@Override
protected List<Article> doInBackground(Void... params) {
// 模拟网络请求
SystemClock.sleep(2000);
final List<Article> data = new ArrayList<>();
for (int i = 0; i < 40; i++) {
data.add(new Article("title-" + i, "message:" + i));
}
return data;
}
@Override
protected void onPostExecute(List<Article> data) {
if (listener != null) {
listener.onLoadComplete(data);
}
}
}
}
3、 ArticleViewInterface就是主界面的逻辑接口,代表View接口角色,用于Presenter回调View的操作
public interface ArticleViewInterface {
void showProgressBar(); // 显示进度条
void hideProgressBar(); // 隐藏进度条
void showArticles(List<Article> data); // 展示数据
}
4、Presenter层,作为View和Model的中间人。
public interface ArticlePresenter {
void loadArticles();
}
public class ArticlePresenterImpl implements ArticlePresenter {
// ArticleView的接口,代表了View角色
private ArticleViewInterface mView;
// 文章数据的Model,也就是Model角色
private ArticleModel mArticleModel;
public ArticlePresenterImpl(ArticleViewInterface view) {
this.mView = view;
mArticleModel = new ArticleModelImpl();
}
// 获取文章,也就是我们的业务逻辑
@Override
public void loadArticles() {
mView.showProgressBar();
mArticleModel.loadArticles(new ArticleModel.OnArticleListener() {
@Override
public void onLoadComplete(List<Article> data) {
// 数据加载完后,通知View层更新UI
mView.hideProgressBar();
mView.showArticles(data);
}
});
}
}
5、ArticleActivity需要实现ArticleViewInterface接口,并且需要建立与Presenter的联系,ArticleActivity的业务逻辑都交给Presenter进行处理,处理结果通过ArticleViewInterface接口回调给ArticleActivity类
public class ArticleActivity extends AppCompatActivity implements ArticleViewInterface {
private ProgressBar mProgressBar;
private ArrayAdapter<Article> mAdapter;
private ArticlePresenter mArticlePresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
// 构建ArticlePresenter,与ArticleActivity建立关联
mArticlePresenter = new ArticlePresenterImpl(this);
}
private void initView() {
mProgressBar = findViewById(R.id.load_progress_bar);
ListView listView = findViewById(R.id.list_view);
mAdapter = new ArrayAdapter<>(this, R.layout.item_list, R.id.item_title);
listView.setAdapter(mAdapter);
}
@Override
protected void onResume() {
super.onResume();
// 请求文章数据
mArticlePresenter.loadArticles();
}
@Override
public void showArticles(List<Article> data) {
mAdapter.setNotifyOnChange(true);
mAdapter.addAll(data); // 更新UI
}
// ....
}
由于Presenter经常需要执行一些耗时操作,如请求网络数据,而Presenter持有了ArticleActivity的强引用,如果在请求结束之前Activity被销毁了,那么由于网络请求还没有返回,导致Presenter一直持有ArticleActivity对象,使得ArticleActivity对象无法被回收,此时就发生内存泄露。
如何解决这样的问题呢?
我们可以通过弱引用和Activity、Fragment的生命周期来解决这个问题。
1、 首先建立一个Presenter抽象,BasePresenter,它是一个泛型类,泛型类型为View角色要实现的接口类型
public abstract class BasePresenter<V> {
private Reference<V> mViewRef; // View接口类型的弱引用
public void attachView(V view) {
mViewRef = new WeakReference<>(view); // 建立关联
}
public void detachView() {
if (mViewRef != null) {
mViewRef.clear();
mViewRef = null;
}
}
public boolean isViewAttached() {
return mViewRef != null && mViewRef.get() != null;
}
protected V getView() {
return mViewRef.get();
}
}
2、 创建一个MVPBaseActivity基类,通过这个基类的生命周期函数来控制它与Presenter的关系。MVPBaseActivity有两个泛型参数,第一个是View的接口类型,第二个是Presneter的具体类型
public abstract class MVPBaseActivity<V, T extends BasePresenter<V>> extends AppCompatActivity{
protected T mPresenter; // Presenter对象
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter = createPresenter(); // 创建Presenter
mPresenter.attachView((V) this);
}
@Override
protected void onDestroy() {
super.onDestroy();
mPresenter.detachView();
}
protected abstract T createPresenter();
}