【Android】 Intent 笔记

1、Intent的用途

Intent主要有如下几种重要用途:
  • 1. 启动Activity能够将Intent对象传递给startActivity()方法或startActivityForResult()方法以启动一个Activity,该Intent对象包含了要启动的Activity的信息及其余必要的数据。
  • 2. 启动Service能够将Intent对象传递给startService()方法或bindService()方法以启动一个Service,该Intent对象包含了要启动的Service的信息及其余必要的数据。关于使用startService()方法启动Service,能够参见《Android中startService基本使用方法概述》。关于使用bindService()方法启动Service,能够参见《Android中bindService基本使用方法概述》。
  • 3. 发送广播广播是一种全部App均可以接收的信息。Android系统会发布各类类型的广播,好比发布开机广播或手机充电广播等。咱们也能够给其余的App发送广播,能够将Intent对象传递给sendBroadcast()方法或sendOrderedBroadcast()方法或sendStickyBroadcast()方法以发送自定义广播。
 
 

2、Intent的类型 

有两种类型的Intent: explicit(显式)的和implict(隐式)的
  • 显式的Intent:若是Intent中明确包含了要启动的组件的完整类名(包名及类名),那么这个Intent就是explict的,即显式的。使用显式Intent最典型的情形是在你本身的App中启动一个组件,由于你本身确定知道本身的要启动的组件的类名。好比,为了响应用户操做经过显式的Intent在你的App中启动一个Activity或启动一个Service下载文件。

当建立了一个显式Intent去启动Activity或Service的时候,系统会当即启动Intent中所指定的组件。 android

  • 隐式的Intent:若是Intent没有包含要启动的组件的完整类名,那么这个Intent就是implict的,即隐式的。虽然隐式的Intent没有指定要启动的组件的类名,可是通常状况下,隐式的Intent都要指定须要执行的action通常,隐式的Intent只用在当咱们想在本身的App中经过Intent启动另外一个App的组件的时候,让另外一个App的组件接收并处理该Intent。例如,你想在地图上给用户显示一个位置,可是你的App又不支持地图展现,这时候你能够将位置信息放入到一个Intent中,而后给它指定相应的action,经过这样隐式的Intent请求其余的地图型的App(例如Google Map、百度地图等)来在地图中展现一个指定的位置。隐式的Intent也体现了Android的一种设计哲学:我本身的App无需一应俱全全部功能,能够经过与其余App组合起来,给用户提供很好的用户体验。而链接本身的App与其余App的纽带就是隐式Intent。

当建立了一个隐式Intent去使用的时候,Android系统会将该隐式Intent所包含的信息与设备上其余全部App中manifest文件中注册的组件的Intent Filters进行对比过滤,从中找出知足可以接收处理该隐式Intent的App和对应的组件。若是有多个App中的某个组件都符合条件,那么Android会弹出一个对话框让用户选择须要启动哪一个App。编程

 
 
Intent Filter,即Intent过滤器, 一个组件能够包含0个或多个Intent Filter。Intent Filter是写在App的manifest文件中的其经过设置action或uri数据类型等指明了组件可以处理接收的Intent的类型
若是你给你的Activity设置了Intent Filter,那么这就使得其余的App有可能经过隐式Intent启动你的这个Activity。反之,若是你的Activity不包含任何Intent Filter,那么该Activity只能经过显式Intent启动,因为咱们通常不会暴露出咱们组件的完整类名,因此这种状况下,其余的App基本就不可能经过Intent启动咱们的Activity了(由于他们不知道该Activity的完整类名),只能由咱们本身的App经过显式Intent启动。
 
须要注意的是, 为了确保App的安全性,咱们应该老是使用显式Intent去启动Service而且不要为该Service设置任何的Intent Filter。经过隐式的Intent启动Service是有风险的,由于你不肯定最终哪一个App中的哪一个Service会启动起来以响应你的隐式Intent,更悲催的是,因为Service没有UI的在后台运行,因此用户也不知道哪一个Service运行了。从Android 5.0 (API level 21)开始,用隐式Intent调用bindService()方法,Android会抛出异常,可是也有相应技巧,将一个隐式的Intent转换为显式的Intent,而后用显式的Intent去调用bindService()方法就没有问题了,具体解决办法能够参见博文《Android中经过Messenger与Service实现进程间双向通讯》中最后的“注意事项”部分,里面有相关代码的解决方案。
 
 

3、Intent的组成

Android能够根据Intent所携带的信息去查找要启动的组件,Intent还携带了一些数据信息以便要启动的组件根据Intent中的这些数据作相应的处理。
 
Intent由6部分信息组成:Component Name、Action、Data、Category、Extras、Flags。根据信息的做用用于,又可分为三类:
  • a. Component Name、Action、Data、Category为一类,这4中信息决定了Android会启动哪一个组件,其中Component Name用于在显式Intent中使用,Action、Data、Category、Extras、Flags用于在隐式Intent中使用。
  • b. Extras为一类,里面包含了具体的用于组件实际处理的数据信息。
  • c. Flags为一类,其是Intent的元数据,决定了Android对其操做的一些行为,下面会介绍。
 

三A、Component name

Component name 要启动的组件的名称。若是你想使用显式的Intent,那么你就必须指定该参数,一旦设置了component name,Android会直接将Intent传递给组件名所指定的组件去启动它。若是没有设置component name,那么该Intent就是隐式的,Android系统会根据其余的Intent的信息(例以下面要介绍到的action、data、category等)作一些比较判断决定最终要启动哪一个组件。因此,若是你启动一个你本身App中的组件,你应该经过指定component name经过显式Intent去启动它(由于你知道该组件的完整类名)。
 
须要注意的是,当启动Service的时候,你应该老是指定Component Name。不然,你不肯定最终哪一个App的哪一个组件被启动了,而且用户也看不到哪一个Service启动了。
 
component name在Intent中对应的 field是ComponentName对象,你能够经过要启动的组件的完整类名(包括应用的包名)指定该值,例如 com.example.ExampleActivity。你能够经过Intent的setComponent()方法、setClass()方法、setClassName()方法或Intent的构造函数指定component name。
 

三B、Action

是表示了要执行操做的字符串,好比查看或选择, 其对应着Intent Filter中的action标签<action />。
 
你能够指定你独有的action以便于你的App中的Intent的使用或其余App中经过Intent调用你的App中的组件。Intent类和Android中其余framework级别的一些类也提供了许多已经定义好的具备必定通用意义的action。如下是一些用于启动Activity的常见的action:
Intent.ACTION_VIEW 其值为 “android.intent.action.VIEW”,当你有一些信息想让经过其余Activity展现给用户的时候,你就能够将Intent的action指定为ACTION_VIEW,好比在一个图片应用中查看一张图片,或者在一个地图应用中展示一个位置。
Intent.ACTION_SEND 其值为”android.intent.action.SEND”,该action经常使用来作“分享”使用,当你有一些数据想经过其余的App(例如QQ、微信、百度云等)分享出去的时候,就可使用此action构建Intent对象,并将其传递给startActivity()方法,因为手机上可能有多个App的Activity均支持ACTION_SEND这一action,因此颇有可能会出现以下的图片所示的情形让用户具体选择要经过哪一个App分享你的数据:
 
能够经过查看Intent类了解更多的Intent预约义的一些常见的action。Android中framework级别的一些类也定义了一些action,例如Settings中定义了一些action用以分别打开系统中“设置”这个应用的不一样界面以完成对指定配置(如WLAN设置、语言设置等)。
 
你能够经过调用intent对象的setAction()方法或在Intent的构造函数中指定intent的action。
 
若是你定义了你本身的action,请务必将你的App的包名做为该action的前缀,这是一种良好的编程习惯,避免形成混淆,例如:
 
复制代码 代码以下:
static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
Data
此处所说的Intent中的data指的是Uri对象和数据的MIME类型,其对应着Intent Filter中的data标签<data />。
一个完整的Uri由scheme、host、port、path组成,格式是<scheme>://<host>:<port>/<path>,例如content://com.example.project:200/folder/subfolder/etc。Uri就像一个数据连接,组件能够根据此Uri得到最终的数据来源。一般将Uri和action结合使用,好比咱们将action设置为ACTION_VIEW,咱们应该提供将要被编辑修改的文档的Uri。
当建立了一个Intent对象的时候,除了指定Uri以外,指定数据的MIME类型也很重要。例如,一个Activity可以显示图片,可是不可以播放视频,显示图片的Uri和播放视频的Uri可能很相似,为了避免让Android误将一个含有视频Uri的Intent对象传递给一个只能显示图片的Activity,咱们须要在该Activity的Intent Filter中指定MIME类型为图片(例如<data android:mimeType="image/*" ... />)而且还要给Intent对象设置对应的图片类型的MIME,这样Android就会基于Uri和MIME类型将Intent传递给符合条件的组件。而后有个特例,若是Uri使用的是content:协议,那么这就说明Uri所提供的数据未来自于本地设备,即数据由ContentProvider提供,这种状况下Android会根据Uri自动推断出MIME类型,此种状况咱们无需再本身指定MIME类型。
 
若是只设置数据的Uri,须要调用Intent对象的setData()方法;若是只设置数据的MIME类型,须要调用Intent对象的setType()方法;若是要同时设置数据的Uri和MIME类型,须要调用Intent对象的setDataAndType()方法。
 
须要注意的是,若是你想要同时设置数据的Uri和MIME类型,不要前后调用Intent对象的setData()方法和setType()方法,由于setData()方法和setType()是互斥的,即若是调用了setData()方法,会将Intent中已经经过setType()方法设置的MIME类型重置为空。若是调用了setType()方法,会将Intent中已经经过setData()方法设置的Uri重置为空。因此在须要同时设置数据的Uri和MIME类型的时候,必定要调用Intent对象的setDataAndType()方法,而不是分别调用setData()方法和setType()方法。
 
Category
category包含了关于组件如何处理Intent的一些其余信息,虽然能够在Intent中加入任意数量的category,可是大多数的Intent其实不须要category。
如下是一些常见的category:
CATEGORY_BROWSABLE 目标组件会容许本身经过一个连接被一个Web浏览器启动,该连接多是一个图片连接或e-mail信息等。
 
CATEGORY_LAUNCHER 用于标识Activity是某个App的入口Activity。
 
你能够在Intent类中查找到更多预约义的category。
 
Extras extras,顾名思义,就是额外的数据信息,Intent中有一个Bundle对象存储着各类键值对,接收该Intent的组件能够从中读取出所须要的信息以便完成相应的工做。有的Intent须要靠Uri携带数据,有的Intent是靠extras携带数据信息。
 
你能够经过调用Intent对象的各类重载的putExtra(key, value)方法向Intent中加入各类键值对形式的额外数据。你也能够直接建立一个Bundle对象,向该Bundle对象传入不少键值对,而后经过调用Intent对象的putExtras(Bundle)方法将其一块设置给Intent对象中去。
 
例如,你建立了一个action为ACTION_SEND的Intent对象,而后想用它启动e-mail发送邮件,那么你须要给该Intent对象设置两个extra的值:
用Intent.EXTRA_EMAIL 做为key值设置收件方,用Intent.EXTRA_SUBJECT 做为key值设置邮件标题。
Intent类里面也指定了不少预约义的EXTRA_*形式的extra,例如上面咱们提到的(Intent.EXTRA_EMAIL 和Intent.EXTRA_SUBJECT)。若是你想要声明你本身自定义的extra,请确保将你的App的包名做为你的extra的前缀,例如:
 
复制代码 代码以下:
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
Flags
flag就是标记的意思,Intent类中定义的flag可以起到做为Intent对象的元数据的做用。这些flag会告知Android系统如何启动Activity(例如,新启动的Activity属于哪一个task)以及在该Activity启动后如何对待它(好比)。更多信息可参见Intent的setFlags()方法。
一、显式Intent使用示例
 
?
1
2
Intent intent = new Intent(this, ActivityB.class);
startActivity(intent);
上面的代码在Intent的构造函数中指定了要启动的组件的ComponentName是ActivityB,该intent对象是显式的,调用startActivity(intent)时,Android系统会当即启动ActivityB。
 
二、隐式Intent使用示例
 
以前提到过,在使用隐式Intent的时候须要指定其action。若是你的App不能完成某个功能,可是其余的App可能完成该功能,那么你就能够用隐式Intent启动其余的App去完成相应的功能。例如,你有一段文本信息,想经过其余App分享出去,那么隐式Intent对象去启动潜在的支持分享的App,示例代码以下:
 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Intent sendIntent = new Intent();
// 设置action, action对隐式Intent来讲是很是重要的
sendIntent.setAction(Intent.ACTION_SEND);
// 设置数据的MIME类型为纯文本类型
sendIntent.setType("text/plain");
// 设置额外的数据
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
 
// 获取包管理器
PackageManager pm = getPackageManager();
// 先判断系统中有没有潜在的App的Activity支持对该sendIntent的接收与处理
if (pm.resolveActivity(sendIntent, 0) != null) {
 startActivity(sendIntent);
}
上面的代码中,咱们构建了一个Intent对象,并无给其设置component name,因此该Intent是一个隐式的Intent对象。咱们首先给intent设置了action的值为Intent.ACTION_SEND,action对隐式Intent来讲是很是重要的。而后咱们将intent的数据的MIME类型设置为纯文本类型(“text/plain”),告知Android咱们的Intent持有的是文本类型的数据。最后咱们将实际的文本数据经过putExtra()方法做为额外数据设置进去。
须要注意的是,在构建好了Intent对象以后,咱们没有当即执行startActivity(sendIntent)方法,而是将sendIntent做为参数传递给了PackageManager的resolveActivity()方法中,该方法会让Android根据该sendIntent找到潜在的适合启动的组件的信息,并以ResolveInfo类的对象的形式返回结果,若是返回null,表示当前系统中没有任何组件能够接收并处理该sendIntent。若是返回不是null,就代表系统中至少存在一个组件能够接收并处理该sendIntent,只有在这种状况下,咱们才会执行代码startActivity(sendIntent),在经过intent启动组件以前先判断要启动的组件存不存在是个良好的编程习惯,由于若是系统中不存在支持你的intent的组件,那么当你调用startActivity()、startService()、bindService()等方法的时候,Android就会抛出异常。
4、强制用户使用App Chooser
 
在上文中咱们已经提到,若是咱们的Intent是隐式的,当咱们经过startActivity(intent)尝试启动组件的时候,可能Android系统会显示上面的截图文件询问用户要启动哪一个App,有时候用户会将某一个App设置为默认的App,这样下次咱们再执行代码startActivity(intent)的时候就有可能不会再出现选择App的界面,而是直接运行上次用户设置为默认App的应用。这对于用户选择一个默认浏览器打开网页这种情形是有好处的,由于通常一个用户习惯于用一个本身喜欢的浏览器。
 
可是若是用户不想每次都用同一个默认App处理这样的情形怎么办呢?这时候咱们能够在代码中明确地使用App选择对话框,好比党咱们的App执行一个action为ACTION_SEND的分享功能时,咱们想让用户分享本身数据的代码,可是咱们不肯定用户想经过哪一个App去分享,咱们想每次都弹出App选择对话框让用户决定想经过哪一个App分享,示例代码以下所示:
 
?
1
2
3
4
5
6
7
8
9
10
11
12
Intent sendIntent = new Intent(Intent.ACTION_SEND);
...
 
String title = "请选择想经过哪一个App分享数据";
 
// 验证是否有App可以接收并处理sendIntent
if (sendIntent.resolveActivity(getPackageManager()) != null) {
 // 根据sendIntent建立一个须要显示App选择对话框的intent对象
 Intent chooserIntent = Intent.createChooser(sendIntent, title);
 // 咱们使用chooserIntent做为startActivity()方法的参数,而非sendIntent
 startActivity(chooserIntent);
}
首先咱们建立了咱们原始的sendIntent,并对其设置action等相关信息,而后咱们将sendIntent传递给了Intent.createChooser()方法中,建立了另外一个chooserIntent。后面咱们经过调用Intent.resolveActivity(PackageManager)方法判断系统中是否有App可以接收并处理sendIntent,该方法与上面以前提到过的PackageManager的resolveActivity()方法是等价的。最后咱们使用chooserIntent做为startActivity()方法的参数,而非sendIntent,chooserIntent会让Android系统强制显示用户选择App处理Intent的界面。
 
本文大部分参考了Android中对Intent部分的Develop Guide的描述,但愿本文对你们更好地使用Intent对象有所帮助。
相关文章
相关标签/搜索