Android的系统设计,与别的智能手机操作系统有很大区别,甚至在以往的任何操作系统里,很难找到像Android这样进行全面地系统级创新的操作系统。从创新层面上来说,Android编程上的思想和支持这种应用程序运行环境的系统,这种理念本身就是一种大胆的创新。

整个Android系统,实际主要目的,就是打造一个功能共享的世界。

功能共享最重要的交互,于是Android创造出一种Intent和IntentFilter配合的低耦合的交互模型,Intent只是一种描述要完成什么工作跨进程的结构体,而最终如何解析这些Intent并完成其响应,是由IntentFilter来进行换算,最终是由用户来决定如何完成。

而在Intent这种超级交互消息之上,Android进一步把应用程序的实现逻辑拆分成多种特殊的实现:

Þ     Activity:带显示与交互能力的部分

Þ     Service:不带显示与交互能力的部分

Þ     Content Provider:在功能交互之外,提供数据交互能力的部分

Þ     Broadcast Receiver:用来处理广播交互的部分

这四种功能上的拆分,也体现了Android设计者在设计上抽象思绪能力,即便是随着Android迅猛发展,目前已经到了4.1这么功能丰富、用户体验良好的状态,我们编程也还是与这四种功能组件打交道,可以满足我们任何的编程时所需要的任何行为。

而这四种基本组件组成部分,使Android应用程序反倒成了一个“空壳子”。静态上看,应用程序只是一种包装这些功能的容器;从运行态来看,所谓的应用程序,也只是承载某些功能的进程。

Android世界里的共享

 作为一个智能手机操作系统,其用户可能在功能上有各种各样的功能组合。比如最简单的打电话,则后续动作会有保存联系人,同时需要给联系人拍照做来电大头贴。又比如需要来了个短信通信用户到某个地方干什么事情,这时,用户需要打开地图,搜索一下地址,然后还有可能需要定位到那个位置。

用户在主界面里点击相应功能的应用程序之后,就可能有非常多的功能性的组合,因为用户的想法是不可预估的。我们当然也可以限制用户当前菜单下可以干什么事情,但这样就失去了智能系统的意义。

我们也可以假设用户都会按一个“Home”键回到主界面,这时原来的执行的程序就会被锁定当前状态,用户重新打开另外一个应用程序,操作完再按“Home”键可以退回到原来的应用程序。通过这种“应用程序”到“Home”到“应用程序”的循环,我们也可以达到我们想要达到的目的。但这时,出于交互性的考虑,我们也还是需要有限地提供一些交互手段,比如“短信”应用程序里包含地址信息,一点击可以直接打开“地图”进行后续操作,但这些有限交互是可以在系统设计阶段被固化。这时,我们是不是就得到了我们想要的能够应付用户任何操作组合的系统?是的,恭喜您,您得到了iPhone的设计思路。但此时的用户交互流程则被改变成这个样子:

这种解决问题的办法也不是不可以,但需要很固化的设计,应用程序的行为比较受限。虽然通过横扫全世界的iPhone证明了这样的设计可能是比较合乎用户体验之道的(不容易出错),但这样的解决思路从系统设计角度来看,并不是很灵活。另外一个麻烦是必须要有苹果级设计功底的“Home”键,山寨货则用不了多久就会因为键盘失灵而失效。当然即使苹果级设计,iPhone里的“Home”键还是会失效,于是又不得不在屏幕上加上触摸的Home手势。

作为开源系统的Android,当然不可能基于iOS的交互思路来解决问题,这种交互时多了一步不停要回到主界面这一步。在Android的设计里,最重要的是能够解决一个应用程序之间进行交互的问题,然后可以实现我们想要在Android系统里完成某种操作时,可以享受从一路顺畅完成的快感。

Android的解决之道,则是将传统意义上的应用程序,细化成一个个完成某项功能的部分,这种功能部分,在Android世界里被称为Activity。Activity都应该被设计成可以独立地被执行以解决某个问题,当它完成或是用户选择退出执行时,又会自动跳回到调用这一Activity的界面,当然这时跳回的位置肯定是另外一个Activity。当然,在一个Android系统里有可能存在无限多的Activity,在他们进行跳转切换时,我们就需要一种很灵活的消息传输机制(因为我们必须兼容系统里所有可能的互相调用的情况)。而且这种传输机制还必须能够跨进程,不然,我们所有的涉及Activity互相调用部分都必须在同一进程里完成。于是,Android系统里又有了Intent,用于解决交互通信。

这样的编程模型也需要有一定前提,那就是我们Application概念必须被弱化,我们不能有main函数入口(如果系统执行依赖main作入口,则不能实现Activity之间互相调用了,所有的Activity执行之前,必须先通过main入口来初始化环境)。出于这样的设计,所以Application必须只是一个容器,将各种不同的Activity实现包装起来加载到系统里。

当然,将功能拆分成一个个的单一功能界面之后,我们需要有种机制可以将用户一路点击过去历史记录下来,当用户处理完时,可以退回到他们之前操作过的界面,这次就可以由多个应用程序组合出像是在用同一个应用程序的效果。有了Activity,有了Activity之间起到调用作用的Intent,这时所有界面间操作变得有点像是函数调用一样,于是我们可以找函数调用时的基本数据结构—栈来帮忙,发生调用时,需要退出的Activity及其状态压栈,当从调用退出时则进行栈的弹出操作,这时我们的Activity管理就演变成如下图所示的简单栈管理。

有了这样的概念,于是我们响应用户点击操作的问题便迎刃而解,我们在设计应用程序时,不再是设计一个复杂的功能实现,而是实现一组完成单项功能的实现,也就是Activity。然后这些Activity,只会通过用户点击来驱动它们之间是如何进行交互的。比如,我们前面看到的地图、搜索、定位三个功能,虽然它都会被包装到同一个地图的应用程序里,但在实现上会是地图、搜索、定位三个不同的Activity。

因为现在我们的界面上的互相调用,已经变成了一种函数式的调用,这样,整个手机上的功能都被切分成各个单一的小功能,而真正要在Android系统上完整地实现某复杂个操作,则会提交由用户的点击来组合生成。这样的复杂功能,则已经不是一个编程上的概念了,在Android系统里,这种需要完成什么事情的操作被抽象成一个虚拟的概念Task。比如我们前面提到的打电话加拍大头贴的操作组合,就构成一个Task,这一Task需要由Launcher.apk,Contacts.apk,Gallery.apk来协同完成。

如果我们有两个能够提供同样功能的Activity,这种执行模式的灵活性表现得会更加明显。比如中间打电话的功能,我们系统里有三个Activit(CallScreen, SipPhone, Dialer)都可以完成电话呼叫的功能,这时执行上的路径则会有三种可能性,会在进行跳转时弹出圣对话框由用户来选择:

通过Activity的这种可以动态被用户选择的特点,当用户对某一功能不满意时,完全就有可能通过下载另一个能实现这种功能的应用程序进行替换,甚至可以自己写一个。事实上,Android系统里除了系统状态条与锁屏界面之后,没有任何的不可被替换的功能,这也是Android设备总是会长得千奇百怪的原因之一。

到这时,我们就可以看到Activity之所以会不被称为Window的原因,它也是单个界面或是MVC里的Controller实现部分那么简单,Activity这个名字代表的是某种单一交互功能上的实现。这种功能的实现将在系统里通过Intent串接起来,构成了一个在功能上具备极大可拓展性的系统。基于这样的特点,Android也就被称作是“无边界”系统,因为它在功能上延展不再受限于系统的能力,而只受限于智商与创意。

这就是Android世界里的功能共享。

在这种功能共享模型之下,可能还是会有一些微调的需求:

1. 我们有一些情况下不宜使用这种栈式Activity管理,比如我们写一个需要注册的应用程序,注册完开始使用,然后再按退出,我们又会一步步退回到注册填个人信息的界面,而不合理地完全退出。这样可能不合适。这时,我们可以使用Intent的Flag参数, 加上Activity的Affinity属性进行组合控制。

2. 如果不停地跳出对话框让用户选,用户会崩溃掉。当然,用户可以在选择时点选一个“始终”的默认选择,这时下次就会使用默认的Activity处理某种操作。但还是有可能会不合理地使用跨.apk文件里使用Activity,造成性能上的开销,这时,我们也可以在执行下次Activity执行操作时进行强制性地指定。

当然,我们通过Activity这种概念还需要另外一个前提,这就是Android会有别于传统操作系统的前提,那就是单窗口。想像一下,在多窗口环境下,我们的栈式管理Activity在进行跳转和返回时将会构成多大的灾难啊。好在使用电容屏的设备,单窗口是天生的需求。由于手指触摸的精度非常低,无法点准过小的按钮,比如窗口上的关闭按钮,如果将这些按钮放大,又造成了屏幕显示空间上的浪费。iPhone带来的“后PC时代”革命,最重要的一点就是使用“返祖”式的单窗口显示。

这种怪异的操作方式,实际上在我们生活中也有类似的例子,就比如说我们的动态网页。动态网页,特别是HTML5构建的网络应用程序,其操作模式,就是可以在不同的链接里不断地点击下去,如果不是弹出新窗口,我们始终还可以退回到发起这一连串点击的起始页面。Android应用程序,XML构成的UI语言的作用跟Html页面类似,而Java构建的Activity就相当与网页交互中使用的JavaScript,有了这样的相似性,Android编程环境可以说是最接近HTML5的一种编程环境了,但可惜不能像HTML5那样可以跨平台。

我们解析了能完成单一功能的Activty,这时还需要了解Intent,就像是我们了解过了函数实现原理,我们还需要掌握函数之间的参数传递。当然,一般在介绍编程的思路里,会结合起来说明,或是先说明参数传递。但Android环境里有点特殊性,一是Intent是一种能够实现跨进程调用的信息传递机制,二是Intent在消息传递上又很灵活,有一定的动态性。Intent不光服务于Activity之间的调用,还会用于一些不直接与界面打交道的逻辑实现部分,比如我们后面将提到的Service,Broadcast Receiver,以及 Notification。

1.3   Intent与Intent Filter

 Intent,英文原意就是要“干什么”的意思,之所以取这个名字,也是因为在Android系统里,Intent所起到的作用就是用来指明下一步具体是做什么,具体是不是执行,由谁来执行,则会由根据当前的系统状态(能不能解析这个Intent请求)来决定。这不只是简简单单地发个消息而已,而是一种更安全的、更加松散的消息机制

在一个Intent消息对象里,共有六个成员(并不都是必须赋值的,只要一个Intent对象能够被解析,就会得以执行,否则就会会被舍弃):

成员

类型

说明

示例

ComponentName

String

用于定义谁将处理这一Intent。它由一个Activity的具体实现的全名(加上包名)来指定

org.lianlab.hello.HelloActivity

Action

String

用于定义这一动作是做什么,可以被拓展自定义类型

ACTION_CALL

开始通话.

ACTION_EDIT                     进行编辑.

Data

String

用一个URI来指定Intent的操作对象,因为URI一般会包含种类信息,于是这个值也可能被用作MIME设别。

“content://contacts/people/1”

指定联系列表中的第一个

Category

String

用来进一步明确什么样的可执行实体将处理这一Intent。是可选项,也可多选。

CATEGORY_HOME

主界面应用程序

CATEGORY_LAUNCHER

可在主界面里被点击

Type

String

用来指定特定的MIME类型

"video/*"

视频

Extra

Bundle

用来传递额外的数据传递,前面我们也介绍了Bundle是一种key:value配对的字典类型,于是Extra里可以转递复杂的数据

putExtra("sms_body", "some text");

发短信时指定内容

Flags

int

预定义一系列用来控制Intent行为的属性值

在这个成员变量里,最能体现灵活性的就是Component,如果指定了这个值,则我们在通过startActivity()方法来发送Intent时,就会自动启动Component指定的Activity。如果没有指定,则会由系统来选择一个能够处理这一Intent的Activity来执行,这时就引入了Intent Filter的概念。

Intent Filter在Android里是一种类似于Windows里的注册表一样的东西,虽然我们也可以通过编程来进行Intent Filter的控制,但一般情况下,我们只在AndroidManifest.xml文件里进行定义,对它一个<intent-filter>的标签进行指定。应用程序在安装过程中,它的AndroidManifest.xml会被系统扫描并汇总到系统环境里,这时<intent-filter>也会被导入。当Activity发送出来的Intent,没有指定Component时,系统就会通过<intent-filter>找到合适的处理对象,如果只有一个或是用户设置了默认项,则启动这个功能部件来完成任务;如果有多个<intent-filter>匹配同时用户又没有指定默认项,则会弹出对话框让用户选择。当然,默认项也会随着系统里新增了同一Intent匹配项而失效,用户也可以通过“设置”——> “应用程序”来取消默认值。

在AndroidManifest.xml里面定义<intent-filter>很简单,就是通过指定Intent对象的Action,Data,Type,和Category这四个成员变量来指定。比如:

<activity android:name=".PlayerActivity"android:label="@string/app_name"

android:configChanges="orientation" >

<intent-filter>

<actionandroid:name="android.intent.action.MAIN" />

<categoryandroid:name="android.intent.category.LAUNCHER" />

</intent-filter>

<intent-filter>

<actionandroid:name="android.intent.action.VIEW" />

<categoryandroid:name="android.intent.category.DEFAULT" />

<dataandroid:scheme="file" />

</intent-filter>

...

</activity>

当我们的某个Activity,发送了一个Intent,其Action是”android.intent.action.VIEW”,data又是以file:///开始的URI指定的内容(也就是文件类型),这时上面例子里的PlayerActivity就会成为播放时的候选项。

我们可以继续修改我们前面的HelloWorld的例子,我们新建一个Intent,将Action设成”android.intent.action.VIEW”(可以通过Intent.ACTION_VIEW这个预变量来转义),data使用某个文件“file:///sd-ext/Movies/test.mp4”,这时就会匹配到我们上面的<intent-filter>定义:

public void onClick(Viewv) {

Intent request = new Intent(Intent.ACTION_VIEW);

request.putData(“file:///sd-ext/Movies/test.mp4”);

startActivity(request);

finish();

}

当然,我们并不一定需要代码来进行这样的测试,我们也可以使用设备上的am命令来完成。要完成与上面的点击操作一样的功能,也可以通过adb来执行这条命令:

$adb shell am start –aandroid.intent.action.VIEW –d file:///sd-ext/Movies/test.mp4

在我们具体写代码过程中,我们可以根据需求来定义我们所需要的<intent-filter>,可以将过滤规则写得很细,也可以写得很粗,让我们的Activity有更多地被执行到的机会。在这些规则里,可能最重要的规则,就是我们前面也示范过的:

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

这两行<intent-filter>规则,将使用我们的应用程序可以被主界面所收集,使用户可以在主界面里点击运行这个Activity。

有了Activity,我们就可以构建基于功能共享而实现所谓应用程序,而有了Intent,使用我们在共享时所受的限制可以变得更小。而且,由于是简单化的单窗口模式,再加上一些在性能设计上的精细设计,于是我们的Android系统便有了良好的人机交互体验。

1.4   编程角度的应用程序

光有Activity与Intent,并不是Android应用程序编程时的全部。应用程序除了有人机交互界面之外,有可能还需要使用到一些不直接与人交互而在后台长期运行操作;我们还需要有某种机制,能够提供数据共享,并且在数据共享时能使用统一的访问机制;最后,我们可能还需要处理以广播方式发送的消息,广播与Intent不同之处在于一到多的方式传播,同时消息只在某个时间段内有效。

事实上,我们的Android编程,是被包装成四个不同的类型,同时通过Intent将这些类包装起来,以解决我们上面提到的,在编写图形应用程序里可能遇到的问题的:

l   Intent: 全局性的、松散的消息传递机制

l   Activity:带图形界面的,可以与用户进行交互的逻辑实现。

l   Service:  不带图形界面的,不直接与用户交互的代码。一般会被用于在后台做些什么事情,比如监听网络、下载、拷贝文件等。(这可能是一般的Android工程师觉得没有必要实现的部分,笔者在讲解Android应用程序相关的课程时,就常有问及,Actiivity会进入到后台,然后有可能被杀死掉,这样的问题如何解决?实际上Activity只解决交互,需要在后台时还需要继续执行的代码,需要用Service来实现。只自己可访问的Service,可以使用简单的本地Service,而需要提供给别的进程来访问的情况下,我们需要通过AIDL编写Remote Service。Service的实现,我们在后台再详细说明,因为Android系统的核心Framework,本身就是由大量这样的Remove Service来组成的。)

l   Content Provider:  提供数据层共享,以CRUD(Create Read Update Delete)方式进行数据访问来统一化数据读写指口一种模型。如果使用了Sqlite做后台的数据支持(实际上相当于应用程序MVC模型里的Model部分被Sqlite延展开来),我们可以通过ContentProvider来各系统内的其他部分提供数据源,当然系统本身也给我们提供了大量这样的ContentProvider,像Setting里的设置的值、联系列表、多媒体文件扫描结果等。(这种数据层上的共享机制,也是应用程序编程上需要加强的技巧之一,因为有了Content Provider,我们则有可能使用Cursor式进行访问,这时我们就可以使用CursorAdapter来自动化地处理数据源。)

l   Broadcast Receiver:处理广播类消息的监听器,从而可以给应用程序提供广播式的信息处理,同时也提供系统消息的广播式分发。比如,Android会将一些系统事件广播出来,像电话振铃、电量状态变化、网络状态变化等,我们需要能够处理这样的事件,电话振铃时我们写的多媒体播放器就应该静音、电量过低时需要保存状态等。对我们应用程序而言,广播方式也是一种很好的通信机制,我们不需要写一个循环通知所有的Activity、Service我们状态发生了改变,而只需要发一个广播,则所有关心这一事件的部分都可以收到。

这些功能实体,都是我们通过Java代码根据不同的基类(Service、 ContentProvider、BroadcastReceiver)派生出相应的子类,再加以具体实现。这样的功能实现不需要自己去创建这个对象,会通过AndroidManifest.xml里的定义,由系统按执行的需要自动创建。有了这些不同的功能实体,我们最后的应用程序,实际上就成了这个样子:

而我们的所有代码,从运行态行为来看,都不再是直接的互相调用关系,而是全部都通过Intent来进行彼此之间的交互。而这样的交互,也不再是传统式地自己陪自己玩,而是会进入到一个大的功能集合体时,提供功能给系统内其他应用程序所使用,而自己也会调用其他部分的代码。

当然,随着Android版本变更,Android系统又新增了一些新的概念,比如针对多窗口功能的Fragment、针对于使用异步机制操作Cursor的Loader等。但万变不离其宗,这些Android的核心原理则一直如此。

总结一下Android编程思想,我们就会知道其实在Android整个生态环境最重要的元素,应用程序,反倒是Android编程上最不重要的。所谓的Android编程,就是要通过编写一个个的Activity、Service、Content Provider、Broadcast Receiver实现,通过功能上的共享与数据上的共享进一步丰富用户可用的功能。当用户可以通过Market或自己下载取得我们封装到.apk文件里的实现之后,这些功能就会无缝地被Intent整合到了一起。

从Android这个编程原则来看,我们可以看到,如果我们使用某种Java执行环境,将Android应用程序的这些组成部分的支持都加入进来,我们也可以得到一个Android兼容的环境。的确如此,已经有人在打这方面的主意,有将Android环境移植到Windows环境里的BlueStack商业解决方案,也有号称兼容Android应用程序,通过一个类似于JAVA ME的虚拟机环境来支持Android应用程序的BlackBerryOS。但我们可以再来看看Android的真正的支持环境,我们可以看到,Android有其独特的特性,也不是那么容易被取代。

转自:http://blog.csdn.net/21cnbao/article/details/7917652

Android背后的设计思想——功能共享机制的更多相关文章

  1. Android应用程序开发以及背后的设计思想深度剖析

    1 http://www.uml.org.cn/mobiledev/201211063.asp 2 ...

  2. 王家林的81门一站式云计算分布式大数据&移动互联网解决方案课程第14门课程:Android软硬整合设计与框架揭秘: HAL&Framework &Native Service &App&HTML5架构设计与实战开发

    掌握Android从底层开发到框架整合技术到上层App开发及HTML5的全部技术: 一次彻底的Android架构.思想和实战技术的洗礼: 彻底掌握Andorid HAL.Android Runtime ...

  3. 《深入理解Android内核设计思想》

    <深入理解Android内核设计思想> 基本信息 作者: 林学森 出版社:人民邮电出版社 ISBN:9787115348418 上架时间:2014-4-25 出版日期:2014 年5月 开 ...

  4. 讨论Android开发中的MVC设计思想

    最近闲着没事,总是想想做点什么.在时间空余之时给大家说说MVC设计思想在Android开发中的运用吧! MVC设计思想在Android开发中一直都是一套比较好的设计思想.很多APP的设计都是使用这套方 ...

  5. javascript继承机制的设计思想(ryf)

    我一直很难理解Javascript语言的继承机制. 它没有"子类"和"父类"的概念,也没有"类"(class)和"实例" ...

  6. javascript 继承机制设计思想

    作者: 阮一峰 原文链接:http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_java ...

  7. 转:Javascript继承机制的设计思想

    我一直很难理解Javascript语言的继承机制. 它没有"子类"和"父类"的概念,也没有"类"(class)和"实例" ...

  8. Javascript继承机制的设计思想

    转自:http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.htm ...

  9. Javascript prototype 及 继承机制的设计思想

    我一直很难理解Javascript语言的继承机制. 它没有"子类"和"父类"的概念,也没有"类"(class)和"实例" ...

随机推荐

  1. Tinyos Makerules解读

    Makerules 文件解读 位于/opt/tinyos-2.1.2/support/make #-*-Makefile-*- vim:syntax=make #$Id: Makerules,v 1. ...

  2. 初识PDO数据库抽象层

    目录: 00x1 php中的pdo是什么? 00x2 pdo创建一个PDO对象 00x1 php中的pdo是什么? 就是操作数据库的方法,pdo就是把操作数据库的函数封装成一个pdo类,其间做了安全验 ...

  3. webgote的例子(6)SQL注入(盲注)

    SQL Injection - Blind (WS/SOAP) 本期演示的是盲注的手法.有些网站在与数据库交互的地方进行了很好的修饰,将报错的语句进行修改,即使你找到了注入点也无法下手拿数据,这个时候 ...

  4. 64_o2

    openrdf-sesame-queryrender-2.8.10-2.fc26.noarch..> 11-Feb-2017 18:38 52014 openrdf-sesame-queryre ...

  5. HOJ 1108

    题目链接:HOJ-1108 题意为给定N和M,找出最小的K,使得K个N组成的数能被M整除.比如对于n=2,m=11,则k=2. 思路是抽屉原理,K个N组成的数modM的值最多只有M个. 具体看代码: ...

  6. linux系统查找具体进程

    ps -ef | grep '查找内容' eg:ps -ef | grep '测试USB设备穿透'

  7. What does “=>” mean in import in scala?(转自StackOverflow问答)

      As others have mentioned, it's an import rename. There is however one further feature that proves ...

  8. mysql delete 注意

    mysql中You can't specify target table <tbl> for update in FROM clause错误的意思是说,不能先select出同一表中的某些值 ...

  9. linux抓包工具tcpdump基本使用

    tcpdump 是一款灵活.功能强大的抓包工具,能有效地帮助排查网络故障问题. tcpdump 是一个命令行实用工具,允许你抓取和分析经过系统的流量数据包.它通常被用作于网络故障分析工具以及安全工具. ...

  10. WordPress 用户管理插件 WP User Manager

    WP User Manager 是一个较新的用户管理插件,可以在前端实现 用户注册.登录.找回密码.修改个人资料.修改密码等功能,如果你在找这方面的插件,WP User Manager 应该是一个不错 ...