Android MVC

概念

MVC 全名 Model View Controller即Model(模型) View(视图) Controller(控制器),一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
注:mvc、mvp、mvvm等是框架,工厂模式策略模式等是设计模式,两者不要混淆。
这里推荐笔者另一文MVP for Android

示意图1

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,方便区分和理解两种框架

https://pan.baidu.com/s/1Y47H5UfUAuUeJR4adfRt-A

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容