Intent组件构成,解析机制
Android中提供了Intent机制来协助应用间的交互与通讯,Intent负责对应用中一次操作的动作、动作涉及数据、附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组件,将 Intent传递给调用的组件,并完成组件的调用。
由于Intent的出现,组件只需要将自己的功能通过Intent描述,而不必具体实现对组件的引用,Service、BroadcastReceiver等都是通过Intent组件关联起来的,这些工作全部由Andorid Runtime来实现,因此,Intent最大的优点就是完美地实现了调用者与被调用者之间的解耦。
比如说调用startActivity()来启动一个activity,或者由broadcaseIntent()来传递给所有感兴趣的BroadcaseReceiver,再或者由startService()/bindservice()来启动一个后台的service.所以可以看出来,intent主要是用来启动其他的activity或者service,所以可以将intent理解成activity之间的粘合剂。
二、Intent的构成
在Android参考文档中,对Intent的定义是执行某操作的一个抽象描述(确实很抽象)。我们先来看看这里的抽象描述,到底描述了什么。
首先,是要执行的动作(action)的一个简要描述,如VIEW_ACTION(查看)、EDIT_ACTION(修改)等,Android为我们定义了一套标准动作:(具体的可以查阅android SDK-> reference中的Android.content.intent类,里面的constants中定义了所有的action。)
标准的Activity Actions
ACTION_MAIN作为一个主要的进入口,而并不期望去接受数据
ACTION_VIEW向用户去显示数据
ACTION_ATTACH_DATA用于指定一些数据应该附属于一些其他的地方,例如,图片数据应该附属于联系人
ACTION_EDIT访问已给的数据,提供明确的可编辑
ACTION_PICK从数据中选择一个子项目,并返回你所选中的项目
ACTION_CHOOSER显示一个activity选择器,允许用户在进程之前选择他们想要的
ACTION_GET_CONTENT允许用户选择特殊种类的数据,并返回(特殊种类的数据:照一张相片或录一段音)
ACTION_DIAL拨打一个指定的号码,显示一个带有号码的用户界面,允许用户去启动呼叫
ACTION_CALL根据指定的数据执行一次呼叫
(ACTION_CALL在应用中启动一次呼叫有缺陷,多数应用ACTION_DIAL,ACTION_CALL不能用在紧急呼叫上,紧急呼叫可以用ACTION_DIAL来实现)
ACTION_SEND 传递数据,被传送的数据没有指定,接收的action请求用户发数据
ACTION_SENDTO发送一个信息到指定的某人
ACTION_ANSWER 处理一个打进电话呼叫
ACTION_INSERT插入一条空项目到已给的容器
ACTION_DELETE 从容器中删除已给的数据
ACTION_RUN运行数据,无论怎么
ACTION_SYNC同步执行一个数据
ACTION_PICK_ACTIVITY为已知的Intent选择一个Activity,返回别选中的类
ACTION_SEARCH 执行一次搜索
ACTION_WEB_SEARCH 执行一次web搜索
ACTION_FACTORY_TEST 工场测试的主要进入点,
标准的广播Actions
ACTION_TIME_TICK 当前时间改变,每分钟都发送,不能通过组件声明来接收,只有通过Context.registerReceiver()方法来注册
ACTION_TIME_CHANGED 时间被设置
ACTION_TIMEZONE_CHANGED 时间区改变
ACTION_BOOT_COMPLETED 系统完成启动后,一次广播
ACTION_PACKAGE_ADDED 一个新应用包已经安装在设备上,数据包括包名(最新安装的包程序不能接收到这个广播)
ACTION_PACKAGE_CHANGED 一个已存在的应用程序包已经改变,包括包名
ACTION_PACKAGE_REMOVED 一个已存在的应用程序包已经从设备上移除,包括包名(正在被安装的包程序不能接收到这个广播)
ACTION_PACKAGE_RESTARTED用户重新开始一个包,包的所有进程将被杀死,所有与其联系的运行时间状态应该被移除,包括包名(重新开始包程序不能接收到这个广播)
ACTION_PACKAGE_DATA_CLEARED用户已经清楚一个包的数据,包括包名(清除包程序不能接收到这个广播)
ACTION_BATTERY_CHANGED电池的充电状态、电荷级别改变,不能通过组建声明接收这个广播,只有通过Context.registerReceiver()注册
ACTION_UID_REMOVED一个用户ID已经从系统中移除
此外,我们还可以根据应用的需要,定义我们自己的动作,并可定义相应的Activity来处理我们的自定义动作。
其次,是执行动作要操作的数据(data),Android中 采用指向数据的一个URI来表示,如在联系人应用中,一个指向某联系人的URI可能为:content://contacts/1。这种URI表示,通过 ContentURI这个类来描述,具体可以参考android.net.ContentURI类的文档。
以联系人应用为例,以下是一些action / data对,及其它们要表达的意图:
- VIEW_ACTION content://contacts/1-- 显示标识符为"1"的联系人的详细信息
- EDIT_ACTION content://contacts/1-- 编辑标识符为"1"的联系人的详细信息
- VIEW_ACTION content://contacts/-- 显示所有联系人的列表
- PICK_ACTION content://contacts/-- 显示所有联系人的列表,并且允许用户在列表中选择一个联系人,然后把这个联系人返回给父activity。例如:电子邮件客户端可以使用这个Intent,要求用户在联系人列表中选择一个联系人
另外,除了action和data这两个重要属性外,还有一些附加属性:
- category(类别、范畴),被执行动作的附加信息。例如 LAUNCHER_CATEGORY 表示Intent 的接受者应该在Launcher中作为顶级应用出现;而ALTERNATIVE_CATEGORY表示当前的Intent是一系列的可选动作中的一个,这 些动作可以在同一块数据上执行。
- type(数据类型)用于指定类型,以供过滤(比如ACTION_VIEW同时指定为Type为Image,则调出浏览图片的应用).一般Intent的数据类型能够根据数据本身进行判定,但是通过设置这个属性,可以强制采用显式指定的类型而不再进行判定。
- component(组件),指定Intent的的目标组件的类名称。通常Android会根据Intent 中包含的其它属性的信息,比如action、data/type、category进行查找,最终找到一个与之匹配的目标组件。但是,如果 component这个属性有指定的话,将直接使用它指定的组件,而不再执行上述查找过程。指定了这个属性以后,Intent的其它所有属性都是可选的。
- extras(附加信息),是其它所有附加信息的集合。使用extras可以为组件提供扩展信息,比如,如果要执行“发送电子邮件”这个动作,可以将电子邮件的标题、正文等保存在extras里,传给电子邮件发送组件。
总之,action、data/type、category和extras 一起形成了一种语言。这种语言使系统能够理解诸如“查看某联系人的详细信息”之类的短语。随着应用不断的加入到系统中,它们可以添加新的action、 data/type、category来扩展这种语言。应用也可以提供自己的Activity来处理已经存在的这样的“短语”,从而改变这些“短语”的行 为。
三、Android如何解析Intent
在应用中,我们可以以两种形式来使用Intent:
- 直接Intent:指定了component属性的Intent(调用setComponent(ComponentName)或者setClass(Context, Class)来指定)。通过指定具体的组件类,通知应用启动对应的组件。
- 间接Intent:没有指定comonent属性的Intent。这些Intent需要包含足够的信息,这样系统才能根据这些信息,在在所有的可用组件中,确定满足此Intent的组件。
对于直接Intent,Android不需要去做解析,因为目标组件已经很明确,Android需要解析的是那些间接Intent,通过解析,将 Intent映射给可以处理此Intent的Activity、IntentReceiver或Service。
Intent解析机制主要是通过查找已注册在AndroidManifest.xml中的所有IntentFilter及其中定义的Intent,最终找 到匹配的Intent。在这个解析过程中,Android是通过Intent的action、type、category这三个属性来进行判断的,判断方 法如下:
- 如果Intent指明定了action,则目标组件的IntentFilter的action列表中就必须包含有这个action,否则不能匹配;
- 如果Intent没有提供type,系统将从data中得到数据类型。和action一样,目标组件的数据类型列表中必须包含Intent的数据类型,否则不能匹配。
- 如果Intent中的数据不是content: 类型的URI,而且Intent也没有明确指定它的type,将根据Intent中数据的scheme (比如 http: 或者mailto: ) 进行匹配。同上,Intent 的scheme必须出现在目标组件的scheme列表中。
- 如果Intent指定了一个或多个category,这些类别必须全部出现在组建的类别列表中。比如Intent中包含了两个类别:LAUNCHER_CATEGORY 和 ALTERNATIVE_CATEGORY,解析得到的目标组件必须至少包含这两个类别。
- 例子:
xml文件:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="fill_parent"android:layout_height="fill_parent"><Button android:text="activity1" android:id="@+id/activity1"android:layout_width="wrap_content" android:layout_height="wrap_content"></Button><Button android:text="activity2" android:id="@+id/activity2"android:layout_width="wrap_content" android:layout_height="wrap_content"></Button><Button android:text="service1" android:id="@+id/service1"android:layout_width="wrap_content" android:layout_height="wrap_content"></Button><Button android:text="service2" android:id="@+id/service2"android:layout_width="wrap_content" android:layout_height="wrap_content"></Button></LinearLayout>
Activity文件:
package com.demo.intent;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.view.View;import android.widget.Button;public class main extends Activity {public static final String User_ACTION = "com.demo.intent.Useraction";public static final String User_ACTION2 = "com.demo.intent.Useraction2";@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);Button firstbtn = (Button) findViewById(R.id.activity1);Button firstbtnservice = (Button) findViewById(R.id.service1);Button secondbtn = (Button) findViewById(R.id.activity2);Button secondbtnservice = (Button) findViewById(R.id.service2);firstbtn.setOnClickListener(new View.OnClickListener() {public void onClick(View v) {// 显式启动 Activity OneIntent i = new Intent(getApplicationContext(),ActivityOne.class);startActivity(i);}});firstbtnservice.setOnClickListener(new View.OnClickListener() {public void onClick(View v) {Intent i = new Intent(getApplicationContext(), ServiceOne.class);startService(i);}});//隐式启动secondbtn.setOnClickListener(new View.OnClickListener() {public void onClick(View v) {Intent intent = new Intent(); intent.setAction(User_ACTION);startActivity(intent);}});secondbtnservice.setOnClickListener(new View.OnClickListener() {public void onClick(View v) {Intent intent = new Intent();intent.setAction(User_ACTION2);startService(intent);}});}}
应用程序的配置文件AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.demo.intent" android:versionCode="1" android:versionName="1.0"><application android:icon="@drawable/icon" android:label="@string/app_name"><activity android:name="main" android:label="@string/app_name"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><activity android:name=".ActivityOne" android:label="@string/app_name"></activity><activity android:name=".ActivityTwo" android:label="@string/app_name"><intent-filter><action android:name="com.demo.intent.Useraction" /> <category android:name="android.intent.category.DEFAULT" /></intent-filter></activity><service android:name=".ServiceOne"></service><service android:name=".ServiceTwo"><intent-filter><action android:name="com.demo.intent.Useraction2" /><category android:name="android.intent.category.DEFAULT" /></intent-filter></service></application><uses-sdk android:minSdkVersion="9" /></manifest>
上面分别演示了显式和隐式启动组件,在显示启动的组件操作中,首先利用组件的类名作为参数来创建Intent对象,然后将此Intent对象作为参数传递给startActivity()或startService()方法,
在隐式启动组件中,首先利用new intent()创建一个空的Intent对象,然后通过setAction,setDateType等方法来设置Intent对象的属性,随后将此Intent对象作为参数传递给startActivity或者startService方法。
如果用过Intent实现调用组件,各组件必须在AndroidManifest.xml中进行配置,并通过Intent-Filter来声明自己的能力,如果不声明Intent-Filter,则组件只能通过显式配置。
对于隐式调用,Intent默认的category为android:intent.category.DEFAULT,如果将<intent-filter>节点的category属相删除,启动程序会抛出异常,显式:ActicityNotFoundException。
所以,对于组件来说,除非不希望被隐式调用,否则一定要加上category属性:android.intent.category.DEFALUT。
更多相关文章
- 深入剖析Android四大组件(三)——AIDL实现Android IPC
- 打造android ORM框架opendroid(七)——数据库升级方案
- android——桌面组件的开发
- Android数据通信--USB通信的几种方式及使用场景
- Android 使用Sharedpreference存储数据
- 使用wireshark 动态显示Android应用中的联网数据
- 像网易,新浪新闻android客户端的数据是怎么更新的?
- 傻瓜式建立数据库,高效数据库操作代码的编写--android