摘要:现在可以看中文的部分文档,可是内容真心不好记。看过之后就无名的又忘记了。还是摘抄一道的方式去加深记忆和当做一次笔记方便后面自己快速查找。记录一下,Intent,Intent的过滤和 常用的通用 Intent
Intent
intent是一种运行时绑定(run-time binding)机制,它能在程序运行过程中链接两个不同的组件。通过 intent,你的程序可以向 Android表带某种请求或者意愿,Android会根据意愿的内容适当的组件来完成请求。比如,有一个 activity 希望打开网页浏览器查看某一网页内容,那么这个activity 只需要发出 WEB_SEARCH_ACTION给Android,Android就会根据 intent的请求内容,查询个组件注册是声明的 intentFilter,找到网页浏览器的 activity 来浏览网页。Android的三个基本组件——Activity,Service和Broadcast Receiver都是通过intent机制激活的,不同类型的组件有不同的传递intent:
●启动 Activity
Activity 表示应用中的一个屏幕。通过将 intent 传递给 startactivity( ),你可以启动新的 Activity实例。Intent 描述了要启动的 Activity,并携带了任何必要的数据。相应的 activity 可以通过 getIntent( )方法来查看激活它的 intent。Android 通过调用 activity的 onNewIntent( )方法来传递给它激发的 intent。
如果你希望在 Activity完成后收到结果,可以使用 startActivityForResult( )。在 Activity 的 onActivityResult()回调中,你的Activity将结果作为单独的 intent 对象接收。比如,如果它启动了另一个activity已使用户挑选一张照片,它也许想知道那张照片被选中了。结果将会被封装在一个 intent对象中,并传递给发出调用的activity的onActivityResult( )方法。
●启动 Service
service是一个不使用用户界面而在后台执行操作的组件。通过将 intent传递给 startService( ),可以启动服务执行一次性操作(例如:下载文件)。intent描述了要启动的服务,并携带了任何必要的数据。
如果服务旨在使用客服单-服务器接口,则通过将 intent 传递给 bindService( ),可以从其它组件绑定到此服务。
●传递广播
广播是任何应用均可以接收的消息。系统将针对系统时间(例如:系统启动或设备开始充电时)传递各种广播。通过将 intent 传递给 sendBroadcast()、sendOrderedBroadcast() 或 sendStickyBroadcast(),发送之后,所有已注册的并且拥有与之相匹配 intentFilter 的 Broadcast就会被激活。
intent一旦发出,Android都会准确找到相匹配的一个或者多个 Activity,service或者BroadcastReceiver做响应。所以,不同类型的intent消息不会出现重叠,即Broadcast的intent消息只会发送给 Broadcastreceiver,而决定不会发送给 activity或者 service。由startactivity( )传递消息也只会发给activity,由startService()传递的Intent只会发送给Service。
intent类型
intent分为 两种类型:
●显示 intent:按名称(完全限定类名)指定要启动的组件。指定 component属性的intent(调用setComponentName)或者setClass(Context context,Class clz)。通过指定具体的组件类,通知应用启动对应的组件。
Intent intent = new Intent();
// 构造的参数为当前Context和目标组件的类路径名
ComponentName cn = new ComponentName(HelloActivity.this, "com.hu.smaple.OtherActivity");
intent.setComponent(cn);
startActivity(intent);
相当于以下常用方法: Intent intent = new Intent();
intent.setClass(HelloActivity.this, OtherActivity.class);
startActivity(intent);
Intent intent = new Intent();
intent.setClass(Context packageContext, OtherActivity.class);
startActivity(intent);
●隐式 intent:不指定特定的组件,声明要执行的常规操作,从而允许其它应用中的组件来处理它。例如,如果需要地图上向用户显示位置,则可以使用隐式intent,请求里有一个具有此功能的应用在地图上显示指定的位置。创建隐式intent时,Android系统通过将intent的内容与在设备上其他应用的清单文件中声明的 intent 过滤器进行比较,从而找到要启动的相应的组件。如果 intent与intent过滤匹配,则系统将启动组件。并向其传递intent对象。如果多个intent过滤器兼容,则系统会显示一个对话框,支持用户选取要使用的应用。
intent过滤器是应用清单文件中的一个表达式,它指定该组件要接收的intent类型。例如:通过为 activity声明intent过滤器,你可以会用其它应用能够直接使用某一特定类型的 intent 启动 activity。同样,如果没有给activity声明任何intent过滤器,则 activity只能通过显示 intent启动。
- Activity A 创建包含操作描述的 intent,并将其传给 startActivity( )
- Android 系统搜索所有应用中与intent匹配的intent 过滤器。
- 找到匹配项之后,该系统通过调用匹配 Activity(activity B)的 oncreate() 方法并将其传递给 intent,以此启动匹配的 activity
注意:为了确保应用的安全性,启动 Service时,请始终使用显示intent,切不要为服务声明intent过滤器。使用隐式intent启动服务存在安全隐患,因为你无法确定哪些服务将相应 intent,其用户无法看到哪些服务以启动。从 Android 5.0(api 21)开始,使用隐式intent调用 bindService(),系统会引发异常。
强制使用应用选择器
如果有多个应用响应隐式的intent,则用户可以选着要使用的应用。并将其设置为该操作的默认选项。如果用过可能希望今后一直使用相同的应用执行某一项操作(例如:打开网页,往往倾向于仅使用一种网络浏览器),则这一点十分有用。
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://fanyi.baidu.com/#en/zh/choose"));
//这样写的好处是,会验证是否activity会接受这个intent。
//如果结果为非空,则至少有一个应用能够处理该 Intent,且可以安全调用 startActivity()。
//如果结果为空,则不应使用该 Intent。
//如有可能,应停用发出该 Intent 的功能
if(intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
但是如果多个应用可以响应 intent,且用户可能希望每一次使用不同的应用,则采用显示方式显示选择器对话框。选择器对话框每次都会要求用户选择用于操作的应用(用户无法为该操作选择默认应用)。例如,当使用 ACTION_SEND操作执行“共享”时,用户根据目前的状况可能需要使用另一个不同的应用,因此应当始终使用选择器对话框 Intent intent = new Intent(Intent.ACTION_VIEW) ;
intent.setData(Uri.parse("http://fanyi.baidu.com/#en/zh/choose"));
//选择框的标题
String title = "每一次都弹出选项框";
Intent choose = Intent.createChooser(intent,title);
if(intent.resolveActivity(getPackageManager())!= null){
startActivity(choose);
}
Tip:这样的写法,在下面没有了“仅此一次“ 和 “始终“ 这两个选项了,所以,在每一次启动的时候。都会进行选择Intent 的过滤
上面的隐式intent是要开系统自动去启动某一个 activity,那系统是怎样匹配 activity的呢 ,系统又是怎样知道这个activity就是某个intent想要的呢 ?
某个activity能不能被某个 intent激活,要看这个 activity是不是符合这个 intent的要求,而某个activity能被那个intent激活是有定义的,定义就在 androidManifest.xml
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
看这里有一个标签 ,为每一个应用组件声明一个或多个 intent 过滤器。每个 intent过滤器根据 intent的 Action、Data、Category指定自身接收的 intent类型。仅当隐式 intent 可以通过 intent 过滤器之一传递时,系统才会将该 intent 传递给应用组件。注意:显示 intent始终传递给其目标,无论组件声明的intent 过滤器如何均是如此。
●Action:该 Activity可以执行的动作
该标识用来说明这个 activity 可以执行那个动作,所以当隐式 intent 传递过来的 action时,如果跟这里的所列出的任意一个匹配的话,就说明这个 activity 是可以完成这个 intent 意图的,可以将它激活。在 name属性中声明接收的 intent 操作,该值必须是文字符串,而不是类常量。
两个原则
- 一条<intent-filter>元素至少包含一个action,否则任何intent请求都不能和该<intent-filter>匹配
- 如果 intent 请求的 action和<intent-filter>中任意一条 action匹配,那么该intent就可以激活该activity(前提是处了 action的其它项也要通过)
两个注意
如果intent 请求或 <intent-filter>中没有说明 action的类型。那么,会出现下面两种情况。
- 如果<intent-filter>中没有包含任何的 action类型。那么,无论什么请求都无法和这条<intent-filter>匹配
- 反之,如果 intent 请求中没有设定 action类型。那么,只要<intent-filter>中包含有action类型,这个intent请求就将顺利的通过<intent-filter>额行为测试
常用 action 操作
ACTION_CALL activity 启动一个电话.
ACTION_EDIT activity 显示用户编辑的数据.
ACTION_MAIN activity 作为Task中第一个Activity启动
ACTION_SYNC activity 同步手机与数据服务器上的数据.
ACTION_BATTERY_LOW broadcast receiver 电池电量过低警告.
ACTION_HEADSET_PLUG broadcast receiver 插拔耳机警告
ACTION_SCREEN_ON broadcast receiver 屏幕变亮警告.
ACTION_TIMEZONE_CHANGED broadcast receiver 改变时区警告.
●Category:(类别)指定当前动作(action) 被执行的环境 一个字符串,包含了关于处理该intent的组件的种类信息。即这个activity在那个环境中才能被激活,不属于这个环境,不能被激活。一个intent的对象可以有任意个 category。intent类定义了许多了 category常数。addCategory()方法为一个intent对象增加一个category,removeCategory删除一个category,getCategories()获取intent所有的category。常用的Category属性如下所示:
CATEGORY_DEFAULT:Android系统中默认的执行方式,按照普通Activity的执行方式执行。表示所有intent都可以激活它
CATEGORY_HOME:设置该组件为Home Activity。
CATEGORY_PREFERENCE:设置该组件为Preference。
CATEGORY_LAUNCHER:设置该组件为在当前应用程序启动器中优先级最高的Activity,通常为入口ACTION_MAIN配合使用。
CATEGORY_BROWSABLE:设置该组件可以使用浏览器启动。表示该activity只能用来浏览网页。
CATEGORY_GADGET:设置该组件可以内嵌到另外的Activity中。
如果该 activity想要通过隐式 intent方式激活,那么不能没有任何的category设置,至少包含一个 android.intent.category.DEFAULT。因为在隐式 intent调用的时候,或默认增加一个 android.intent.category.DEFAULT 属性。●Data 执行时要操作的数据 举个栗子
/**
* 打开指定网页
* @param view
*/
public void invokeWebBrowser(View view) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.google.com.hk"));
startActivity(intent);
}
/**
* 进行关键字搜索
* @param view
*/
public void invokeWebSearch(View view) {
Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
intent.putExtra(SearchManager.QUERY, "android"); //关键字
startActivity(intent);
}
上面的两个方法分别是启动浏览器并打开指定的网页、进行关键字搜索,分别对应的action是Intent.ACTION_VIEW和Intent.ACTION_WEB_SEARCH,前者需要指定相应网页地址,后者需要指定关键字信息,对于关键字搜索来说,浏览器会按照自己设置的默认搜索引擎进行搜索。我们注意到,在打开网页时,为 intent 指定一个 data属性,这其实是指定要操作的数据,是一个 Uri 的形式,我们可以将一个指定的前缀的字符串转换成特定的 URI类型,如:“http:”或者 “https:”表示网络地址类型,“tel:”表示电话号码类型,“mailto:”表示邮件地址类型,等。例如,我们要呼叫给定的号码,可以这样做:
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:12345678"));
startActivity(intent);
那么我们如何知道是否接受这种前缀了?这久需要看一下目标中的<data>元素的匹配规则了。
在目标<data >标签中包含了几种子元素,他们定义了 url的匹配规则 api 15:
<declare-styleable name="AndroidManifestData" parent="AndroidManifestIntentFilter">
<attr name="mimeType" format="string" />
<attr name="scheme" format="string" />
<attr name="host" format="string" />
<attr name="port" format="string" />
<attr name="path" />
<attr name="pathPrefix" />
<attr name="pathPattern" />
</declare-styleable>
android:mimeType | 这个属性是用于设置数据的MIME,如:image/jpeg或audio/mpeg4-generic。其子类可用星号通配符(*)来代替,指示能够跟任何子类型匹配 注意:在Android框架中,MIME类型的匹配是大小写敏感的,跟RFC格式不一样。因此,要始终使用小写字母来指定MIME类型。 |
android:scheme | 这个属性用于设定 URI 的 scheme部分。它是指定 URI设置的最基本的属性,至少要给过滤器设置一个 scheme属性。否则,其它的URI属相就么有意义了 scheme属性值没有”:”符号结尾(如,http,而不是 http:) scheme如果过滤器有一个数据类型(设置了mimeType属性),但没有设置 scheme属性,俺么系统就会假定 scheme是content:和 file: scheme注意:在Android框架中,scheme的匹配时大小写敏感的,跟RFC格式不一样。因此,要始终使用小写字母来指定scheme。/td> |
android:host | 这个属性用户定义了URI授权的主机部分,除非给过滤器也指定了<data >元素上的 scheme属性,否则这个属性没有意义 注意:在Android框架中,主机名的匹配是大小写敏感的,跟RFC格式不一样。因此,要始终使用小写字母来指定主机名。 |
android:port | 这个属性用于定义URI授权的断就部分。只有给过滤器指定了scheme和host属性时,这个属性才有意义 |
android:path | 这三个属性用于指定UrI的路径部分, Path属性指定一个完整的路径。这个路径会跟 intent 对象中的路径进行匹配。 PathPrefix属性只指定了部分的路径,它会跟 intent对象中的路径初始部分匹配 pathPattern属性指定一个要跟Intent对象中的路径进行匹配的完整路径,但是这个路径中可以包含下列通配符:
因为系统读取XML中的字符串时,会把’\’符号作为强制转义字符,因此就需要两次转义。例如,符号”*”要被写成”\\*”,符号’\’要被写成”\\\\”。这与Java代码中的写法基本相同。 有关这三种模式的更多信息,请看PatternMatcher类中的PATTERN_LITERAL、PATTERN_PREFIX、PATTERN_SIMPLE_GLOB的说明。 http://developer.android.com/reference/android/os/PatternMatcher.html |
android:pathPrefix | |
android:pathPattern |
这个元素用于把数据规范添加到一个 intent过滤器中,数据规范能够只是数据类型(mimeTyep属性)、或数据位置标识(URI)、也可以是数据类型和数据位置标识(URI)。一个URI(如下格式)被分成几个独立的属性来分别指定:
scheme://host:port/path or pathPrefix or pathPattern
这些属性是可选得,但也是相互依赖的。如果没有给 intent过滤器指定 scheme属性,那么所有其它的 URI 属性都会被忽略。如果没有给过滤器指定 host属性。那么,port 属性和所有的路径属性都会被忽略。
包含同一个<intent-filter >元素中所有的 <data >元素只会对这个过滤器起作用,例如:
<intent-filter . . . >
<data android:scheme="something" android:host="project.example.com" />
. . .
</intent-filter>
等同于
<intent-filter . . . >
<data android:scheme="something" />
<data android:host="project.example.com" />
. . .
</intent-filter>
可以在<intent-filter>元素内放置多个<data>元素,来给过滤器设置多个数据选项、<data>元素的属性没有默认值。
一个摘抄的demo示例,我们改动一下 TargetActivity的声明信息:
<activity android:name=".TargetActivity">
<intent-filter>
<action android:name="com.scott.intent.action.TARGET"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="scott" android:host="com.scott.intent.data" android:port="7788" android:path="/target"/>
</intent-filter>
</activity>
这个时候如果指定 action就不够了,我们需要为其设置 data值,如下:
public void gotoTargetActivity(View view) {
Intent intent = new Intent("com.scott.intent.action.TARGET");
intent.setData(Uri.parse("scott://com.scott.intent.data:7788/target"));
startActivity(intent);
}
此时,url中的每个部分和 TargetActivity 配置信息中全部一致才能跳转成功,否则就系统拒绝。
不过有时候对path 限定死了也不好,比如我们这样的 url:(scott://com.scott.intent.data:7788/target/hello)(scott://com.scott.intent.data:7788/target/hi)
这个时候该怎么办呢? 我们需要使用另一个元素 Android:pathPrefix,表示路径前缀。
我们把android:path=”/target” 修改为 android:pathPrefix=“/target”,然后就可以满足以上的要求了。
而在进行搜索时,我们使用一个 putExtra方法,将关键的参数放置在 intent中,我们成为 extras(附加信息),这里面涉及到一个 Bundle对象。
Bundle和intent有着密不可分的关系,主要负责为intent保存附加参数信息,它实现了 android.os.Paracelable接口,内部维护一个 Map类型的属性,用于以键值对的形式存放附加的参数信息。在我们使用 intent的putExtra方法放置信息时,该方法会检查默认的Bundle实例为不为空。如果为空,则创建一个Bundle实例,然后将具体的参数信息放置到Bundle实例中。我们也可以自己创建Bundle对象,然后为intent指定这个Bundle即可,如下:
public void gotoTargetActivity(View view) {
Intent intent = new Intent("com.scott.intent.action.TARGET");
Bundle bundle = new Bundle();
bundle.putInt("id", 0);
bundle.putString("name", "scott");
intent.putExtras(bundle);
startActivity(intent);
}
需要注意的是,在使用putExtras方法设置Bundle对象之后,系统进行的不是引用操作。而是复制操作。所以如果设置完之后再更改 Bundle实例中的数据。将不会影响 intent内部附加信息。那我们如何获取设置在intent中附加的信息了?与之对应的是,我们要从intent中获取到Bundle实例,然后在从中取出对应的键值信息:
Bundle bundle = intent.getExtras();
int id = bundle.getInt("id");
String name = bundle.getString("name");
当然我们也可以使用 intent 的getIntExtra和getStringExtra方法获取,其数据源都是 intent中的bundle类型的实例对象。
PendingIntent
PendingIntent对象是 intent对象的包装类。PendingIntent 的主要目的是授权外部应用使用包含的 intent,就像是它从应用本身的进程中执行的一样。
PendingIntent的主要用例包括:
- 声明用户使用你的通知执行操作时,所要执行的 intent(Android 系统的 NotificationManager 执行 intent)
- 声明用户使用你的 应用小部件执行操作时要执行的 intent(主屏幕应用执行 Intent)
- 声明未来某一特定时间要执行的 Intent(Android 系统的 AlarmManager 执行 Intent)
由于每个 Intent 对象均设计为由特定类型的应用组件(Activity、Service 或 BroadcastReceiver)进行处理,因此还必须基于相同的考虑因素创建 PendingIntent。使用待定 Intent 时,应用不会使用调用(如 startActivity())执行该 Intent。相反,通过调用相应的创建器方法创建 PendingIntent 时,您必须声明所需的组件类型:
- PendingIntent.getActivity(),适用于启动 Activity 的 Intent
- PendingIntent.getService(),适用于启动 Service 的 Intent
- PendingIntent.getBroadcast(),适用于启动 BroadcastReceiver 的 Intent
除非您的应用正在从其他应用中接收待定 Intent,否则上述用于创建 PendingIntent 的方法可能是您所需的唯一 PendingIntent 方法。每种方法均会提取当前的应用 Context、您要包装的 Intent 以及一个或多个指定应如何使用该 Intent 的标志(例如,是否可以多次使用该 Intent)。
通用 Intent
Intent 用于通过描述您想在某个 Intent 对象中执行的简单操作(如“查看地图”或“拍摄照片”)来启动另一应用中的某个 Activity。 这种 Intent 称作隐式 Intent,因为它并不指定要启动的应用组件,而是指定一项操作并提供执行该操作所需的一些数据。
当您调用 startActivity() 或 startActivityForResult() 并向其传递隐式 Intent 时,系统会 将 Intent 解析为可处理该 Intent 的应用并启动其对应的 Activity。 如果有多个应用可处理 Intent,系统会为用户显示一个对话框,供其选择要使用的应用。 链接:通用 Intent
// 调用浏览器
Uri webViewUri = Uri.parse("http://blog.csdn.net/zuolongsnail");
Intent intent = new Intent(Intent.ACTION_VIEW, webViewUri);
// 调用地图
Uri mapUri = Uri.parse("geo:100,100");
Intent intent = new Intent(Intent.ACTION_VIEW, mapUri);
// 播放mp3
Uri playUri = Uri.parse("file:///sdcard/test.mp3");
Intent intent = new Intent(Intent.ACTION_VIEW, playUri);
intent.setDataAndType(playUri, "audio/mp3");
// 调用拨打电话
Uri dialUri = Uri.parse("tel:10086");
Intent intent = new Intent(Intent.ACTION_DIAL, dialUri);
// 直接拨打电话,需要加上权限<uses-permission id="android.permission.CALL_PHONE" />
Uri callUri = Uri.parse("tel:10086");
Intent intent = new Intent(Intent.ACTION_CALL, callUri);
// 调用发邮件(这里要事先配置好的系统Email,否则是调不出发邮件界面的)
Uri emailUri = Uri.parse("mailto:zuolongsnail@163.com");
Intent intent = new Intent(Intent.ACTION_SENDTO, emailUri);
// 直接发邮件
Intent intent = new Intent(Intent.ACTION_SEND);
String[] tos = { "zuolongsnail@gmail.com" };
String[] ccs = { "zuolongsnail@163.com" };
intent.putExtra(Intent.EXTRA_EMAIL, tos);
intent.putExtra(Intent.EXTRA_CC, ccs);
intent.putExtra(Intent.EXTRA_TEXT, "the email text");
intent.putExtra(Intent.EXTRA_SUBJECT, "subject");
intent.setType("text/plain");
Intent.createChooser(intent, "Choose Email Client");
// 发短信
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra("sms_body", "the sms text");
intent.setType("vnd.android-dir/mms-sms");
// 直接发短信
Uri smsToUri = Uri.parse("smsto:10086");
Intent intent = new Intent(Intent.ACTION_SENDTO, smsToUri);
intent.putExtra("sms_body", "the sms text");
// 发彩信
Uri mmsUri = Uri.parse("content://media/external/images/media/23");
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra("sms_body", "the sms text");
intent.putExtra(Intent.EXTRA_STREAM, mmsUri);
intent.setType("image/png");
// 卸载应用
Uri uninstallUri = Uri.fromParts("package", "com.app.test", null);
Intent intent = new Intent(Intent.ACTION_DELETE, uninstallUri);
// 安装应用
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File("/sdcard/test.apk"), "application/vnd.android.package-archive");
// 在Android Market中查找应用
Uri uri = Uri.parse("market://search?q=愤怒的小鸟");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
//一个选择相册的 intent
//调用相册
Intent intent = new Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, IMAGE);
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//获取图片路径
if (requestCode == IMAGE && resultCode == Activity.RESULT_OK && data != null) {
Uri selectImage = data.getData();
String[] filePathColumns = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(selectImage, filePathColumns, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumns[0]);
String imagePath = cursor.getString(columnIndex);
picPath = imagePath ;
cursor.close();
}
}