`
king_tt
  • 浏览: 2232940 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Android 架构:基于消息的 MVC

 
阅读更多

译自 <http://mindtherobot.com/blog/675/android-architecture-message-based-mvc/>

注:由于本文原文链接 mindtherobot 网站被浏览器报告为恶意网站,故本文清除了链接内容,但是本人访问过该链接,并未发现异常,如果放心的话可以复制链接地址自己去浏览。文中有个别地方在原文中有链接,本文一律加上<>处理,不提供直接链接。

译序:Android 平台的开发框架貌似有点 MVC 的意思,比如用 xml 文件做 View 之类的,但是这样写出的代码还是会出现冗长的 Activity。网上很多文章在介绍 Android MVC 的时候都没有根本解决这一问题。偶尔发现这么一篇文章讲的很不错,于是决定翻译过来和大家共享。有些语言和原文不太一致,但都是为了表词达意用了自己的话,但相信还是保留了原作者的意思。第一次翻译技术文章,不当之处欢迎指正。

  想在你的 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, HandlerThreadMessage类的方法。当然,你多少要懂点它们的工作方式。

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

  • 送给 Controller 一个inboxhandler——这样 View 层就能给他写信(message)了,信的内容可以是“用户让我退出”或者“用户让我更新这些数据”以及其它一些甜言蜜语。
  • 送给 Controller 一个或多个outboxhandlers——这样 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

分享到:
评论

相关推荐

    安卓Android源码:基于Java的安卓个人财务管理系统

    首先,这个系统采用了MVC(Model-View-Controller)架构模式。Model负责数据处理和业务逻辑,View负责显示用户界面,而Controller作为桥梁,协调Model和View的交互。在Java中,我们可以使用Android提供的SQLite...

    Android mvc详解

    **GeoQuiz** 是一个典型的基于MVC架构的Android应用案例。该应用主要包含两个核心组件:`TrueFalse` 类和 `Question` 类。 - **Model 层**:由 `Question` 类实现,用于存储和管理题目数据。例如,题目文本、答案等...

    基于 MVC 模式的博客(BLOG)系统的设计与实现

    本研究详细介绍了基于MVC模式的博客系统的开发过程,包括系统的架构设计、关键功能模块的设计以及所使用的开发技术和工具。通过采用MVC设计模式和Struts框架,该系统实现了良好的性能表现和用户体验。这对于构建类似...

    java技术架构图汇总.docx

    Android架构:Android是一个基于Linux的开源移动操作系统,旨在提供一个灵活的平台来开发移动应用程序。Android架构主要包括Linux内核、System Server、Activity Manager、Window Manager等几个部分。 云计算架构:...

    ASP.NET3.5MVC架构与实战

    在实战篇中,采用ASP.NET3.5技术,基于多层架构实现了原创的ASP.NET3.5MVC博客网站,便于初学者学习、运用ASP.NET3.5MVC技术。 目录 第3篇 实战篇 第19章 多层架构的博客网站 第20章 博客网站页面总体设计 第22章...

    MVCDemo:Android Java MVC示范

    MVCDemo是基于Java实现的一个Android MVC模式的示范应用,旨在帮助开发者理解和实践这种设计模式。 **一、MVC模式概述** MVC模式由三个主要组件构成: 1. **Model(模型)**:负责处理业务逻辑和数据管理。模型...

    Android架构师技能.pdf

    为了满足要求,我将基于标题中的内容——“Android架构师技能”——创建一份与Android架构师相关知识点的概要。 Android架构师是负责设计和制定Android应用基础架构的专业人员,他们需要掌握一系列的技能和知识以...

    Android MVC、MVP、MVVM、MVI架构示例

    本文将深入探讨四种常见的Android架构模式:MVC(Model-View-Controller)、MVP(Model-View-Presenter)、MVVM(Model-View-ViewModel)以及MVI(Model-View-Intent)。我们将详细解释每种模式的核心概念、优缺点,...

    基于Android平台的记事本App的开发与实现.pdf

    本文介绍了基于Android平台的记事本App的开发与实现过程,该App基于MVC模式,使用fragment、sqlite等技术,实现了信息的富文本编辑与查询、用户注册与登录、信息上传至服务器、PC端的后台管理等功能。 关键技术点:...

    Android项目架构

    本文将从不同层次对Android系统的架构进行详细分析,并重点介绍四种常见的项目架构模式:MVC(Model-View-Controller)、MVP(Model-View-Presenter)、MVVM(Model-View-ViewModel)及AndroidFlux。 ##### 1. 应用...

    Android开发之旅(2):android架构.pdf

    - 应用基于Application Framework构建,遵循MVC(Model-View-Controller)或其他设计模式,实现各种功能和用户界面。 总的来说,Android架构的设计旨在提供一个高效、灵活且可扩展的平台,让开发者能够充分利用...

    android开发:俄罗斯方块 源代码

    这些功能通常在Java代码中实现,与视图层分离,遵循Model-View-Controller(MVC)架构。 5. **事件处理**: - 方块的移动和旋转由用户输入(例如,屏幕触摸或按键)触发。开发者需要监听这些事件,并相应地更新游戏...

    Android软件架构设计

    8. **Android架构组件**:包括Room数据库、WorkManager、Lifecycle组件等,它们是Google官方推荐的架构组件,可以简化很多开发任务,提升应用质量。 9. **单元测试与集成测试**:如何编写测试用例,以及使用JUnit、...

    mvc架构的学生宿舍管理系统+sqlite数据库

    **mvc架构的学生宿舍管理系统+sqlite数据库** 在信息技术领域,MVC(Model-View-Controller)架构是一种广泛应用的设计模式,尤其在Web开发中。这个模式将应用程序分为三个主要部分:模型(Model)、视图(View)和...

    基于Cocos2d-x框架,并使用MVC架构的棋类游戏

    基于Cocos2d-x框架,并使用MVC架构的一个棋类游戏。 Pure MVC架构,构建可维护的Cocos2d-x游戏。 ###使用的框架 Cocos2d-x2.2.3 ###apk下载地址 应用宝 360手机助手 ###相关博客 架构篇:...

    基于Android的网上点餐系统

    4. MVC架构:MVC是一种软件设计模式,将模型(Model)、视图(View)和控制器(Controller)分离,提高代码的可维护性和可扩展性。 - 模型(Model):负责数据的处理和存储,与数据库交互。 - 视图(View):显示...

    基于Android平台的植物生长柜智能监控软件设计.pdf

    2. 软件架构:基于Java和Android编程技术对软件进行设计,采用MVC架构模式,包括Model、View和Controller三个层次,实现软件的高效、灵活和可维护性。 智能监控技术: 1. 环境参数监控:实时监控生长柜内的环境参数...

    mvc-whackamole:新泽西州MVC约会检查器

    `mvc-whackamole` 是一个基于Kotlin编程语言构建的项目,其名称暗示了它可能与经典的打鼹鼠游戏有关,但在这个上下文中,它更像是一个针对模型-视图-控制器(MVC)架构的测试或演示应用。在IT领域,MVC是一种广泛...

Global site tag (gtag.js) - Google Analytics