最近负责一个androidAPP 项目的开发工作,现在结合自己以往的工作经验以及这个项目的开发过程谈一谈我对目前较为流行的设计模式——MVC模式的理解,分享给需要的人。
开发一个APP 其实很简单,会JAVA 然后 翻翻文档基本就可以写出一个android 程序。但开发一个“好”的应用就不简单,它需要好的程序结构、高质量的代码、稳定的整体表现。程序结构,对整个程序来说非常重要,好的结构有利于前期开发,后期维护以及版本的演进。
我刚接触开发工作的时候,写的第一个Android程序就只有一个java文件,把界面、事件、响应、逻辑、数据都塞在一个activity里面,代码很乱,估计只有我一人能看懂,程序出问题查找和修改都特别麻烦,后来参加老员工的一个技术分享会以及后来的工作中慢慢学习,深深体会到模块化编程的好处,慢慢地应用到实际项目中。MVC模式,也就是Model-View-Controller,从模块化设计的角度说就是把模型和视图分开,然后控制器作为消息传递的桥梁。
视图(View),是指界面元素及界面显示,用户能直接感知的、能操作的。比如一个Button,用户能点击,再比如一个ListView,用户可以看到列表的内容并且能选择某一项。这些一般是使用XML 来描述的,当然也可以是在程序里面编写或添加界面元素,还有一个比较特别的View ——WebView,通过html和css 结合它可以有很强大的展示能力。
模型(Model),是指业务逻辑,它应该具有以下功能:1、数据库读写;2、网络上传下载;3、数据解析封装;4、必要的操作进度通知;5、提供控制接口;6、向UI也就是视图层提供数据,模型并不是具体的一个数据或格式,而是实现上面6个功能的统称(它可以是不同的功能的集合),它是程序的核心。
控制器(Controller),可以说是一个中介,它一般有以下功能:1、监听并处理用户的操作,告知业务逻辑模块(模型)做出相应的任务;2、告知UI层(视图)有关业务逻辑的处理进度或者结果。Activity 是优秀的控制器,android 为我们提供了多种消息传递机制:Intent、BroadcastReceiver、接口回调等等,使一切变得简单。
下面通过一个简单例子来理解这几个部分:
1、 视图
主界面 activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true" >
</ListView>
</RelativeLayout>
列表项布局 view_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/textView_item_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="@+id/textView_item_content"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
2、模型
列表项Item.java
package com.test.item;
public class Item {
private String title;
private String content;
public Item(String title, String content)
{
super();
this.title = title;
this.content = content;
}
public String getTitle()
{
return title;
}
public void setTitle(String title)
{
this.title = title;
}
public String getContent()
{
return content;
}
public void setContent(String content)
{
this.content = content;
};
}
数据库操作DataBaseOperator.java
package com.test.data;
import java.util.ArrayList;
import java.util.List;
import com.test.item.Item;
public class DataBaseOperator {
public static List<Item> readData()
{
List<Item> itemList = new ArrayList<Item>();
//从数据中读取数据 填入itemList
return itemList;
}
}
适配器TestAdapter.java
package com.test.data;
import java.util.ArrayList;
import java.util.List;
import com.example.test.R;
import com.test.item.Item;
import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class TestAdapter extends BaseAdapter {
<span style="white-space:pre"> </span>private Activity context;
<span style="white-space:pre"> </span>private List<Item> itemList;
<span style="white-space:pre"> </span>public TestAdapter(Activity context)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>this.context = context;
<span style="white-space:pre"> </span>itemList = new ArrayList<Item>();
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>public void updateDataAsync()
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>new Thread()
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>@Override
<span style="white-space:pre"> </span>public void run()
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>List<Item> itemList_ = DataBaseOperator.readData();
<span style="white-space:pre"> </span>synchronized (itemList)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>itemList.clear();
<span style="white-space:pre"> </span>itemList = itemList_;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>// 这里通过Activity 通知ListView 刷新
<span style="white-space:pre"> </span>context.runOnUiThread(new Runnable()
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>@Override
<span style="white-space:pre"> </span>public void run()
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>TestAdapter.this.notifyDataSetChanged();
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>});
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}.start();
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>@Override
<span style="white-space:pre"> </span>public int getCount()
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>return itemList.size();
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>@Override
<span style="white-space:pre"> </span>public Object getItem(int index)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>return itemList.get(index);
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>@Override
<span style="white-space:pre"> </span>public long getItemId(int index)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>return index;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>@Override
<span style="white-space:pre"> </span>public View getView(int index, View convertView, ViewGroup arg2)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>Holder holder = null;
<span style="white-space:pre"> </span>if (convertView == null)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>LayoutInflater mInflater = ((Activity) context).getLayoutInflater();
<span style="white-space:pre"> </span>convertView = mInflater.inflate(R.layout.view_item, null);
<span style="white-space:pre"> </span>TextView tv_title = (TextView) convertView
<span style="white-space:pre"> </span>.findViewById(R.id.textView_item_title);
<span style="white-space:pre"> </span>TextView tv_content = (TextView) convertView
<span style="white-space:pre"> </span>.findViewById(R.id.textView_item_content);
<span style="white-space:pre"> </span>holder = new Holder(tv_title, tv_content);
<span style="white-space:pre"> </span>convertView.setTag(holder);
<span style="white-space:pre"> </span>} else
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>holder = (Holder) convertView.getTag();
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>Item item = itemList.get(index);
<span style="white-space:pre"> </span>holder.getTv_title().setText(item.getTitle());
<span style="white-space:pre"> </span>holder.getTv_content().setText(item.getContent());
<span style="white-space:pre"> </span>return convertView;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>class Holder {
<span style="white-space:pre"> </span>private TextView tv_title;
<span style="white-space:pre"> </span>private TextView tv_content;
<span style="white-space:pre"> </span>public Holder(TextView tv_title, TextView tv_content)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>super();
<span style="white-space:pre"> </span>this.tv_title = tv_title;
<span style="white-space:pre"> </span>this.tv_content = tv_content;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>public TextView getTv_title()
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>return tv_title;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>public void setTv_title(TextView tv_title)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>this.tv_title = tv_title;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>public TextView getTv_content()
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>return tv_content;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>public void setTv_content(TextView tv_content)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>this.tv_content = tv_content;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
}
3、控制器
MainActivity.java
package com.example.test;
import com.test.data.TestAdapter;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;
public class MainActivity extends Activity {
private ListView listView;
private TestAdapter testAdapter;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initUI();
testAdapter = new TestAdapter(this);
listView.setAdapter(testAdapter);
}
private void initUI()
{
listView = (ListView) findViewById(R.id.listView);
}
@Override
protected void onResume()
{
super.onResume();
testAdapter.updateDataAsync();
}
}
以上是通过控制器Activity 的runOnUiThread 方法 通知UI的,下面改用接口回调方式:
添加接口UpdateCallback.java
package com.test.data;
public interface UpdateCallback {
public void onDataUpdated(boolean if_Succ);
}
改TestAdapter.java
package com.test.data;
import java.util.ArrayList;
import java.util.List;
import com.example.test.R;
import com.test.item.Item;
import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class TestAdapter extends BaseAdapter {
private Activity context;
private UpdateCallback callback;
private List<Item> itemList;
public TestAdapter(Activity context,UpdateCallback callback)
{
this.context = context;
this.callback = callback;
itemList = new ArrayList<Item>();
}
public void updateDataAsync()
{
new Thread()
{
@Override
public void run()
{
List<Item> itemList_ = DataBaseOperator.readData();
synchronized (itemList)
{
itemList.clear();
itemList = itemList_;
}
// 这里通过接口 通知到控制器
if(itemList.size()>0)
callback.onDataUpdated(true);
else
callback.onDataUpdated(true);
}
}.start();
}
@Override
public int getCount()
{
return itemList.size();
}
@Override
public Object getItem(int index)
{
return itemList.get(index);
}
@Override
public long getItemId(int index)
{
return index;
}
@Override
public View getView(int index, View convertView, ViewGroup arg2)
{
Holder holder = null;
if (convertView == null)
{
LayoutInflater mInflater = ((Activity) context).getLayoutInflater();
convertView = mInflater.inflate(R.layout.view_item, null);
TextView tv_title = (TextView) convertView
.findViewById(R.id.textView_item_title);
TextView tv_content = (TextView) convertView
.findViewById(R.id.textView_item_content);
holder = new Holder(tv_title, tv_content);
convertView.setTag(holder);
} else
{
holder = (Holder) convertView.getTag();
}
Item item = itemList.get(index);
holder.getTv_title().setText(item.getTitle());
holder.getTv_content().setText(item.getContent());
return convertView;
}
class Holder {
private TextView tv_title;
private TextView tv_content;
public Holder(TextView tv_title, TextView tv_content)
{
super();
this.tv_title = tv_title;
this.tv_content = tv_content;
}
public TextView getTv_title()
{
return tv_title;
}
public void setTv_title(TextView tv_title)
{
this.tv_title = tv_title;
}
public TextView getTv_content()
{
return tv_content;
}
public void setTv_content(TextView tv_content)
{
this.tv_content = tv_content;
}
}
}
改MainActivity.java
package com.example.test;
import com.test.data.TestAdapter;
import com.test.data.UpdateCallback;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;
public class MainActivity extends Activity implements UpdateCallback {
private ListView listView;
private TestAdapter testAdapter;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initUI();
testAdapter = new TestAdapter(this, this);
listView.setAdapter(testAdapter);
}
private void initUI()
{
listView = (ListView) findViewById(R.id.listView);
}
@Override
protected void onResume()
{
super.onResume();
testAdapter.updateDataAsync();
}
@Override
public void onDataUpdated(boolean if_Succ)
{
if (if_Succ)
{
// 额外操作
} else
{
// 额外操作
}
runOnUiThread(new Runnable()
{
@Override
public void run()
{
testAdapter.notifyDataSetChanged();
}
});
}
}
1、我们知道android UI线程阻塞5s 就会导致程序异常,为了使应用有流畅的用户体验,业务逻辑模块应该采用多线程,而视图刷新需要在UI线程中,我们可以采用Activity.runOnUiThread 的方法,还有一种很常用的方法,就是使用Handler 来捕获子线程发送来的消息 这里不介绍;
2、关于线程安全的,多线程对同一资源(对象)操作时一定要实现同步锁。