隐式Intent启动Activity

前言

我们在开发一个app时,最常用到的是通过Intent设置组件类名,然后启动相应的Activity。这种通过为Intent指定组件名称启动Activity的方式,我们称之为显示Intent启动Activity。举个例子:

Intent explicitIntent = new Intent();
//通过类启动(一般用于同一个应用)
explicitIntent.setClass(context, MainActivity.class);
//通过ComponentName类启动(一般用于同一组织开发的同一个或不同应用)
explicitIntent.setComponent(new ComponentName(("com.thoughtworks.demo","com.thoughtworks.demo.MainActivity"));
//通过包名+完整类名启动(ComponentName类启动的一种简写调用)
explicitIntent.setClassName("com.thoughtworks.demo","com.thoughtworks.demo.MainActivity");
startActivity(explicitIntent);

因为显示Intent启动Activity需要知道具体的类名包名,所以常用于同一个应用内,或者同一个组织开发的不同应用间。

正文

在了解了显示Intent启动后,我们来说下今天的主题,隐式Intent启动Activity。

什么是隐式Intent启动Activity?

通过设置Intent的action、category和data来启动Actvity的方式就是隐式Intent启动。举个例子:

Intent implicitIntent = new Intent();
implicitIntent.setAction("com.thoughtworks.demo.itent.action.EDIT_IMAGE");
implicitIntent.setData(Uri.parse(" content://com.thoughtworks.provider.userprovider/user/1?image=avatar"));
startActivity(implicitIntent);

隐式Intent启动的是哪个Activity?

具体此Intent能启动哪个Activity,需要看Activity在AndroidManifest.xml中的配置。下面给出一个配置,来具体分析隐式Intent启动Activity。

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="com.thoughtworks.demo.intent.action.EDIT_IMAGE" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="content" />
        <data android:host="com.thoughtworks.provider.userprovider" />
        <data android:pathPattern="/user/.*" />
    </intent-filter>
</activity>

隐式Itent启动MainActivity必须满足如下条件:

  • Intent-filter内必须至少包含一个categoryandroid:nameandroid.intent.category.DEFAULT

  • Intent-filter内必须至少包含一个action

    intent内是否需要setAction根据setData中Uri的scheme决定(如果Uri的scheme为http, https, content等,这时候不指定intent的action,系统或者其他应用会尝试启动自己可以匹配的组件),为了能准确启动需要的组件,请在intent中调用setAction。

所以,只要一个Activity的Manifest配置满足上面2个条件,那么这个Activity一定是可以被隐式启动的。至于怎么启动,这就需要结合Intent内设置的action、category、data和AndroidManifest文件中Activity的intent-filter的匹配规则决定了。

是不是感觉好麻烦?要不你也不会看到这篇文章,下来我就说下intent-filter的匹配规则,其实很简单。

intent-filter的作用和特点

看名字就知道intent-filter的作用当然就是用来过滤intent的了,本质目的就是为了确定哪个intent可以和当前的组件匹配,如果匹配就启动它。所以intent-filter的作用就是为了隐式启动组件

intent-filter的特点有3个,如下:

  1. 一个组件内可以有多组intent-filter标签。

  2. 只要intent内的信息和任意一组intent-filter匹配,那么就可以启动这个组件。

  3. 一组intent-filter内可以多个action、category、data。

    一般情况不会将多个action、category、data同时写在一组intent-filter标签内,而是会根据具体action,拆分为多组intent-filter。

intent-filter的匹配

还是举个例子:

intent信息:

Intent implicitIntent = new Intent();
implicitIntent.setAction("com.thoughtworks.demo.itent.action.EDIT_IMAGE");
//implicitIntent.setAction("com.thoughtworks.demo.itent.action.SHOW_IMAGE");
startActivity(implicitIntent);

intent-filter信息:

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="com.thoughtworks.demo.intent.action.SHOW_IMAGE" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
    <intent-filter>
        <action android:name="com.thoughtworks.demo.intent.action.EDIT_IMAGE" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

implicitIntent只能有一个action(看名字就是知道setAction嘛),这个action不管是SHOW_IMAGE还是EDIT_IMAGE都会启动MainActivity。

下面我们修改下intent-filter信息:

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="com.thoughtworks.demo.intent.action.SHOW_IMAGE" />
        <action android:name="com.thoughtworks.demo.intent.action.EDIT_IMAGE" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

效果和之前的intent-filter完全一样。如果MainActivity只能负责show和edit用户的图片。我们要怎么办?我们再修改下intent-filter信息:

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="com.thoughtworks.demo.intent.action.SHOW_IMAGE" />
        <action android:name="com.thoughtworks.demo.intent.action.EDIT_IMAGE" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="content" />
        <data android:host="com.thoughtworks.provider.userprovider" />
        <data android:pathPattern="/user/.*" />
    </intent-filter>
</activity>

所以要启动MainActivity,intent信息如下:

Intent implicitIntent = new Intent();
implicitIntent.setAction("com.thoughtworks.demo.itent.action.EDIT_IMAGE");
//implicitIntent.setAction("com.thoughtworks.demo.itent.action.SHOW_IMAGE");
implicitIntent.setData(Uri.parse(" content://com.thoughtworks.provider.userprovider/user/1?image=avatar"));
startActivity(implicitIntent);

在MainActivity中通过getData获取contentProvider的Uri地址查询user信息,在通过Uri中的query获取avatar图片完成show或edit操作。

如果intent中的data不能和intent-filter中data的规则匹配,是无法启动组件的。

如果需求在复杂一点,MainActivity不仅仅可以对ContentProvider中user的图片做处理,还可以对指定域名下的网络图片做处理。我们要怎么办?我们继续修改intent-filter信息:

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="com.thoughtworks.demo.intent.action.SHOW_IMAGE" />
        <action android:name="com.thoughtworks.demo.intent.action.EDIT_IMAGE" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="content" />
        <data android:host="com.thoughtworks.provider.userprovider" />
        <data android:pathPattern="/user/.*" />
        
        <data android:scheme="http" />
        <data android:host="lovemumu.cn" />
        <data android:pathPattern="/assets/images/hero/.*\\.jpg" />
    </intent-filter>
</activity>

intent信息为:

Intent implicitIntent = new Intent();
implicitIntent.setAction("com.thoughtworks.demo.itent.action.EDIT_IMAGE");
implicitIntent.setData(Uri.parse("http://lovemumu.cn/assets/images/hero/Geirangerfjord-Norway.jpg"));
startActivity(implicitIntent);

上面的intent一样可以启动MainActivity。But!!有一个问题,如果我们把intent中的data设置为:

implicitIntent.setData(Uri.parse("content://lovemumu.cn/user/1?image=avatar"));

会发生什么?我操,既然也打开了MainActivity。这显然不是我们想要的。所以此时我们应该拆分intent-filter,代码如下:

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="com.thoughtworks.demo.intent.action.SHOW_IMAGE" />
        <action android:name="com.thoughtworks.demo.intent.action.EDIT_IMAGE" />
        <category android:name="android.intent.category.DEFAULT" />     
        <data android:scheme="http" />
        <data android:host="lovemumu.cn" />
        <data android:pathPattern="/assets/images/hero/.*\\.jpg" />
    </intent-filter>
    <intent-filter>
        <action android:name="com.thoughtworks.demo.intent.action.SHOW_IMAGE" />
        <action android:name="com.thoughtworks.demo.intent.action.EDIT_IMAGE" />
        <category android:name="android.intent.category.DEFAULT" /> 
        <data android:scheme="http" />
        <data android:host="lovemumu.cn" />
        <data android:pathPattern="/assets/images/hero/.*\\.jpg" />
    </intent-filter>
</activity>

说了这么多,大家有木有发现,我们仅仅只提到过一次category,即:Intent-filter内必须至少包含一个categoryandroid:name为android.intent.category.DEFAULT。

呢到底category重要吗?看下官网的解释:

A string containing additional information about the kind of component that should handle the intent. Any number of category descriptions can be placed in an intent,but most intents do not require a category.

大多数情况是不需要catagory的。好吧,看来用的时候不多。但是我们还是要简单说说它。

category在intent-filter内可以配置多个。如果intent内有category,那么这些categories都必须包含在intent-filter内,否则无法隐式启动组件。如果intent内没有任何category,只要其他规则匹配(action,data),就可以启动组件。

intent-filter匹配规则总结

以下规则中intent-filter都必须包含<category android:name="android.intent.category.DEFAULT" />

  1. 如果intent-filter内只有action(单个或多个),只要intent内的action和intent-filter内任意一个action匹配,即可启动组件。
  2. 如果intent-filter内有action和category(单个或多个),满足规则1的条件下,只要intent内的categories都属于intent-filter,即可启动组件。
  3. 如果intent-filter内有action、category、data(单个或多个),满足规则1,2的条件下,只要intent内的data和intent-filter内的规则匹配,即可启动组件。

关于action、category、data的理解

action:对于intent,action表示你期望完成什么操作。而对于组件,intent-filter内的action表示你的组件具有什么样的能力。例如:如果你的activity的intent-filter配置了android.intent.action.SEND的action,表示你的activity具有处理SEND的能力。SEND的是什么,可以根据data的mineType指定。而Inent的setAction设置了android.intent.action.SEND,表示你希望去SEND一个消息,具体是发送什么消息(文本,图片,音视频),通过intent的setType指定。而谁会去真的帮你SEND,那就要看哪些应用的组件配置了对应的action和data。其实android.intent.action.SEND是系统的action。很多第三方应用想要共享自己应用的能力,他们可以通过公开文档的形式告诉你action,但是大多数是直接使用系统的action。举个例子:

intent信息为:

Intent implicitIntent = new Intent();
implicitIntent.setAction("android.intent.action.SEND");
implicitIntent.putExtra(Intent.EXTRA_TEXT, "I am Twer~");
implicitIntent.setType("text/plain");
startActivity(implicitIntent);

intent-filter信息:

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <data android:mimeType="text/plain" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

此时系统会弹出应用或组件选择器,因为很多应用的组件都匹配到了你intent。例如微信会帮你SEND信息到好友或者群聊在或者朋友圈,邮件会帮你SEND信息到发送页面,百度会帮你SEND到搜索结果页面,等等。

category:虽然说他不是很重要,但是它也有自己的应用场景。最常用的2个系统category如下:
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.BROWSABLE"/>

LAUNCHER:这个不用多说,我们的项目的入口Activity都必须有的配置。它表示该 Activity 是任务的初始 Activity,在系统的桌面列出图标。

BROWSABLE:表示该 Activity 允许自身通过网络浏览器启动。举个例子:

intent-filter信息:

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="app" />
        <data android:host="thoughtworks"/>
        <data android:pathPrefix="/behring"/>
    </intent-filter>
</activity>

此时你在手机浏览器访问连接app://thoughtworks/behring 就可以拉起App的MainActivity。

android.intent.category.BROWSABLE必须配合android.intent.action.VIEW还有data一起使用。

data:主要是用来传递Uri类型的数据,可以通过?key=value&key=value的形式传递参数。包含的属性有schemehostportpathmimeTypepathPrefixpathPattern

对应内容如下图:

intent-filter-data.png

详细内容请参考

需要注意的是,若要同时给intent设置 URI 和 MIME 类型,请勿调用 setData()setType(),因为它们会互相抵消彼此的值。请始终使用 setDataAndType() 同时设置 URI 和 MIME 类型。

总结

了解了上面的知识后,我有2个问题。

  1. 什么时候使用隐式Intent启动组件?

    当你需要调用其他应用的能力,包括系统能力和第三方应用能力。例如:调用拨号盘,调用微信分享,调用美图秀秀处理图片等等。

  2. 什么时候配置自己的组件支持隐式启动?

    需要对外暴露自己组件的能力。例如:京东和淘宝提供”拍立得“接口,可以直接调用他们的该组件完成图片搜索购物。

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

推荐阅读更多精彩内容