MVC架构的三层模型

MVC的流程:
- View接收到用户操作,并将这些操作交给Controller
- Controller完成具体业务逻辑
- 得到结果的封装Model,再进行View更新
缺点:类似单线、Controller做的事情多,不可或缺的环节
MVC架构设计

在Android中Controller(如Activity)层做的事情特别多,要做Model的职责,又要做View层的职责,违反了面向对象的单一原则。
MVC的项目很多,中小项目中高达50%以上,这是由项目的适合场景决定的。
MVC的致命缺点:内存泄漏
Java经典模型及Android对照

Java而言,MVC是一种非常完善的设计。在Java后台设计流程是这样的,由View层(JSP)发起一个事件请求,Controller层(action)收到请求,控制功能模块完成业务逻辑,再将结果封装成Model层数据,最后刷新View层。

Controller层的业务逻辑尤为多,如注册/登录需求、网络请求、图片加载等等。
在所有的Android开发文档中,几乎没有MVC的字眼,也没有明确指出项目当中的模型就是MVC。
其实,MVC就存在项目中,如:新创建的项目,MainActivity,activity_main.xml,XxxBean等,在Controller层(MainActivity)中接收了View层的点击事件,在处理事件中操作了Model。也可以通过Model层(数据的更改)来刷新UI。
这就是最经典的、最简单的MVC。
代码演示
M层:
public class ImageBean {
private String requestPath; // 网络图片地址
private Bitmap bitmap; // 结果返回Bitmap对象
public String getRequestPath() {
return requestPath;
}
public void setRequestPath(String requestPath) {
this.requestPath = requestPath;
}
public Bitmap getBitmap() {
return bitmap;
}
public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}
}
V层:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/bt_get_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="获取图片" />
<ImageView
android:id="@+id/iv_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/bt_get_image" />
</RelativeLayout>
C层:
@ContentView(value = R.layout.activity_main)
public class MainActivity extends BaseActivity implements ImageCallback {
private final static String PATH = "http://xxx/yyy/zzz.png";
// 耗时操作,需要Handler子线程来接收。用来刷新UI的
private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
switch (msg.what) {
case ImageDownloader.SUCCESS:
imageView.setImageBitmap((Bitmap) msg.obj);
break;
case ImageDownloader.ERROR:
// toast error
break;
}
return false;
}
});
@InjectView(R.id.bt_get_image)
Button btnImg;
@InjectView(R.id.iv_image)
ImageView imageView;
@Override
protected void onStart() {
super.onStart();
}
// 触发ImageDownloader功能模块工作
@OnClick({R.id.bt_get_image})
public void getImage(View view){
ImageBean imageBean = new ImageBean();
imageBean.setRequestPath(PATH);
new ImageDownloader().down(this, imageBean);
}
// 请求完成,就需要handler来刷新UI,假设返回成功,就可以从ImageBean中拿到Bitmap对象
@Override
public void callback(int resultCode, ImageBean imageBean) {
Message message = handler.obtainMessage(resultCode);
message.obj = imageBean.getBitmap();
handler.sendMessage(message);
}
}
回调接口:
/**
* 一旦做耗时操作,就需要在Activity中接收这样的及时反馈,最平常的就是接口来处理。
*/
public interface ImageCallback {
/**
* @param resultCode 请求结果返回标识码
* @param imageBean Model层数据中bitmap对象(用于C层刷新V)
*/
void callback(int resultCode, ImageBean imageBean);
}
功能模块:下载图片
/**
* 功能模块
* 1.下载地址
* 2.下载后,通知Activity(通过Callback回调通知)
*/
public class ImageDownloader {
// 成功
public static final int SUCCESS = 200;
// 失败
public static final int ERROR = 404;
/**
* 下载需要地址,下载完成后需要回调给Activity
* @param callback 回调接口
* @param imageBean 携带地址的Model层对象
*/
public void down(ImageCallback callback, ImageBean imageBean) {
new Thread(new DownLoader(callback, imageBean)).start();
}
// final跟性能优化有关系
static final class DownLoader implements Runnable {
private final ImageCallback callback;
private final ImageBean imageBean;
public DownLoader(ImageCallback callback, ImageBean imageBean) {
this.callback = callback;
this.imageBean = imageBean;
}
@Override
public void run() {
InputStream inputStream = null;
try {
// 知道地址
URL url = new URL(imageBean.getRequestPath());
// 打开链接
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setConnectTimeout(5000);
httpURLConnection.setRequestMethod("GET");
if (httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
// 拿到流
inputStream = httpURLConnection.getInputStream();
// 将流转成Bitmap
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
showUi(SUCCESS, bitmap);
} else {
showUi(ERROR, null);
}
} catch (Exception e) {
e.printStackTrace();
showUi(ERROR, null);
} finally {
if (inputStream!=null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void showUi(int resultCode, Bitmap bitmap) {
if (callback != null) {
imageBean.setBitmap(bitmap);
callback.callback(resultCode, imageBean);
}
}
}
}
异常解决:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.uml">
<uses-permission android:name="android.permission.INTERNET"/>
<!-- 9.0设备之后,Android默认要求使用加密连接。
application节点下有个属性usesCleartextTraffic 9.0之前均为true,之后为false,所以要设置为true。-->
<application
android:usesCleartextTraffic="true"
android:name=".app.BaseApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Test">
<activity android:name=".activity.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
MVC 为何演变为 MVP ?
大家想象一个场景:用户在某个界面中按了返回键退出了,界面销毁了,那肯定是调用了onDestroy方法,那么该Activity是被回收了还是没有被回收?
修改下MainActivity中的代码,打开性能分析,看一下:
@Override
protected void onStart() {
super.onStart();
// 这不是耗时操作,是一个线程消耗
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(50000);
}
}).start();
}
run一下看看,点击Profiler,打开性能分析,进入内存:

现在线程开销已经跑起来了,点击返回键,在性能分析中点击快照。

根据包名进行分析,发现MainActivity并未结束:


这显然是个非常严重的问题:内存泄漏。
无论是 MVC、MVP、MVVM,Activity对于用户来说都是最直观的,Activity在销毁的时候,能不能干净、纯粹,是内存泄漏评估的一个点。但是这个程序告诉我们,onDestroy调用了,界面也销毁了,但是Activity并未被回收。这就是MVC中C层和M层,和V层之间的交互特别多,导致它很容易产生内存泄漏。
生活中这种例子常不常见呢?当你在玩一个应用,来了一个电话或微信消息,你接听了电话或打开了微信消息,花费了一些时间,再回头打开刚才的应用,发现它重启了,为什么?说明它是有内存泄漏的问题的。
致命弱点——内存泄漏,也许就是MVC演变为MVP的关键。
本文探讨了MVC架构的三层模型及其在Android中的应用,指出Controller层承担过多职责的问题,以及可能导致内存泄漏的致命缺点。通过代码演示和分析,揭示了MVC在实际项目中的体现,并解释了由于内存泄漏问题,MVC如何逐渐演变为MVP架构。
529

被折叠的 条评论
为什么被折叠?



