Android 架构 之 MVC
第一次写博客,好紧张。就怕自己写不好,语言组织不好,标点符号使用错误。大家如果在阅读的过程中发现有错别字、病句、技术错误等问题,欢迎留言 或者给我发邮件 39156263@qq.com。
首先说下在接下来的一系列的文章主要是说MVC、MVP、MVVM。今天我们主要将MVC部分,但是本文的重点不是说 MVC 如何使用、有什么优点、适应什么场景等。我们今天要说的是 为什么要使用MVC 或 MVP。我相信很多同学自己内心对于这个问题是没有一个明确的答案的。
好了,开始今天的主题 MVC。 MVC它的全称是 Model View Controller 它是一种架构设计模式。 它分三层 model、view、controller。 在Android中 model是模型层 指的是java bean,View是视图层 指的是各种layout文件 和 自定义的view,controller是控制层 指的是Activity 和 Network。MVC在java领域是非常成熟的,目前来说的话,后端绝大部分还是使用的这种模式,但是移动端使用的越来越少了。 下图是MVC的模型图
1562637362141.jpg
它的具体流程是 首先View层接收到用户的操作,然后View层将用户的操作交给Controller,然后Controller完成具体的业务逻辑,最后将得到的结果封装成model,交给View刷新。从模型图我们可以看出 Controller是作为媒介处于View 和 Model之间,model 和 view之间有紧密的联系,耦合性偏强。Controller既要做View层的职责,又要做model层的职责,这样就违反了面向对象的单一原则。
接下来我们看一个典型的MVC代码。首先新建一个项目 结构如下:
image
activity_main.xml文件代码如下
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<Button
android:id="@+id/mLoadingImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="下载图片"/>
<ImageView
android:id="@+id/mImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="20dp"/>
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity implements Callback{
private static final String IMG_PATH = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1563193466&di=93ef19d7a55e5329da4ba468a4af029d&imgtype=jpg&er=1&src=http%3A%2F%2Fwww.liuhaihua.cn%2Fwp-content%2Fuploads%2F2018%2F05%2F6vm2y2I.jpg";
private Button mLoadingImage;
private ImageView mImageView;
private Handler mHandler = new Handler(new Handler.Callback(){
@Override
public boolean handleMessage(Message msg) {
switch (msg.what){
case ImageDownloader.SUCCESS:{
mImageView.setImageBitmap((Bitmap) msg.obj);
break;
}
case ImageDownloader.ERROR:{
Toast.makeText(MainActivity.this, "下载失败", Toast.LENGTH_SHORT).show();
break;
}
}
return false;
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImageView = findViewById(R.id.mImageView);
mLoadingImage = findViewById(R.id.mLoadingImage);
mLoadingImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ImageBean imageBean = new ImageBean();
imageBean.setUrlPath(IMG_PATH);
new ImageDownloader().down(imageBean, MainActivity.this);
}
});
}
@Override
public void callback(int resultCode, ImageBean imageBean) {
Message msg = mHandler.obtainMessage(resultCode);
msg.obj = imageBean.getBitmap();
mHandler.sendMessage(msg);
}
}
Callback.java
public interface Callback {
/**
*
* @param resultCode 返回请求的code
* @param imageBean 返回的结果
*/
void callback(int resultCode, ImageBean imageBean);
}
ImageBean.java
public class ImageBean {
//网络图片地址
private String urlPath;
//bitmap对象
private Bitmap bitmap;
public String getUrlPath() {
return urlPath;
}
public void setUrlPath(String urlPath) {
this.urlPath = urlPath;
}
public Bitmap getBitmap() {
return bitmap;
}
public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}
}
ImageDownloader.java
public class ImageDownloader {
static final int SUCCESS = 200;
static final int ERROR = 404;
public void down(ImageBean imageBean, Callback callback) {
new Thread(new DownRunnable(imageBean, callback)).start();
}
static final class DownRunnable implements Runnable{
private ImageBean imageBean;
private Callback callback;
public DownRunnable(ImageBean imageBean, Callback callback) {
this.imageBean = imageBean;
this.callback = callback;
}
@Override
public void run() {
try {
URL url = new URL(imageBean.getUrlPath());
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setConnectTimeout(5000);
httpURLConnection.setRequestMethod("GET");
if(httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK){
Bitmap bitmap = BitmapFactory.decodeStream(httpURLConnection.getInputStream());
showUi(SUCCESS, bitmap);
}else {
showUi(ERROR, null);
}
} catch (Exception e) {
e.printStackTrace();
showUi(ERROR, null);
}
}
private void showUi(int code, Bitmap bitmap) {
if(callback != null){
imageBean.setBitmap(bitmap);
callback.callback(code, imageBean);
}
}
}
}
我们可以看到 当“下载图片”按钮接收到点击事件后,将事件传递给MainActivity 然后 MainActivity 将请求交给ImageDownloader处理,ImageDownloader开启异步线程处理完成后 通过接口回调返回ImageBean, 最后MainActivity拿到ImageBean通过Handler 更新UI。 从这就可以看出其实MainActivity 除了承担了控制层的工作外 还承担了View层的工作。
在实际的项目需求中,可能会有一些比较耗时的操作需要开启一个子线程处理,我们修改MainActivity代码如下:
public class MainActivity extends AppCompatActivity implements Callback{
private static final String IMG_PATH = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1563193466&di=93ef19d7a55e5329da4ba468a4af029d&imgtype=jpg&er=1&src=http%3A%2F%2Fwww.liuhaihua.cn%2Fwp-content%2Fuploads%2F2018%2F05%2F6vm2y2I.jpg";
private Button mLoadingImage;
private ImageView mImageView;
private Handler mHandler = new Handler(new Handler.Callback(){
@Override
public boolean handleMessage(Message msg) {
switch (msg.what){
case ImageDownloader.SUCCESS:{
mImageView.setImageBitmap((Bitmap) msg.obj);
break;
}
case ImageDownloader.ERROR:{
Toast.makeText(MainActivity.this, "下载失败", Toast.LENGTH_SHORT).show();
break;
}
}
return false;
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImageView = findViewById(R.id.mImageView);
mLoadingImage = findViewById(R.id.mLoadingImage);
mLoadingImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ImageBean imageBean = new ImageBean();
imageBean.setUrlPath(IMG_PATH);
new ImageDownloader().down(imageBean, MainActivity.this);
}
});
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(50000);
}
}).start();
}
@Override
public void callback(int resultCode, ImageBean imageBean) {
Message msg = mHandler.obtainMessage(resultCode);
msg.obj = imageBean.getBitmap();
mHandler.sendMessage(msg);
}
}
就是在onCreate方法中 启动了一个线程,在线程中 休眠50000毫秒 模拟耗时操作。
然后我们运行app 打开 Profiler 的 Memory 然后点击返回键退出MainActivity页面, 然后点击Dump Java Heap 如下:
image.png
可以看到,MainActivity内存溢出了。这就是MVC最致命的缺点,容易导致内存溢出。