美文网首页
隐式Intent启动Activity

隐式Intent启动Activity

作者: 健忘仙森 | 来源:发表于2018-06-06 20:52 被阅读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必须满足如下条件:

    • 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. 什么时候配置自己的组件支持隐式启动?

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

    相关文章

      网友评论

          本文标题:隐式Intent启动Activity

          本文链接:https://www.haomeiwen.com/subject/uauisftx.html