[Android基础]Intent用法的二三事(上)——Componet、Action、Category的属性详解

Intent在英语中的意思是意图,android中的日常使用就是通过intent语句去表达不同的操作意图,让系统知道我们的意图之后完成一系列的操作,例如启动Activity、Service、广播等等。


Intent中包含7大属性,分别是ComponentName、Action、Category、Data、Type、Extra以及Flag,每个属性都有其不同的作用。按照分类可以归为以下三种:

用于启动:ComponetName(显式)、Action(隐式)、Category(隐式)。

用于传递数据:Data(隐式),Type(隐式),Extra(隐式、显式)。

用于规定启动模式:Flag。


一、Intent启动:

在Intent中,我们最常使用也是最基础的操作就是启动一个Activity。下面就以启动Activity作为例子分析一下ComponetName、Action和Category的用法。


1.显式启动

通过Intent的ComponentName可以显式启动一个Activity。ComponentName的实例需要传入参数,有:

(String pkg, String cls)、

(Context pkg, String cls)、

(Context pkg, Class<?> cls)、

(Parcel in) 

四种选择。ComponentName的第一个参数是要被启动的Activity所属的包的名字或者Context,第二个参数则是被启动的Activity的全称(包名加类名)或者class文件。这几种用法令Intent显式启动就可以启动同一个app中另一个Actvivity,也可以启动不同app中的Activity。下面来看看用法示例:


1.1 启动同一个应用的Activity

当前这个应用我命名为Intent_exercise_01。在这个应用中我们新建两个Activity,分别是MainActivity和Aty1,两个Activity所在的包名为package="com.example.tahlia.intent_exercise_01"。布局界面如下。



修改MainActivity,为3个Button添加响应事件:

@Override
    public void onClick(View view) {
        Intent intent = new Intent();
        switch (view.getId()){
            case R.id.BtnStartAty1SS:
                intent.setComponent(new ComponentName("com.example.tahlia.intent_exercise_01", "com.example.tahlia.intent_exercise_01.Aty1"));
                startActivity(intent);
                break;
            case R.id.BtnStartAty1CS:
                intent.setComponent(new ComponentName(this, "com.example.tahlia.intent_exercise_01.Aty1"));
                startActivity(intent);
                break;
            case R.id.BtnStartAty1CC:
                intent.setComponent(new ComponentName(this, Aty1.class));
                startActivity(intent);
                break;
        }
    }

运行后,三个按钮都能顺利启动Aty1。

和使用Component这个写法等同效果的还有:

Intent intent = new Intent(MainActivity.this, Aty1.class);
startActivity(intent);

intent.setClass(this, Aty1.class);
startActivity(intent);



1.2 启动不同应用中的Activity

在同一个工程之中建立一个新的模块Module,在这个module下新建两个Activity,分别是Aty2和Aty3,其中Aty2是这个模块应用的主活动。布局如下:



这两个Activity是在一个我命名为TheOtherApp里面的活动,而一开始的MainActivity和Aty1是在另一个app中的活动。也就是说这四个活动是在两个不同的app里面的。

修改一下Intent_exercise_01中的布局,添加两个按钮用于启动Aty2和Aty3:



修改Intent_exercise_01中MainActivity的代码,添加代码,点击button从Intent_exercise_01中启动theOtherApp的Aty2和Aty3:

            case R.id.BtnStartAty2:
                intent.setComponent(new ComponentName("com.example.tahlia.theotherapp", "com.example.tahlia.theotherapp.Aty2"));
                startActivity(intent);
                break;
            case R.id.BtnStartAty3:
                intent.setComponent(new ComponentName("com.example.tahlia.theotherapp", "com.example.tahlia.theotherapp.Aty3"));
                startActivity(intent);
                break;

完成后运行一下,点击BtnStartAty2或者BtnStartAty3,发现程序崩溃了。



查看日志,发现报这个错误:

FATAL EXCEPTION: main
Process: com.example.tahlia.intent_exercise_01, PID: 5644
android.content.ActivityNotFoundException: Unable to find explicit activity class {com.example.tahlia.theotherapp/com.example.tahlia.theotherapp.Aty2}; have you declared this activity in your AndroidManifest.xml?


也就是说,无法找到对应Activity的class文件。这个错误很好解决,因为我们新建了一个theOtherApp的应用之后,并没有在模拟器上安装,因此模拟器中目前还没有这个Activity的存在(因为这个应用不存在)。因此先在模拟器上运行一次theOtherApp,确保模拟器中已经安装了要被启动的Activity所在的应用后,我们再次运行Intent_exercise_01,点击启动Aty2和Aty3。效果分别如下:



可以看到,启动Aty2时一切正常,而启动Aty3时程序就会崩溃。报错如下:

FATAL EXCEPTION: main
Process: com.example.tahlia.intent_exercise_01, PID: 14015
java.lang.SecurityException: Permission Denial: starting Intent { cmp=com.example.tahlia.theotherapp/.Aty3 } from ProcessRecord{fddb6eb 14015:com.example.tahlia.intent_exercise_01/u0a77} (pid=14015, uid=10077) not exported from uid 10078


报错提示:安全性错误。

解决方法:将Aty3的manifest文件注册语句添加一句允许导出:

<activity android:name=".Aty3"
            android:exported="true">
        </activity>

这个属性的意思是允许这个活动被其他程序访问。如果没有这一句,exported属性默认为false,即不可被外部程序访问。

而Aty2由于作为一个应用的主Activity,在manifest文件<activity>标签中包含了过滤器<intent-filter>,对于包含了过滤器的组件,系统默认exported为true,可以被外部程序访问。这就是Aty2能被成功启动而Aty3会出错的原因了。


2.隐式启动

Intent的隐式启动方法是指不通过目标组件名字,而是通过intent-filter过滤器的设置,进行组件的匹配和解析后,由Android决定把这个Intent交给谁去处理。在<intent-filter>中,我们一般通过action和category的值进行设置和匹配。


2.1 action的匹配规则

action是一个用户定义的字符串,用来描述当前定义action的组件,我把它理解为是Activity的一个名字。action的匹配规则有两个:

1.对于设置了action的Activity来说,如果使用隐式启动这个Activity,就必须匹配到他的action。

2.一个Activity允许设置多个action,在匹配的时候只需要匹配到其中一个就可以了。

这个规则可以这么理解:有个人的名字是王五,大家都叫他老王,也有叫王胖子的。如果想要和他打招呼,叫“王五”、“老王”、“王胖子”都可以得到他的回应。但是如果喊他老张,他就不会给任何的回应,因为他不是老张。


使用一段代码来加深了解action的匹配规则。新建工程后新建一个准备被启动的Activity,命名为Aty1。MainActivity和Aty布局如下,通过点击MainActivity中的button隐式启动Aty。



由于通过action使用隐式启动,因此先给Aty1添加action属性值。action实际上是一个用户自定义的区分大小写的字符串,因此使用任何字符串都可以。但为了更好地区分不同的类,约定俗成的写法是“包名+intent.action.类名”。有时候这个写法会让action的值较长,为了减少差错,也会在被启动的类中声明一个静态常量用于存放action的值。

	<activity android:name=".Aty1">
            <intent-filter>
                <action android:name="abcdefghijklmnopqrstuvwxyz"/>
                <action android:name="com.example.tahlia.intent_exercise_02.intent.action.Aty1"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>

在Aty1类中定义静态常量:

public static final String ACTION_START = "com.example.tahlia.intent_exercise_02.intent.action.Aty1";

在使用Intent进行隐式启动时,可以通过setAction的方法设置action,也可以通过Intent构造函数传入action。两者写法效果是一样的。给MainActivity的button注册监听事件:

@Override
    public void onClick(View view) {
        Intent intent;
        switch (view.getId()){
            case R.id.BtnStartAty1_01:
                intent = new Intent();
                intent.setAction("abcdefghijklmnopqrstuvwxyz");
                startActivity(intent);
                break;
            case R.id.BtnStartAty1_02:
                intent = new Intent("com.example.tahlia.intent_exercise_02.intent.action.Aty1");
                startActivity(intent);
                break;
            case R.id.BtnStartAty1_03:
                intent = new Intent(Aty1.ACTION_START);
                startActivity(intent);
                break;
            case R.id.BtnStartAty1_04:
                intent = new Intent("test");
                startActivity(intent);
                break;
        }
    }

运行程序,看看效果:



发现通过setAction传入“abcdefghijklmnopqrstuvwxyz”、通过Intent构造方法传入“com.example.tahlia.intent_exercise_02.intent.action.Aty1”、通过Intent构造方法传入Aty1类的静态常量ACTION_START均能成功启动Aty1。传入“test”后程序崩溃并报错:

FATAL EXCEPTION: main
Process: com.example.tahlia.intent_exercise_02, PID: 17013
android.content.ActivityNotFoundException: No Activity found to handle Intent { act=test }


由于Aty1的action并没有“test”的值,因此在Android解析中找不到匹配的类去启动,因此也就崩溃了。


※要注意的是,一旦使用了<intent-filter>,标签内一定要定义<category android:name="android.intent.category.DEFAULT"/>,否则程序将会报错。由于在通过Intent调用Activity时,我们不主动使用intent.addCategory添加category,Android就会默认自动添加“android.intent.category.DEFAULT”,因此在<intent-filter>配置中category_DEFAULT是不可缺少的。


不同的Activity业可以配置同一个action值。使用Intent通过action去启动Activity时,Android通过过滤器对action进行匹配筛选,就会得到多个Activity(他们同时都拥有一个action值)。如下:



另外,当我们配置action的时候,系统也会提供常用的属性值提示,以下举例部分:

ACTION_MAIN

Android Application的入口,每个APP必须且只能有一个Activity包含一个此类型的Action声明。

ACTION_DIAL

打开系统默认的拨号程序,如果Data中设置了电话号码,则自动在拨号程序中输入此号码。

ACTION_CALL

直接拨打Data设置好的电话号码

ACTION_VIEW

根据不同的Data类型,显示不同的数据

ACTION_SEND

由用户指定发送方式进行数据发送操作



2.2 Category的匹配规则

category也是一个用户自定义的字符串。但和action不一样的是,它可以在intent中通过intent.addCategory设置多个category的值。在匹配时,通过过滤器寻找到的Activity必须包含intent设置的所有category的值。

比如通过intent设置:

intent.addCategory("category1");
intnet.addCategory("category2");
那么能够被启动的Activity的category中就 必须包含“category1”“category2”和“android.intent.category.DEFAULT”这三个category属性值。

※category_DEFAULT是隐式启动一个Activity必不可少的一个属性值。使用隐式启动Activity,Android会默认在隐式启动的intent中加入intent.addCategory(“android.intent.category.DEFAULT”);这一句,表示Android会把它当成Activity进行处理执行,因此对于Activity,我们都应该主动加上category_DEFAULT这一句。(app的入口活动除外,当然也可以手动添加进去,不添加也不会出错)

下面用例子来加深一下category的匹配印象。新建一个工程,在MainActivity以外新建4个Activity,分别命名为Aty1,Aty2,Aty3,Aty4。在manifest文件中对他们4个activity都设置同一个action和不同的category如下:

	<activity android:name=".Aty1" >
            <intent-filter>
                <action android:name="com.example.tahlia.intent_exercise_03.intent.action.Aty"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>
        <activity android:name=".Aty2" >
            <intent-filter>
                <action android:name="com.example.tahlia.intent_exercise_03.intent.action.Aty"/>
                <category android:name="myCategory"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>
        <activity android:name=".Aty3" >
            <intent-filter>
                <action android:name="com.example.tahlia.intent_exercise_03.intent.action.Aty"/>
                <category android:name="myCategory"/>
                <category android:name="otherCategory"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>
        <activity android:name=".Aty4">
            <intent-filter>
                <action android:name="com.example.tahlia.intent_exercise_03.intent.action.Aty"/>
                <category android:name="myCategory"/>
                <category android:name="otherCategory"/>
                <category android:name="ourCategory"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>

在MainActivity的布局文件中添加布局如下:



对应地注册监听事件:

@Override
    public void onClick(View view) {
        Intent intent = new Intent("com.example.tahlia.intent_exercise_03.intent.action.Aty");
        switch(view.getId()){
            case R.id.BtnStartAction1:
                startActivity(intent);
                break;
            case R.id.BtnStartAction2:
                intent.addCategory("myCategory");
                startActivity(intent);
                break;
            case R.id.BtnStartAction3:
                intent.addCategory("myCategory");
                intent.addCategory("otherCategory");
                startActivity(intent);
                break;
            case R.id.BtnStartAction4:
                intent.addCategory("myCategory");
                intent.addCategory("otherCategory");
                intent.addCategory("ourCategory");
                startActivity(intent);
                break;
        }
    }

现在运行一下,查看效果:


从这个结果能看到,点击第一个按钮时,category匹配条件只要求含有category_DEFAULT,对于四个activity来说都含有这个category的值,因此都能匹配上。第二个按钮多添加了一个myCategory的匹配要求,因此除了Aty1以外都含有myCategory的值,Aty2、Aty3、Aty4能匹配上。以此类推,就能很好理解category的匹配要求了。


另外,当我们配置category的时候,系统也会提供常用的属性值提示,以下举例部分:

CATEGORY_DEFAULT

Android系统中默认的执行方式,按照普通Activity的执行方式执行

CATEGORY_HOME

设置该组件为Home Activity

CATEGORY_LAUNCHER

设置该组件为在当前应用程序启动器中优先级最高的Activity,通常为入口ACTION_MAIN配合使用。

CATEGORY_BROWSABLE

设置该组件可以使用浏览器启动。



*****************************

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值