隐式Intent启动Activity

2018-06-06  本文已影响0人  健忘仙森

前言

我们在开发一个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必须满足如下条件:

所以,只要一个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. 什么时候配置自己的组件支持隐式启动?

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

上一篇下一篇

猜你喜欢

热点阅读