Android–Intent和过滤器
Android中提供了Intent机制来协助应用间的交互与通讯。Intent 可以通过多种方式促进组件之间的通信,其基本用例主要包括以下三个: 启动Activity。使用Context.startActivity() 或 Activity.startActivityForResult(),传入一个intent来启动一个activity。使用 Activity.setResult(),传入一个intent来从activity中返回结果。
启动服务。将intent对象传给Context.startService()来启动一个service或者传消息给一个运行的service。将intent对象传给 Context.bindService()来绑定一个service。
传递广播。将intent对象传给 Context.sendBroadcast(),Context.sendOrderedBroadcast(),或者Context.sendStickyBroadcast()等广播方法,则它们被传给 broadcast receiver。
显式 Intent:需要按名称(完全限定类名)指定要启动的组件。 通常在自己的应用中使用显式 Intent 来启动组件,因为此时知道要启动的 Activity 或服务的类名。 隐式 Intent :不会指定特定的组件,而是声明要执行的常规操作,从而允许其他应用中的组件来处理。
创建显式 Intent 启动 Activity 或服务时,系统将立即启动 Intent 对象中指定的应用组件。 创建隐式 Intent 时,Android 系统通过将 Intent 的内容与在设备上其他应用的清单文件中声明的 Intent 过滤器进行比较,从而找到要启动的相应组件。如果多个 Intent 过滤器兼容,系统会显示一个对话框,支持用户选取要使用的应用。
component(组件) Component属性明确指定Intent的目标组件的类名称。该属性是可选项,只用于显示Intent,如果没有指定,则 Intent 是隐式的。 可以使用 setComponent()、setClass()、setClassName() 或 Intent 构造函数设置组件名称。 比如启动一个Activity:
Intent intent = new Intent(); //创建组件,通过组件来响应 ComponentName component = new ComponentName(MainActivity.this, AnotherActivity.class); intent.setComponent(component); startActivity(intent); //或者可以简写为 Intent intent = new Intent(); intent.setClass(MainActivity.this, SecondActivity.class); startActivity(intent); Intent intent = new Intent(MainActivity.this,SecondActivity.class); startActivity(intent);Action(动作) 用来指定要执行的通用操作的字符串。比如你要吃饭,睡觉,这些就是动作。action可以自定义,也可以使用由 Intent 类(比如ACTION_VIEW,ACTION_PICK等)或其他框架类定义的操作常量。所施加的动作越多,越精确。
category(类别) 类别是一个字符串,包含该类型组件需要处理的意图的附加信息。addCategory() 方法为意图对象添加类别,removeCategory() 方法删除之前添加的类别,getCategories() 获取所有被设置到意图对象中的类别。 Category和action都是作为子元素来声明,举例如下:
Manifest代码:
<activity android:name=".AnotherActivity"> <intent-filter> <action android:name="com.example.testIntent.ACTION_TEST"/> <category android:name="android.intent.category.CATEGORY_TEST" /> </intent-filter> </activity>MainActivity代码:
Intent intent = new Intent(); intent.setAction("com.example.testIntent.ACTION_TEST"); intent.addCategory("com.example.testIntent.CATEGORY_TEST"); startActivity(intent);只有action和Category同时匹配时,才能响应这个Intent。intent Filter中如果category没有指定,必须使用默认的。intent设置时没有添加category时为自动添加默认的category。每个Intent中只能指定一个action,但可以指定多个category。一个Intent Filter中可以包含多个action和category。
data(数据)和type(数据类型) data是要访问的数据,用URI表示,通常情况下,使用action+data属性的组合来描述一个意图:做什么。intent Filter中声名时可以只是一个数据类型(type),一条 URI ,或者同时包括数据类型和 URI 。 setData() 方法只能以 URI 来指定数据,setType() 只能以元类型指定数据,setDataAndType() 可以同时指定 URI 和元类型。URI 通过 getData() 读取,类型通过 getType() 读取。如果Intent对象中既包含Uri又包含Type,那么,在中也必须二者都包含才能通过。
Data属性与Type属性的关系比较微妙,这两个属性会相互覆盖,例如: 如果为Intent先设置Data属性,后设置Type属性,那么Type属性将会覆盖Data属性。 如果为Intent先设置Type属性,后设置Data属性,那么Data属性将会覆盖Type属性。 如果希望Intent既有Data属性,也有Type属性,则应该调用Intent的setDataAndType() 方法。
注: Uri字符串总满格式:scheme://host:port/path,例如content://com.android.contacts/contacts/1tel:4536,其中 content 是 scheme 部分, com.android.contacts 是 host 部分,port 部分被省略了,/contacts/1 是 path 部分。
Data属性并不要求被启动组件的<intent-filter…/>中<data…/>之元素android:scheme、android:host、android:port、android:path 完全满足。比如: 如果目标组件的<data…/>子元素只指定了 android:scheme属性,那么只要Intent的Data 属性的scheme部分与android:scheme属性值相同,即可启动该组件。
这里有两点需要注意:
如果<data…/>子元素只有android:port属性,没有指定android:host属性,那么android.port属性将不会起作用。 如果<data…/>子元素只有android:path属性,没有指定android:host属性,那么android:path属性将不会起作用。
常见的几个Data属性:
tel://:号码数据格式,后跟电话号码。 mailto://:邮件数据格式,后跟邮件收件人地址。 smsto://:短信数据格式,后跟短信接收号码。 content://:内容数据格式,后跟需要读取的内容。 file://:文件数据格式,后跟文件路径。
extra(拓展信息) 这是传递给需要处理意图的组件的以键值对描述的附加信息。通过 putExtras() 方法设置,每种方法均接受两个参数:键名和值。也可以创建一个包含所有 extra 数据的 Bundle 对象,然后getExtras() 方法读取。
Intent intent = new Intent(Intent.ACTION_SEND); Bundle extra = new Bundle(); extra.putString(Intent.EXTRA_EMAIL,"lalala@domain.com"); extra.putString(Intent.EXTRA_SUBJECT,"Subject"); extra.putString(Intent.EXTRA_TEXT,"Hello"); intent.putExtras(extra); startActivity(intent);flag(标志位) 说明Android系统如何来启动活动,启动后如何处理。一个程序启动后系统会为这个程序分配一个task供其使用,另外同一个task里面可以拥有不同应用程序的activity。 那么,同一个程序能不能拥有多个task?这就涉及到加载activity的启动模式。注:android中一组逻辑上在一起的activity被叫做task,自己认为可以理解成一个activity堆栈。
Intent intent = new Intent(MainActivity.this,SecondActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//相当于singleTask //intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);//相当于singleTop startActivity(intent);activity的四种启动模式
1.Standard模式 Standard模式是Android的默认启动模式,这种模式下,Activity可以有多个实例,每次启动Activity,无论任务栈中是否已经有这个Activity的实例,系统都会创建一个新的Activity实例。大部分情况下,都应该使用这种模式,当确实有特殊需求时,再考虑其他模式。
2 .SingleTop模式 SingleTop模式和standard模式非常相似,主要区别就是当一个singleTop模式的Activity已经位于任务栈的栈顶,再去启动它时,不会再创建新的实例,如果不位于栈顶,就会创建新的实例.
3.SingleTask模式 SingleTask模式的Activity在同一个Task内只有一个实例,如果Activity已经位于栈顶,系统不会创建新的Activity实例,和singleTop模式一样。但Activity已经存在但不位于栈顶时,系统就会把该Activity移到栈顶,并把它上面的activity出栈。
4.SingleInstance模式 singleInstance模式也是单例的,但和singleTask不同,singleTask只是任务栈内单例,系统里是可以有多个singleTask Activity实例的,而singleInstance Activity在整个系统里只有一个实例,启动一singleInstanceActivity时,系统会创建一个新的任务栈,并且这个任务栈只有他一个Activity。
拨打电话
Uri uri = Uri.parse("tel:10086"); Intent intent = new Intent(Intent.ACTION_DIAL, uri); startActivity(intent);打开浏览器访问网站
Uri uri = Uri.parse("http://www.baidu.com"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent);发送短信
Uri uri = Uri.parse("smsto:10000"); Intent intent = new Intent(Intent.ACTION_SENDTO, uri); intent.putExtra("sms_body", "Hello"); startActivity(intent);播放多媒体
Intent intent = new Intent(Intent.ACTION_VIEW); Uri uri = Uri.parse("file:///sdcard/11.mp3"); intent.setDataAndType(uri, "audio/mp3"); startActivity(intent);拍照并存储图片
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); long time = Calendar.getInstance().getTimeInMillis(); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment .getExternalStorageDirectory().getAbsolutePath()+"/tucue", time + ".jpg"))); startActivityForResult(intent, ACTIVITY_GET_CAMERA_IMAGE);发送邮件
Intent intent = new Intent(Intent.ACTION_SEND); intent.putExtra(Intent.EXTRA_EMAIL, "l1245265290@163.com"); intent.putExtra(Intent.EXTRA_SUBJECT, "Subject"); intent.putExtra(Intent.EXTRA_TEXT, "Hello"); intent.setType("text/plain"); startActivity(intent);IntentFilter翻译成中文就是“意图过滤器”,主要用来过滤隐式意图。当用户进行一项操作的时候,Android系统会根据配置的 “意图过滤器” 来寻找可以响应该操作的组件,服务。声名在清单文件中。 用途 Intent Filter是各个组件用于描述其功能的,通过组件的Intent Filter信息,Android的组件管理应用就可以了解和掌握各个组件所具备的能力和能够处理的请求并匹配符合条件的Intent对象。 每个组件都可以有任意数量的Intent Filter。组件包含的Intent Filter对象越多,说明他能接受Intent请求范围越广,同时,其实现也会越复杂。
IntentFilter的匹配规则 IntentFilter的过滤信息有action,category,data.一个组件可以包含多个intent-filter,一个intent只要能完全匹配一组intent-filter即可成功的启动对应的组件。
1.action的匹配 如果IntentFilter中有action,Intent中必须有action Intent中的action必须在相应IntentFilter中存在 Intent中只需要有一个action和IntentFilter中相同即可 2.category的匹配 与action和data不同,Intent中的category必须都在Intent Filter中出现才算匹配成功。Intent可以不指定category,若Intent中未指定category,系统会自动为它带上“android.intent.category.DEFAULT”。所以,想要接收隐式Intent的组件都必须在manifest文件中的Intent Filter声明中必须加上默认的category, 3.data的匹配 同action类似,只要Intent的data只要与Intent Filter中的任一个data相同即可匹配成功。若Intent Filter的data声明部分未指定uri,则缺省uri为content或file,Intent中的uri的scheme部分需为content或file才能匹配;
IntentFilter的匹配算法 算法输入的是进行比较的Intent对象和Intent Filter对象,输出的是一个32位的整数值,用于表征两者的匹配程度。 整个匹配算法的流程可分为3个步骤 1. Action的比较:每个IntentFilter对象都必须包含Action信息,如果没有,则对任何一个Intent对象都会匹配失败 2. Data和Type的比较:Data和Type信息是Intent Filter中最复杂的数据项,其比较算法是决定Intent与Intent Filter对象匹配程度的关键 3. Category的比较
匹配组件的选择
如果有多个Intent Filter对象与调用组件发出的Intent对象都相匹配,就需要在所有符合条件的Intent Filter对象中进行筛选,选出最符合调用组件和用户需求的实现组件,这就是匹配组件的选择。
可以通过设定Intent Filter对象的优先级来进行设定,即通过配置项中的android:priority属性进行变更,或者通过IntentFilter.setPriority函数进行动态地修改。范围是-1000至1000,默认为0,值越大,优先级越高。
匹配的优化 在Intent机制中,通过引入第三方组件管理服务,降低了调用组件与实现组件之间的耦合,提高了整个系统的灵活性及组件的复用性,使得应用开发变得更为简单快捷。但同时,正是由于第三方服务地介入增加了组件间连接的成本,可能会使组件间的调用不够流畅。因此,在组件管理服务中,系统对组件的匹配和选择过程进行了大量的优化,以提高组件调用的效率,其中包括:
(1)索引 组件管理服务通过哈希表,为所有Intent Filter对象的Action、Type等数据项建立索引。每个索引项对应着一组Action相同,Type相同或者其他数据项相同的Intent Filter对象。Intent先与索引项比较,快速地选择出可能与Intent相匹配的Intent Filter对象。以此加快匹配速度。
(2)缓存 将Intent和Intent Filter的匹配结果记录下来,当再碰到相同的Intent的调用时,可直接返回上次记录的结果,从而跳过意图匹配的过程,加速组件的调用。服务组件,组件管理服务会在内存中通过哈希表的形式保留各个Intent对应的服务组件。