概念
MVC 全名 Model View Controller即Model(模型) View(视图) Controller(控制器),一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
注:mvc、mvp、mvvm等是框架,工厂模式策略模式等是设计模式,两者不要混淆。
这里推荐笔者另一文MVP for Android
MVC for Android
理解MVC:
我们通过示意图1来理解MVC是如何在Android上运作起来的。
View:与用户交互,响应用户的操作;
Control:接收View的事件请求、数据等,通知Model;
Model:Model接收到Control指令后,独立运作,将结果通知给View;
例如:User现在要搜索联系人名字为Jack的号码是多少
View:在EditTextView中填写了Jack,点击搜索按钮Button;
Control:接收到交互View发出的搜索请求,和字符串Jack,通知Model进行数据查询;
Model:Model接受到关键字Jack,进行名字匹配,完毕后,通过接口给View发送号码,通知View显示;
MVC特点:
1、通常Android中Activity充当Control,布局中的各种View控件充当View;因为Activity既有Control又有View,所以Control和View有少量的耦合性;
2、MVC把View和Model层很好的分离,启到了很好的解耦作用,耦合性低,减少代码模块之间的相互影响;
3、耦合性低,可方便添加需求,扩展代码,减少代码的修改量;
3、M、V、C三层模块分明,利于代码维护;
实例代码
就以上述搜索联系人例子,看MVC在Android的具体用法
例子就三个控件,EditText输入名字,Button搜索联系人,TextView显示搜索结果,布局如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context="example.yink.mvc.MainActivity">
<EditText
android:id="@+id/et_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/et_name"/>
<Button
android:id="@+id/bt_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/btn_search"/>
<TextView
android:id="@+id/tv_numbere"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/tv_num"/>
</LinearLayout>
那么布局里面的这些控件就可以视为View了。接下来是Control
public class MainActivity extends AppCompatActivity implements SearchNumListener, View.OnClickListener{
private EditText mEditTextName;
private Button mButtonSearch;
private TextView mTExtViewNum;
private SearchNumModelImpl mSearchNumModelImpl;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
mSearchNumModelImpl = new SearchNumModelImpl(this);
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, 1);
}
@Override
public void onSuccess(String num) {
mTExtViewNum.setText(num);
}
@Override
public void onError() {
Toast.makeText(this,"no number",Toast.LENGTH_SHORT).show();
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.bt_search:
mSearchNumModelImpl.getNum(mEditTextName.getText().toString(),this);
break;
}
}
private void initView() {
mEditTextName = (EditText) findViewById(R.id.et_name);
mButtonSearch = (Button) findViewById(R.id.bt_search);
mTExtViewNum = (TextView) findViewById(R.id.tv_numbere);
mButtonSearch.setOnClickListener(this);
}
}
MainActivity先初始化加载好要显示的View,EditText、Button、TextView;然后 new了一个SearchNumModelImpl搜索联系人的Model。
User输入完名字Jack后,点击搜索,Activity作为Control得到交互信息,Jack,通过SearchNumModelImpl的getNum获得号码。
public class SearchNumModelImpl implements SearchNumInterface {
private Context mContext;
public SearchNumModelImpl(Context context) {
mContext = context;
}
@Override
public void getNum(String name, SearchNumListener listener) {
if (isEmptyString(name) || isEmptyString(number(name))) {
listener.onError();
} else {
listener.onSuccess(number(name));
}
}
public String number(String name) {
Cursor cursor = mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
while (cursor.moveToNext()) {
String contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
String contactName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
if (name.equals(contactName)) {
Cursor phone = mContext.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + contactId, null, null);
if (phone.moveToNext()) {
String phoneNumber = phone.getString(phone.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
return phoneNumber;
}
}
}
return null;
}
public static boolean isEmptyString(String str) {
return str == null || str.trim().length() == 0;
}
}
SearchNumModelImpl就进行了联系人的查询操作,并返回查询到的号码;如果查询成功调用接口 listener.onSuccess返回number,如果查询失败listener.onError通知view查询失败;
这里我们看下这两个接口
public interface SearchNumListener {
void onSuccess(String num);
void onError();
}
SearchNumListener有两个方法,作用就是model进行一系列操作后方便通知View结果,可以是查询联系人,也可以是网络请求,也可以是算法等等;是和View的一个桥梁
public interface SearchNumInterface {
void getNum(String name, SearchNumListener listener);
}
SearchNumInterface的作用是对getNum方法的一个封装,具体的实现是通过SearchNumModelImpl
比如我后期要对查询联系人进行修改,不是从系统的数据库里进行查找,需要到第三方联系人Abc.apk数据库里去匹配联系人,那么我只需要新写一个AbcSearchNumModelImpl的实现类继承SearchNumInterface这个接口就可以了。
整个过程如下:
View交互:User点击button
Control:响应点击,发送name给Model;
Model:通过名字查询号码,返回给View显示;
这就是MVC模型的简单例子。
思考
一个好的框架可以让我们在开发当中更高效的开发和维护,MVC就是常用的框架之一,但是若是一个很简单的apk则不必试用框架反而显得代码过于冗余。而在实际开发中,则需要我们更多的思考,比如Control,是哪些操作应该放在Control里呢?如果处理不当,那么View和Control过多耦合,就不能达到我们预期想要的结果。那么开发当中又有哪些方法能够避免这些问题?
笔者另外一文例子和本文相同Android MVP,方便区分和理解两种框架