Android Intent

显示Intent和隐式Intent

Android中的Intent是一个非常重要且常用的类,可以用来在一个组件中启动App中的另一个组件或者是启动另一个App的组件,这里所说的组件指的是Activity、Service以及Broadcast。

  • Intent功能

    • 启动Activity
    • 启动Service
    • 发送广播
  • Intent类型

    • 显示Intent,Intent中明确包含了要启动的组件的完整类名(包名及类名),那么这个Intent就是explict的,即显式的。适用于在App内使用,因为开发者一定知道自己开发的组件完整类名。
    • 隐式Intent,Intent没有包含要启动的组件的完整类名,那么这个Intent就是implict的,即隐式的。使用情况,隐式的Intent只用在当我们想在自己的App中通过Intent启动另一个App的组件的时候,让另一个App的组件接收并处理该Intent。

需要注意的是,为了确保App的安全性,我们应该总是使用显式Intent去启动Service并且不要为该Service设置任何的Intent Filter。通过隐式的Intent启动Service是有风险的,因为你不确定最终哪个App中的哪个Service会启动起来以响应你的隐式Intent,更悲催的是,由于Service没有UI的在后台运行,所以用户也不知道哪个Service运行了。

Intent组成

MIME TYPE

在学习Intent的组成元素之前,先来了解一下基本知识:MIME TYPE。

什么是MIME TYPE

MIME type的缩写为(Multipurpose Internet Mail Extensions)代表互联网媒体类型(Internet media type),MIME使用一个简单的字符串组成,最初是为了标识邮件Email附件的类型。媒体类型通常是通过 HTTP 协议,由Web服务器告知浏览器的,更准确地说,是通过 Content-Type 来表示的,例如:

Content-Type: text/HTML

MIME类型能包含视频、图像、文本、音频、应用程序等数据。

MIME TYPE组成

通常只有一些在互联网上获得广泛应用的格式才会获得一个 MIME Type,如果是某个客户端自己定义的格式,一般只能以 application/x- 开头。例如application/vnd.ms-excel指定了Microsoft Excel文件类型。

每个MIME类型由两部分组成,前面是数据的大类别,例如声音audio、图象image等,后面定义具体的种类。

常见的MIME Type请参考MIME 参考手册

MIME Type通配符

标准的MIME Type中为同一种资源的不同格式指定了不同的MIME Type。例如图片资源MIME Type有,image/bmp、image/cis-cod、image/gif、image/ief、image/jpeg、image/pipeg...如果需要指定大致的类型,比如指定所有图片资源,就可以使用通配符*,构成image/*。

MIME Type作用

在把输出结果传送到浏览器上的时候,浏览器必须启动适当的应用程序来处理这个输出文档。这可以通过MIME Type来判断启动哪个程序来处理。在HTTP中,MIME类型被定义在Content-Type header中

Android MIME Type

Android MIME Type类型

  • 标准的MIME Type

    <data android:mimeType="image/jpeg ">

  • 自定义

    <data android:mimeType="vnd.android.cursor.item/vnd.google.note"/>

<data />属性会在后面的Intent组成元素中介绍。自定义的MIME Type格式也是有要求的,自定义的方式涉及到ContentProvider,所以下次再仔细阅读。

MIME Type参考

认识安卓中的MIME Type

什么是 MIME TYPE?

android中用到的MimeType的处理方式

Intent组成部分

Android可以根据Intent所携带的信息去查找要启动的组件,Intent还携带了一些数据信息以便要启动的组件根据Intent中的这些数据做相应的处理。

Intent由6部分信息组成:Component Name、Action、Data、Category、Extras、Flags。可以根据作用分为以下三种:

  • Component Name、Action、Data、Category,决定了Android会启动哪个组件,其中Component Name用于在显式Intent中使用,Action、Data、Category、Extras、Flags用于在隐式Intent中使用。
  • Extras,里面包含了具体的用于组件实际处理的数据信息。
  • Flags,其是Intent的元数据,决定了Android对其操作的一些行为。

Component Name

要启动的组件的名称。一般在显示Intent中,指定要启动的组件类文件参数,就是Component Name。如果没有设置component name,那么该Intent就是隐式的,Android系统会根据其他的Intent的信息(例如action、data、category等)做一些比较判断决定最终要启动哪个组件。所以,如果你启动一个你自己App中的组件,你应该通过指定component name通过显式Intent去启动它(因为你知道该组件的完整类名)。

需要注意的是,当启动Service的时候,你应该总是指定Component Name。否则,你不确定最终哪个App的哪个组件被启动了,并且用户也看不到哪个Service启动了。

Component Name参数指定

component name在Intent中对应的field是ComponentName对象,你可以通过要启动的组件的完整类名(包括应用的包名)指定该值,例如com.example.ExampleActivity。你可以通过Intent的setComponent()方法、setClass()方法、setClassName()方法或Intent的构造函数指定component name。

Action

是表示了要执行操作的字符串,比如查看或选择,其对应着Intent Filter中的action标签<action />

Action定义

Action可以选用预定义的,也可以自定义。Intent类和Android中其他framework级别的一些类也提供了许多已经定义好的具有一定通用意义的action。
自定义是指定独有的action以便于你的App中的Intent的使用或其他App中通过Intent调用你的App中的组件。

如果自定义了action,请务必将App的包名作为该action的前缀,这是一种良好的编程习惯,避免造成混淆。

设定Action

通过调用intent对象的setAction()方法或在Intent的构造函数中指定intent的action。注意Intent只能由一个action。

Data

Intent中的data指的是Uri对象和数据的MIME类型,其对应着Intent Filter中的data标签<data />

Uri

一个完整的Uri由scheme、host、port、path组成,格式是<scheme>://<host>:<port>/<path>。Uri就像一个数据链接,组件可以根据此Uri获得最终的数据来源。通常将Uri和action结合使用,比如我们将action设置为ACTION_VIEW,我们应该提供将要被编辑修改的文档的Uri。

Android预定义的schema有很多,content协议、http协议、file协议,还有tel协议表示打电话,geo协议表示地理位置...

MIME Type

Android就会基于Uri和MIME类型将Intent传递给符合条件的组件。例如指定了MIME Type之后,不让Android误将一个含有视频Uri的Intent对象传递给一个只能显示图片的Activity。

如果Uri使用的是content:协议,那么这就说明Uri所提供的数据将来自于本地设备,即数据由ContentProvider提供,这种情况下Android会根据Uri自动推断出MIME类型,此种情况我们无需再自己指定MIME类型。因为此Uri的类型可以通过provider的getType(Uri)方法得到。

设置Data

如果只设置数据的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。

<category android:name="android.intent.category.DEFAULT" />在调用startActivity()或者startActivityForResult()方法时会自动给参数Intent添加category.DEFAULT。所以要在相应的IntentFilter中添加该category。

Extras

额外的数据信息。Intent中有一个Bundle对象存储着各种键值对,接收该Intent的组件可以从中读取出所需要的信息以便完成相应的工作。有的Intent需要靠Uri携带数据,有的Intent是靠extras携带数据信息。

定义Extra的键值

Intent类里面也指定了很多预定义的EXTRA_*形式的extra。同时也可以声明自己自定义的extra,请确保App的包名作为你的extra的前缀。

设置Extra

通过调用Intent对象的各种重载的putExtra(key, value)方法向Intent中加入各种键值对形式的额外数据。你也可以直接创建一个Bundle对象,向该Bundle对象传入很多键值对,然后通过调用Intent对象的putExtras(Bundle)方法将其一块设置给Intent对象中去。

Flags

flag就是标记的意思,Intent类中定义的flag能够起到作为Intent对象的元数据的作用。这些flag会告知Android系统如何启动Activity(例如,新启动的Activity属于哪个task)以及在该Activity启动后如何对待它(比如)。更多信息可参见Intent的setFlags()方法。

实践

mImageUri = Uri.fromFile(outPutImage);
Intent intent = new Intent();
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
startActivityForResult(intent, TAKE_PHOTO);

imageUri指定了图片存储的Uri,作为extra,让相机知道拍照完成后照片的存储位置信息。

使用隐式Intent最佳方式

当使用隐式Intent来启动一个组件时,如果Android系统没有相应的组件,会抛出异常。所以有必要在启动组件时,先去判断一下Android系统有无至少一个符合Intent条件的组件。判断方式有两种:PackageManager和Intent两个类中的方法。

两种方法会让Android根据该sendIntent找到潜在的适合启动的组件的信息,并以ResolveInfo类的对象的形式返回结果,如果返回null,表示当前系统中没有任何组件可以接收并处理该Intent。如果返回不是null,就表明系统中至少存在一个组件可以接收并处理该Intent。

PackageManager

PackageManager pm = getPackageManager();
        //参数flag为什么赋予0,不知道
        if(pm.resolveActivity(intent, 0) != null) {
            startActivity(intent);
        }

参数Flag用于指定寻找方式,文档中有一句话:The most important is MATCH_DEFAULT_ONLY, to limit the resolution to only those activities that support the CATEGORY_DEFAULT.指定MATCH_DEFAULT_ONLY flag用于寻找含有CATEGORY_DEFAULT的category的组件。

平时用这种方法来判断有无符合条件的组件,flag一般设置为0,不知道为什么。我个人的理解可能是,多个flag是通过位或来组成一个flag,这里给flag设置为0,当与原始的flag进行位或运算时,不改变原始的flag,也就是保持了默认。如有误,请指出,纯属瞎YY。

Intent

if(intent.resolveActivity(getPackageManager()) != null) {
            startActivity(intent);
        }

App Chooser

当使用隐式Intent时,Android系统中可能有多个符合该Intent条件的组件。第一次打开时会弹出一个选择器供用户选择,用户可以勾选始终让其中一个作为默认启动组件。这样下次再启动时,就会默认打开选择的组件。但是,如果用户想分享某个文件时,不一定只会用一个应用来分享,所以这种情况下我们应该强制每一次都打开选择器,让用户选择。

intent = Intent.createChooser(intent, "Choose WebKit");
        if(intent.resolveActivity(getPackageManager()) != null) {
            startActivity(intent);
        }

这里调用Intent类中静态方法createChooser()来创建一个选择器Intent,每一次都去启动该Intent。

Intent过滤

设定好隐式Intent的各项参数后,需要和所有组件的IntentFilter中各项参数匹配,找到符合条件的组件。

IntentFilter

即Intent过滤器,一个组件可以包含0个或多个Intent Filter。Intent Filter是写在App的manifest文件中的,其通过设置action或uri数据类型等指明了组件能够处理接收的Intent的类型。如果你给你的Activity设置了Intent Filter,那么这就使得其他的App有可能通过隐式Intent启动你的这个Activity。

当Android系统接收到一个隐式Intent要启动一个Activity(或其他组件)时,Android会根据以下三个信息比较Intent的信息与注册的组件的intent-filter的信息,从而为该Intent选择出最匹配的Activity(或其他组件):

  • intent中的action
  • intent中的category
  • intent中的data(包含Uri以及data的MIME类型)

也就是隐式intent对象要分别满足要启动的目标组件中注册的intent-filter中的<action />、<category />、<data />三个标签中的信息。

IntentFilter示例

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>

        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>

<activity android:name=".activity.ImplicitActivity">
    <intent-filter>
        <action android:name="android.example.com.intentusage.ACTION_START"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>

<activity android:name=".activity.TakePhotoActivity">
    <intent-filter>
        <action android:name="android.example.com.intentusage.ACTION_SHOW_PIC"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>

Action测试

  1. 要想让intent对象通过action测试,那么intent-filter中声明的action不能为空且要包含intent对象中的action值(如果intent的action值不为空的话)。
  2. 如果intent-filter没有声明任何action,那么所有的intent的对象(即无论intent如何配置)都无法通过intent-filter的action测试。

Category测试

  1. 如果intent对象不包含任何category,并且该intent不是用来启动Activity的,那么该intent对象总是能通过所有任意的intent-filter的category测试;
  2. 如果intent对象包含category(至少一个),那么只有当intent-filter中声明的category全部包含intent对象中的所有category的时候才通过category测试。
  3. 如果允许Activity被隐式的Intent启动,那么我们必须在该Activity的intent-filter中声明值为android.intent.category.DEFAULT的category。

android.intent.category.DEFAULT的category,这是因为当我们把一个隐式的intent传递给startActivity()或startActivityForResult()方法时,Android会自动给该隐式intent添加值为android.intent.category.DEFAULT的category,所以为了能让intent-filter包含intent中全部的category,我们就需要在Activity的intent-filter中添加该category,在使用时需要特别注意。

Data测试

在IntentFilter中设置data属性的uri值时,使用uri的通配符*#。其中*匹配任意字符串,#匹配任意数字。

参考博客Android中Intent对象与Intent Filter过滤匹配过程详解

参考

Android中Intent概述及使用

Android中Intent对象与Intent Filter过滤匹配过程详解

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容