android mvc设计模式

article/2025/8/23 13:38:22

  想在你的 Android 应用里优雅地分离出应用状态、用户交互和数据表现吗?

  一个开发平台的设计者是的不会有闲工夫为你量身打造一个高层的应用架构的。但是你恰恰可以利用这一点,充分利用你的自由,根据应用需求和规模来实现你自己的架构。如果你仅仅要做些简单的小玩意儿,那么好吧,你可以随意在你的 widgets(像 text fields、spinners 之类的)中存储数据,然后就在诸如 OnClickListener 中去操控它们。可是话又说回来,如果你要写些宏伟的应用,或者想让你的小玩意儿更丰富迷人一点呢?那你还是考虑考虑分个层吧,因为一个好的架构会鼎力支持你去添加新特性,满足你梦寐以求的高性能、高灵活性、高度响应性等等需求。当然,你的代码也会因此摆脱蓬头垢面心乱如麻的糟糕状态,从此过上幸福的生活……

  好吧,说正经的。在接下来的几分钟的光阴里,我将带你看一看如何根据大名鼎鼎的 MVC,来把你的代码大卸三块。呃……没这么恐怖,三块之间还是藕断丝连的,比如我会告诉你怎么用 Android 的 messaging 框架,来把 view (V) 和 controller (C) 连一连。我把这一实践应用到我自己的代码中,也许它并非 100% 的符合学术标准,也可能不是对每一个 app 都能成为天作之合,但是当我的应用变得越来越庞大的时候,我还是很享受它所给我带来的好处的。

Intro

  关于这个 MVC,我就不多说了吧,到处都可以找到它的“广告”。我也不介意你到我写的 <an article on how to apply this architecture to a single component> 里面看一看。你最好先了解了解 Model,View,Controller 这三个东西。另外呢,如果你还没有看过 <the article on Handler>,最好还是去看看吧。尽管这篇文章里没有完整描述 messaging 这朵奇葩,你还是可以在里面打听到一些关于线程模型的八卦。接下来我会一直带你绕着一个样例转一转,转到最后你会看到样例的源代码。

Part 1: Model

  把 Model 层泛化,并且让它只能接触到主逻辑,这是一个不错的开端。我们的样例看起来其实是一目了然加了无趣味,但还是有些有趣的地方。首先,我们有一个单独的类来包含 model 的“数据”——指的是,在应用的生命周期中, Model 层内可能发生变化的各种状态。

复制代码
 1 @Immutable
 2 public final class ModelData implements Serializable {
 3  
 4   private static final long serialVersionUID = 1L;
 5  
 6   private final int answer;
 7  
 8   public ModelData(int answer) {
 9     this.answer = answer;
10   }
11  
12   public final int getAnswer() {
13     return answer;
14   }
15 }
复制代码

  呃,你没有看走眼,我的确喜欢加个标注叫 @Immutable,这样一来我就会铭记这个类事实上是永恒不变的。这样有很多好处,比如说我能够在线程之间共享一个类的实例。另外,我还让这个类 Serializable,这样可以允许我实现一些例如 “Save/Load” 之类的特性。

   至于 Model 类本身,它长成这个样子:

复制代码
 1 @ThreadSafe
 2 public class Model {
 3   public interface Listener {
 4     void onModelStateUpdated(Model model);
 5   }
 6  
 7   private ModelData data = new ModelData(0);
 8  
 9   private final List<Listener> listeners = new ArrayList<Listener>();
10  
11   public Model() {
12  
13   }
14  
15   public final ModelData getData() {
16     synchronized (this) {
17       return data;
18     }
19   }
20  
21   public final void updateData() { // takes a while!
22     SystemClock.sleep(5000);
23     ModelData newData = new ModelData(new Random().nextInt(10) + 1);
24  
25     synchronized (this) {
26       data = newData;
27     }
28  
29     synchronized (listeners) {
30       for (Listener listener : listeners) {
31         listener.onModelStateUpdated(this);
32       }
33     }
34   }
35  
36   public final void addListener(Listener listener) {
37     synchronized (listeners) {
38       listeners.add(listener);
39     }
40   }
41  
42   public final void removeListener(Listener listener) {
43     synchronized (listeners) {
44       listeners.remove(listener);
45     }
46   }
47 }
复制代码

  方法 updateData() 要花一点时间来执行,在真实世界里,它可能是在请求服务器、执行繁重的计算等。data 被 synchronized 好好地保护了起来,不会被并发地访问。Listeners 也能安全地从并发线程中被添加或删除。如果你已经和 MVC 有过交往,你会意识到 model 的 listeners 是这个模式的中坚力量——它们让你能把任何代码附加到 model 上,而 model 却对此一无所知(因此 model 的代码就可以免受任何不相干的逻辑注入的伤害)。

  现在来看最有意思的部分——Controller 层。

Part 2: Controller

  其实吧,你可以通过很多种方式实现 Controller 这一层。 我选择的方式是基于 Handler, HandlerThread 与 Message 类的方法。当然,你多少要懂点它们的工作方式。

  说到底,Android 平台让你可以通过这几个类来实现应用中各部分之间高效、安全且强大的 messaging 通信。在我们的例子里,我想做的的东西是:

  • 送给 Controller 一个 inbox handler——这样 View 层就能给他写信(message)了,信的内容可以是“用户让我退出”或者“用户让我更新这些数据”以及其它一些甜言蜜语。
  • 送给 Controller 一个或多个 outbox handlers——这样 Controller 就也能给 View(或 Views,比较多情,你懂得)写信(message)了,比如“ model 已经更新完了”或者“你自杀吧,我要退出了”,等等。
  • 通过这样的方式,View 只能把用户事件装在信封(message)里寄给 Controller,并不用自己决定怎么对用户事件进行反应。一旦 Controller 给 View 寄来一封信(message),View 就会无脑地处理 Controller 告诉她的事情,并且给用户呈现出一些应用的状态。

  通过这样一个悲情的故事,我们很干净地就实现了 MVC 的架构,虽然有点不太仁义。

  由于 Handler 需要一个 Looper,我们需要选择一个线程,来运行 Controller 的 inbox handler。我们可以选择在 UI 线程中(它既能处理 UI 消息,又能处理 Controller 的消息)或者一个新的单独的 HandlerThread 中来运行。通常我们的 Controller 不会被困在一个很慢的操作中处理它的消息,这样它才能即使对 View 作出相应。所有很慢的操作都会在后台线程中运行。因此我们可以放心让 UI 线程来处理 Controller 消息。但是我还是选择运行一个单独的线程,仅仅是因为这样看起来架构会比较干净,也可能因为这样 Controller 的响应会有一点点的更快吧。既然 messageing 框架帮助我们摆脱了并发问题的牢笼,我们可以不用修改大量代码,就轻易地从一个 solution 跳到另外一个。

   来看看我们管理 inbox handler 和 outbox handler 的代码:

复制代码
 1 public class Controller {
 2  
 3         // ... some code omitted ...
 4  
 5   private final HandlerThread inboxHandlerThread;
 6   private final Handler inboxHandler;
 7   private final List<Handler> outboxHandlers = new ArrayList<Handler>();
 8  
 9         // ... some code omitted ...
10  
11   public Controller(Model model) {
12     this.model = model;
13  
14     inboxHandlerThread = new HandlerThread("Controller Inbox"); // note you can also set a priority here
15     inboxHandlerThread.start();
16  
17         // ... some code omitted ...
18  
19     inboxHandler = new Handler(inboxHandlerThread.getLooper()) {
20       @Override
21       public void handleMessage(Message msg) {
22         Controller.this.handleMessage(msg);
23       }
24     };
25   }
26  
27   public final void dispose() {
28     // ask the inbox thread to exit gracefully
29     inboxHandlerThread.getLooper().quit();
30   }
31  
32   public final Handler getInboxHandler() {
33     return inboxHandler;
34   }
35  
36   public final void addOutboxHandler(Handler handler) {
37     outboxHandlers.add(handler);
38   }
39  
40   public final void removeOutboxHandler(Handler handler) {
41     outboxHandlers.remove(handler);
42   }
43  
44   final void notifyOutboxHandlers(int what, int arg1, int arg2, Object obj) {
45     if (outboxHandlers.isEmpty()) {
46       Log.w(TAG, String.format("No outbox handler to handle outgoing message (%d)", what));
47     } else {
48       for (Handler handler : outboxHandlers) {
49         Message msg = Message.obtain(handler, what, arg1, arg2, obj);
50         msg.sendToTarget();
51       }
52     }
53   }
54  
55         // ... some code omitted ...
56 }
复制代码

  这些并不是 Controller 的完整代码。先说点其它的我们马上回来。你现在能看到的是我们如何初始化 inbox handler 与它的线程,如何提交一个 handler 到 outbox handlers 中去,以及如何用一个消息通知所有的 outbox handlers。

  有了这些代码,我们可以得到 inbox handler,并可以从 View 层向它发送消息,同时我们有绑定了 View 的 outbox handler,能够让 Controller 与它的 Views 有效地交换异步消息。

  为了能处理收到的消息并做点什么,我们需要在 Controller 类中实现 handleMessage() 方法。在大部分应用中,你都可能想在这里实现一个设计模式中的状态模式的实例,因为根据应用正在处理的工作的不同,我们可能需要对一个消息进行不同的处理,乃至忽略一些消息。例如,在我们的应用中,一旦你开始刷新模型了,你肯定不希望用户在刷新完成之前再刷新一次。为了表明这一点,我给出了 ControllerState 接口和它的 ReadyState 的实现:

复制代码
 1 public interface ControllerState {
 2   boolean handleMessage(Message msg);
 3 }
 4  
 5 final class ReadyState implements ControllerState {
 6  
 7   private final Controller controller;
 8  
 9   public ReadyState(Controller controller) {
10     this.controller = controller;
11   }
12  
13   @Override
14   public final boolean handleMessage(Message msg) {
15     switch (msg.what) {
16     case V_REQUEST_QUIT:
17       onRequestQuit();
18       return true;
19     case V_REQUEST_UPDATE:
20       onRequestUpdate();
21       return true;
22     case V_REQUEST_DATA:
23       onRequestData();
24       return true;
25     }
26     return false;
27   }
28  
29   private void onRequestData() {
30     // send the data to the outbox handlers (view)
31     controller.notifyOutboxHandlers(C_DATA, 0, 0, controller.getModel().getData());
32   }
33  
34   private void onRequestUpdate() {
35     // we can't just call model.updateState() here because it will block
36     // the inbox thread where this processing is happening.
37     // thus we change the state to UpdatingState that will launch and manage
38     // a background thread that will do that operation
39  
40     controller.changeState(new UpdatingState(controller));
41   }
42  
43   private void onRequestQuit() {
44     controller.quit();
45   }
46 }
复制代码

  把 Controller 和它的状态们(stages)放到同一个 package 中,我们可以让它们使用默认访问级别就能访问到 Controller 的内部。我就不把 UpdatingState 的代码贴这儿了,在页底的附件中都有。

  这里看看 Controller 是怎么把消息处理委托给它的 state 的:

复制代码
 1 public class Controller {
 2  
 3         // ... some code omitted ...
 4   private ControllerState state;
 5  
 6   public Controller(Model model) {
 7     this.model = model;
 8  
 9         // ... some code omitted ...
10  
11     this.state = new ReadyState(this);
12  
13         // ... some code omitted ...
14   }
15  
16         // ... some code omitted ...
17  
18   private void handleMessage(Message msg) {
19     Log.d(TAG, "Received message: " + msg);
20  
21     if (! state.handleMessage(msg)) {
22       Log.w(TAG, "Unknown message: " + msg);
23     }
24   }
25  
26   final Model getModel() {
27     return model;
28   }
29  
30   final void quit() {
31     notifyOutboxHandlers(C_QUIT, 0, 0, null);
32   }
33  
34   final void changeState(ControllerState newState) {
35     Log.d(TAG, String.format("Changing state from %s to %s", state, newState));
36     state = newState;
37   }
38 }
复制代码

  好了,Controller 大概就是这般模样,现在让我们介绍个 View 给它认识认识吧。

Part 3: View

  不用过多解释了,我猜你已经知道怎么做了。上代码(DemoActivity):

复制代码
public class DemoActivity extends Activity implements Handler.Callback, OnClickListener {
private static final String TAG = DemoActivity.class.getSimpleName();
private Controller controller;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
((Button) findViewById(R.id.update)).setOnClickListener(this);
((Button) findViewById(R.id.quit)).setOnClickListener(this);
controller = new Controller(new Model());
controller.addOutboxHandler(new Handler(this)); // messages will go to .handleMessage()
 
controller.getInboxHandler().sendEmptyMessage(V_REQUEST_DATA); // request initial data
    }
@Override
protected void onDestroy() {
// I think it is a good idea to not fail in onDestroy()
try {
controller.dispose();
} catch (Throwable t) {
Log.e(TAG, "Failed to destroy the controller", t);
} 
super.onDestroy();
}
@Override
public boolean handleMessage(Message msg) {
Log.d(TAG, "Received message: " + msg);
switch (msg.what) {
case C_QUIT:
onQuit();
return true;
case C_DATA:
onData((ModelData) msg.obj);
return true;
case C_UPDATE_STARTED:
onUpdateStarted();
return true;
case C_UPDATE_FINISHED:
onUpdateFinished();
return true;
}
return false;
}
private void onUpdateStarted() {
ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar);
progressBar.setVisibility(View.VISIBLE);
}
private void onUpdateFinished() {
ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar);
progressBar.setVisibility(View.GONE);
// request the updated data
    controller.getInboxHandler().sendEmptyMessage(V_REQUEST_DATA);
}
private void onData(ModelData data) {
TextView dataView = (TextView) findViewById(R.id.data_view);
dataView.setText("The answer is "+ data.getAnswer());
}
private void onQuit() {
Log.d(TAG, "Activity quitting");
finish();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.update:
controller.getInboxHandler().sendEmptyMessage(V_REQUEST_UPDATE);
break;
case R.id.quit:
controller.getInboxHandler().sendEmptyMessage(V_REQUEST_QUIT);
break;
}
}
}
复制代码

  在这一样例中,Activity 实际上占据了 Controller 的实例,而这个 Controller 是控制着 model 的。然而,如果你觉得 Service 的生命周期更能匹配 Controller/Model 的生命周期的话,你也可以考虑把 Controller 转移到一个 Service 中。

Summary

  你现在应该很清楚了,基于 message 的方法的的确确能允许我构建一个强大的、复杂的 Controller 与一个完全百依百顺的贤惠的 View。这样我们就有了一个很好的 MVC 的实现。

  正如我在开头所写的那样,这一解决方案从理论上讲不一定完全有效,也不一定就适合你的 app,但是我已经经历了构建一个非常复杂应用的过程,这中间有很多种状态,并且它完全依赖于消息。这一架构会有很多弱点,比如你要在某个地方聚合 message 的代码(见ControllerProtocol)并在文档里定义很多 message 参数。但是别忘了好处:它很好地实现了层间的分离,当你的 app 越长越大时,你更会体会到这一框架的美妙。

 

源代码:async-mvc.zip


http://chatgpt.dhexx.cn/article/D13xWWhF.shtml

相关文章

mvc与mvvm设计模式

MVC与MVVM设计模式 一、MVC(Model- View-Controller) Model(模型)&#xff1a;负责保存应用数据、和后端交互同步应用数据&#xff0c;Model主要与业务数据有关、与应用内交互状态无关View(视图)&#xff1a;&#xff08;html、css&#xff09;负责构建和维护DOM元素&#xff…

MVC设计模式原理简述

开发MVC项目之前首先明确一点&#xff0c;MVC设计模式可以带来更好的软件结构和代码重用。 MVC如何工作 MVC是一个设计模式&#xff0c;它强制性的使应用程序的输入、处理和输出分开。使用MVC 应用程序被分成三个核心部件&#xff1a;模型、视图、控制器。它们各自处理自己的…

[Java]JavaWeb开发中的MVC设计模式

一、有关Java Web与MVC设计模式 学习过基本Java Web开发的人都已经了解了如何编写基本的Servlet&#xff0c;如何编写jsp及如何更新浏览器中显示的内容。但是我们之前自己编写的应用一般存在无条理性&#xff0c;对于一个小型的网站这样的编写没有任何问题&#xff0c;但是一但…

MVC设计模式及流程

MVC设计模式&#xff1a; 概述&#xff1a; 是一种开发结构合理、能使前后端分离、流程控制逻辑、业务逻辑调用与展示逻辑分离&#xff0c;实现高内聚&#xff0c;低耦合的设计模式。 M:数据实体模型(Model或Entity): 每一个类的成员变量字段对应数据库字段对应(对应的Entity…

SpringMVC(一)MVC设计模式

一、简介 MVC设计模式一般是指MVC框架&#xff0c;Model数据模型层&#xff0c;View视图层&#xff0c;Controller控制层。 使用MVC目的是将M和V实现代码分离&#xff0c;使统一程序有不同的表现形式。其中&#xff0c;View定义比较清晰&#xff0c;指用户界面。 web项目中&a…

MVC设计模式和UML类图

文章目录 一、UML 类图&#xff1a;二、MVC 设计模式 一、UML 类图&#xff1a; 14中UML图&#xff1a;https://blog.csdn.net/yoyo328/article/details/78009237 二、MVC 设计模式 MVC是常用的设计模式之一&#xff0c;将整个程序分为三个层次&#xff1a;视图模式层&…

JSP的MVC设计模式

文章目录 一. MVC模式概念1. M&#xff1a;Model&#xff08;模型&#xff09;2. V&#xff1a;View&#xff08;视图&#xff09;3. C&#xff1a;Controller&#xff08;控制器&#xff09;&#xff1a;4. 图示理解 二. MVC案例1. WebContent内容2. servlet包内容3. entity包…

MVC设计模式学习(一)

笔者对于MVC也是初学&#xff0c;写这篇文章的主要目的是方便自己去理解MVC的相关内容&#xff0c;所以有些描述并不是那么专业和全面&#xff0c;请多多包涵。 一、MVC设计模式概述 MVC全名是Model View Controller&#xff0c;是模型(model)&#xff0d;视图(view)&#xff…

MVC设计模式含义和优点

MVC模式&#xff08;Model-View-Controller&#xff09;是 软件工程 中的一种 软件架构 模式&#xff0c;把软件系统分为三个基本部分&#xff1a;模型&#xff08;Model&#xff09;、视图&#xff08;View&#xff09;和控制器&#xff08;Controller&#xff09;。 MVC模…

什么是MVC设计模式

直接上图&#xff0c;其中model 和view大家经常写&#xff0c;就不说了 有人可能并不清楚controller 到底是啥&#xff0c;其实就是经常写的 data source ,delegate,outlet什么的 先撇开那些乱七八糟的箭头单看他们之间的分界线&#xff0c;view与controller、model与controll…

MVC设计模式详解

MVC(Model View Controller)模型(model)&#xff0d;视图(view)&#xff0d;控制器(controller)&#xff1a; MVC本来是存在于Desktop程序中的&#xff0c;M是指数据模型&#xff0c;V是指用户界面&#xff0c;C则是控制器。使用MVC是将M和V的实现代码分离&#xff0c;从而使同…

MVC 设计模式概述

(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/53292312) 1、MVC设计模式: 如图所示,MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型 (Model)、视图(View)和控制器(Contr…

mvc设计模式与三层架构

mvc与三层架构 1.什么是mvc设计模式 写 Java Web 项⽬时会发现&#xff0c;一个中型或者大型项目随着代码的增多&#xff0c;会发现&#xff1a;代码既可以写在 src目 录下&#xff0c;也可以写在WebContent目 录下。 src 下可以建很多包&#xff0c; WebContent 下可以建很多…

随机游走模型

6.2.1 随机游走模型&#xff08;Random Surfer Model&#xff09; 《这就是搜索引擎&#xff1a;核心技术详解》第6章链接分析&#xff0c;本章主要介绍一些著名的链接分析方法。本节为大家介绍随机游走模型&#xff08;Random Surfer Model&#xff09;。 6.2 两个概念模型及算…

图嵌入表示学习—Node Embeddings随机游走

Random Walk Approaches for Node Embeddings 一、随机游走基本概念 想象一个醉汉在图中随机的行走&#xff0c;其中走过的节点路径就是一个随机游走序列。 随机行走可以采取不同的策略&#xff0c;如行走的方向、每次行走的长度等。 二、图机器学习与NLP的关系 从图与NLP的…

图机器学习——2.1 节点嵌入:基于随机游走

嵌入&#xff08;embedding&#xff09;方法是目前文本分析&#xff0c;知识图谱相关中非常常见的一种算法。其为表示学习的一类方法&#xff0c;可以自动地从数据中学习“有用”的特征&#xff0c;并可以直接用于后续的具体任务。后面学习的相关嵌入学习均为表示学习中的内容。…

Python模拟随机游走

随机游走模型由首先由爱因斯坦在1926年以数学方式描述。由于自然界中的许多实体会以不可预知的方式移动&#xff0c;因此随机游走模型用来描述这种不稳定的移动。在这种移动模型中&#xff0c;移动节点随机选择一个方向和速度来从当前位置移动到新的位置。下面展示一维和多维情…

Python基于随机游走模型的PageRank算法及应用

资源下载地址&#xff1a;https://download.csdn.net/download/sheziqiong/86812933 资源下载地址&#xff1a;https://download.csdn.net/download/sheziqiong/86812933 基于随机游走模型的 PageRank 算法及应用 一、课题背景介绍 1.1 随机游走模型 随机游走也称随机漫步&…

图——随机游走算法

文章目录 推荐基本概念PageRankPersonalRankTextRankSimRank 推荐基本概念 其中用户user[A,B,C],物品item[a,b,c,d]&#xff0c;用户和物品有以下的关系 上述便是一个典型的二分图&#xff0c;我们用G(V,E)来表示&#xff0c;其中V为用户user和物品item组成的顶点集即[A,B,C,…

python二维随机游走_Python模拟随机游走图形效果示例

本文实例讲述了Python模拟随机游走图形效果。分享给大家供大家参考,具体如下: 在python中,可以利用数组操作来模拟随机游走。 下面是一个单一的200步随机游走的例子,从0开始,步长为1和-1,且以相等的概率出现。纯Python方式实现,使用了内建的 random 模块: # 随机游走 i…