Android应用程序中三个主要的组件——Activity, Service, Broadcast receiver——都是由Intent中传递过来的消息激活的,
Intent消息传递是这样一个工具,它把相同或者不同应用的组件后期运行绑定起来。Intent对象本身是一个被动的数据结构,它用来承载一个抽象描述,这个抽象描述是关于将要执行操作基本属性。——在 broadcast的情况下,这个描述是已经发生,并且正在被announced的东西[这句要继续深刻理解一下]。下面有很多相互独立机制,为每种类型的组件传递Intent:
1. 一个Intent 对象传给Context.startActivity()或者Activity.startActivityForResult() 去启动一个Activity或者利用已存在的Activity执行某项操作.
2.一个Intent 对象传给Content.startService(),初始化一个Service或者为一个正在进行的Service传递新的指示。同样地,一个intent对象传给Context.bindService(),用来建立主动调用的组件和目标Service之间的连接。如果Service尚未运行,则可选择性的启动。
3.Intent对象传递给任意broadcast方法,如Context.sendBroadcast(), Context.sendOrderBroadCast(),或者 Context.sendStickyBroadCast(),这种方式的传递,会将Intent传递给所有感兴趣的Broadcastreceiver. 许多broadcast起源于系统代码。
在上述各情况下,Android系统要找到需要的Activity, Service, 或者是broadcast receiver的集合,从而响应intent消息,并且在需要的情况下初始化这些组件。这些Intent消息没有交集:broadcast intent只送往broadcast receiver, 不会送往activity或者service. 一个Intent传给startActivity(), 只会送往activity, 不会送给一个service或者broadcast receiver。
本文档开篇介绍Intent对象. 然后介绍Android中intent和组件的映射规则——Android如何解决那个组件应该接收一个Intent消息。由于Intent没有明显的指明要调用哪个Component, 这个过程包含测试Intent对象和关联潜在目标组件的Intent filter.
Intent Objects:
一个Intent对象是信息的集合. 它包含需要接收这个Intent的组件感兴趣的消息(如产生什么样的动作,以及这个动作的数据对象),还包含Android系统需要的附加信息(需要处理这个Intent的组件分类,以及如何启动目标Activity), 原则上,一个Intent包含如下信息:
Component Name: 处理这个Intent的组件名称。 这是一个ComponentName属性对象——目标组件的类全名(如"com.example.project.app.FreneticActivity")以及在manifest文件中,设置组件Package位置的信息(如"com.example.project"). 组件名称的package部分和package的名称不一定要完全匹配。
组件名称是可选的, 如果设置了这个组件名称, Intent对象会发送到指定组件的实例。如果没有设置,Android利用其它的信息定位和是的组件目标——查看后续章节的 Intent Resolution.
组件的名称通过setComponent(), setClass(), setClassName()设置,通过getComponent读取。
Action:它是将要执行动作的字符串名称——对于Broadcast Intent, 发生的Action不断上报。 Intent类定义了许多Action 常量,包括下面的:
这里是一个表格.后面去移植.
可以查看API,可以发现预先定义的一些Action常量. 其它的一些Actions定义在Android api的其他地方,可以在自己的应用中,自定义可以被组件激活的Action字符串。这些自定义的需要将应用程序的包名作为前缀,如"com.example.project.SHOW_COLOR".
Action很大程度上决定了Intent的其他部分的组织结构——特别是Intent的data和extras属性——如同一个方法名决定了传入的参数和返回值。 鉴于上述原因,明智的做法是:尽可能的使用特别的Action名称,并且和Intent的其他属性紧紧绑定。换句话说,为你的组件用到的Intent对象定义一个完整的约定,而不是每个Action无规则的各自定义。
Intent中Action的设置是用setAction(),读取采用getAction();
Data:指的是操作的数据以及这个数据的MIME类型。不同的Action对应不同的数据说明。 比如,如果Intent的的action属性为
ACTION_EDIT, 则为了显示并编辑,Intent的data属性需要包含文档的URI; 如果Intent的ation属性为ACTION_CALL, 则Intent的data属
性需要为tel:包含拨打电话的URI. 同样地,如果Intent的action域为ACTION_VIEW并且Intent的data域为http:URI, 接收Intent的
Activity将被调用并且下载和现实这个URI表示的资源。
当匹配一个Intent到一个组件,并且能够处理数据时,除了了解URI外,获知这个数据的MIME类型是很重要的。比如,一个能打开图
像文件的组件,不应该用来播放音频文件。
在很多情况下,可以从URI推断出data的类型——特别是content:URIs,它表示数据位于设备中,并且有一个content provider控制(查看
separate discussion on consten providers). 但是,数据类型同样可以显示的在Intent对象找哦你个设置. setData()方法仅仅设置
数据的URI, setType()方法仅仅设置数据的MIMIE类型,setDataAndType()同时设定URI和MIME类型。通过getData()取URI,通过
getType()取MIME类型
Category:指的是一个包含附加信息的String,附加信息是关于应该处理Intent的组件类型。任意数目的category描述都可以防止到一
个Intent 对象中. 跟Action的做法类似,intent类也定义了多个catagory常量,如:
这是一个表....稍后去迁移.
查API文档中的Intent, 可以获得所有Category列表.
addCategory()方法将一个Category设置到Intent对象中。removeCategory() 删除一个之前设置的Category. getCategoryies()方法得
到当前Intent对象中所有的categories.
Extras: 指的是一个Key-Value对,用来给目标组件传递附加信息,正如某些Action和特定的数据URI一样的对应关系,存在某些特殊
的extras, 比如: 一个ACTION_TIMEZONE_CHANGED intent有一个"time-zone" extra定义了一个新时区,ACTION_HEADSET_PLUG 有一
个"state" extra 表明耳机是否插上还是拔出,而"name" extra则表明了耳机的类型. 如果你需要实现一个SHOW_COLOR action, color
的值需要在extra的key-value对中被设置好。
Intent对象中有一系列的putXXX()方法,可以插入不同种类的extra 数据,同样有一系列getXXX()方法用来读取数据. 这些方法对
Bundle对象而言都是平行的. 事实上,这些extras可以用Bundle的形式,通过putExtras()和getExtras()方法插入和存取。
Flags:指的是各种各样的标志。大多数用来告知Android系统如何启动一个Activity(如Activity属于哪个task),如何在启动之后处理
这个Activity(如Activity是否在最近的Activities集合中). 所有的这些标志都定义在Intent类中。
所有总结:
Android系统和它所带的应用程序利用Intent对象, 发送系统产生的broadcast并且激活系统定义的component. 想谅解如何结构化一个Intent对象,并激活系统组件,参考:list of intents.
1. Intent和Activity关联关系
通过传递的Intent来决定Activity的启动。Intent和Activity的关系是一对多,比如,传递一个Type为mp3的Intent,可能会让你选
择多个Activity——Android自带的音乐播放或者你自己选择的播放器等。Activity在AndroidManifest.xml中预先定义了与哪个Intent相关,这样方便后面传递Intent,即可以激活对应的Activity。
2. Intent 和 Activity的全局性.
无疑可以自定义Activity的Intent-Filter,这样每个应用(可以称为进程吗?)自己搞定自己的事情就OK, 但为了应用之间通信
的方便, 比如,系统已经有了一个音乐播放器Activity,何必自己再实现? 因此只需要构造能让该播放器Activity识别的Intent就
行了。 因此通过一些全局的变量塑造Intent,就实现了Activity的复用。方式是利用Intent中的全局常量,如
Intent.ACTION_MAIN,Intent.CATEGORY_SAMPLE_CODE等,定义到Acitity中,这样大家都能使用这样的Activity了。
简单示例:
private void testPM()
{
Intent mainIntent = new Intent("com.ostrichmyself.xxx", null);
PackageManager pm = getPackageManager();
List<ResolveInfo> list = pm.queryIntentActivities(mainIntent, 0);
for (int i = 0; i < list.size(); i++)
{
ResolveInfo info = list.get(i);
Log.i("Test Intent---", info.activityInfo.name);
}
}
AndroidManifest.xml中定义为:
<activity android:name=".MyActivity" android:label="myActivity">
<intent-filter>
<action android:name="com.ostrichmyself.xxx" />
</intent-filter>
</activity>
注意TestPM()这段代码可以放在和MyActivity所在的application中,也可以放在其它地方。 原因是Activity是全局的。
android的Application Components四个部分组成:
1 )Activities
2) Services
3 )Broadcast receivers
4 )Content provider
1 Activities
一个activity是一个可视化的用户界面,可以上面添加layout view button等。一个android由多个activity组成,各个activity之间相互独立。每一个activity实现都是Activity这个基类的子类。
2 Services
service程序没有用户界面,但是可以长时间在后台运行,比如可以音乐的后台播放功能。
3 Broadcast receivers
Broadcast receivers 是一个用来接收响应外部事件消息(broadcast announcements 我翻译不好)的组件。Broadcast receivers没有用户界面,用户看不到它,但是它可以通过一个activity、震动、响铃、状态栏等提示用户有外部消息。所有的事件接收响应继承自BroadcastReceiver基类。
4 Content provider
Content provider提供一组标准的方法接口,供其他应用程序调用。例如,应用程序A通过实现ContentProvider的接口将自己数据暴露出去,外部应用程序可以通过ContentProvider接口对A的数据进行操作(增删改查等)。
android中数据是私有的不公开的(文件存储数据、SQLite数据库数据库、或者其他数据),通过Content provider可以将私有数据暴露出去。
Intent 和 Intent Filter(参考android入门开发与实践一书)
一个Intent就是一次就将要执行操作的抽象描述(抽象啊)。Intent最重要的两个部分是Action(Intent的动作)和Data(Action对应的数据)。Action的类型有MAIN(Activity的门户)、VIEW、EDIT等等,Action对应的Data以URI形式描述。例如要查看某人联系方式,需要创建一个Action类型为view的Intent,以及一个表示此人的URI。Intent也可以进行Activity之间的跳转。
Intent Filter用于描述一个Activity(或Intent Receiver)能操作哪些Intent。例如一个Activity要显示某人联系方式,需要声明一个Intent Filter,Intent Filter需要知道怎么处理view动作和表示某人的URI,Intent Filter一般在AndroidManifest.xml中定义。 Intent in = new Intent(A.class, B.class); startActivity(in);
意图解析Intent Resolution
意图可以被分成两组:
显式意图 通过名字指明目标组件(这个组件名字字段component name field, 前面提到过, 有一个数值集)。既然组件名称通常不为其他应用程序的开发者所了解,显式意图典型的被用作应用程序的内部消息
-例如一个活动启动一个附属服务或姊妹活动。
隐式意图 不命名目标组件(组件名称字段为空)。隐式意图经常用来激活其他应用程序的组件。
Android递交一个显式的意图给一个指定目标类的实例。意图对象中的组件名称唯一的确定哪个组件应该获取这个意图。隐式意图需要一个不同的策略。在没有指定目标的情况下,Android系统必须找到最合适的组件来处理这个意图-单个活动或者服务来执行这个请求动作或者一系列的广播接收器来应对广播通告。
这是通过比较意图对象的内容和意图过滤器,有可能接收意图的组件相关结构。过滤器公布一个组件具备的能力以及限定它能处理的意图。他们使组件接收该公布类型的隐式意图成为可能。如果一个组件没有任何的意图过滤器,那它只能接收显式意图。一个带过滤器的组件可以同时接收显式和隐式意图。
当一个意图对象被一个意图过滤器测试时,只有三个方面会被参考到:
动作
数据(URI以及数据类型)
类别
附加信息和标志并不参与解析哪个组件接收一个意图。
意图过滤器Intent filters
为了通知系统它们可以处理哪些意图,活动、服务和广播接收器可以有一个或多个意图过滤器。每个过滤器描述组件的一个能力,一系列组件想要接收的意图。它实际上按照一个期望的类型来进行意图滤入,同时滤出不想要的意图-但是只有不想要的隐式意图会被滤出(那些没有命名目标的对象类)。一个显式意图总能够被递交给它的目标,而无论它包含什么。这种情况下过滤器不起作用。但是一个显式意图仅当它能通过组件的一个过滤器时才可以被递交到这个组件。
组件为它能做的每项工作,每个呈现给用户的不同方面分有不同的过滤器。比如,范例记事本应用程序中的主要活动有三个过滤器-一个是空白板,另一个是用户可以查看、编辑、或选择的一个指定的记事目录,第三是在没有初始目录说明的情况下查找一个特定的记录。一个意图过滤器是IntentFilter类的一个实例。但是,由于Android系统在启动一个组件前必须知道这个组件的能力,意图过滤器通常不会用Java代码来设置,而是在应用程序清单文件(AndroidManifest.xml)中设置<intent-filter>元素。(有一个例外,通过调用Context.registerReceiver() 来注册的广播接收器的过滤器;它们是作为意图过滤器对象而被直接创建的。
过滤器与安全Filters and security
不能信赖一个意图过滤器的安全性。当它打开一个组件来接收某些特定类型的隐式意图,它并不能阻止以这个组件为目标的显式意图。即使过滤器对组件要处理的意图限制某些动作和数据源,总有人能把一个显式意图和一个不同的动作及数据源组合在一起,然后命名该组件为目标。
一个过滤器和意图对象有同样的动作、数据以及类别字段。一个隐式意图在过滤器的所有三个方面都被测试。为了递交到拥有这个过滤器的组件,它必须通过所有这三项测试。即便只有一个不通过,Android系统都不会把它递交给这个组件-至少以那个过滤器的标准而言。不过,由于一个组件可以包含多个意图过滤器,一个不能通过其中一个组件过滤器的意图可能在另外的过滤器上获得通过。
三个测试详细描述如下:
动作测试Action test
清单文件中的意图过滤器元素里列举了动作元素,比如:
<intent-filter . . . >
<action android:name="com.example.project.SHOW_CURRENT" />
<action android:name="com.example.project.SHOW_RECENT" />
<action android:name="com.example.project.SHOW_PENDING" />
. . .
</intent-filter>
如同例子所示,一个意图对象只对单个动作命名,而一个过滤器可能列举多个。列表不能为空;一个过滤器必须包含至少一个动作元素,否则它将阻塞所有的意图。
为了通过这个测试,在意图对象中指定的动作必须匹配过滤器中所列举的动作之一。如果意图对象或过滤器不指定一个动作,结果将如下:
· 如果这个过滤器没有列出任何动作,那意图就没有什么可匹配的,因此所有的意图都会测试失败。没有意图能够通过这个过滤器。
· 另一方面,一个未指定动作的意图对象自动通过这个测试-只要过滤器包含至少一个动作。
类别测试Category test
一个意图过滤器<intent-filter>元素也列举了类别作为子元素。比如:
<intent-filter . . . >