美文网首页
Activity知识点集合二

Activity知识点集合二

作者: 一枚平庸的软件工程师 | 来源:发表于2019-05-27 10:48 被阅读0次

    Activity的启动模式

    启动模式分为四种,standard、singleTop、singleTask、singleInstance;详情如下图: 启动模式详情.png

    在singleTask详解中,有提到Activity所需任务栈,那么什么是Activity所需要的任务栈呢?
    要从TaskAffinity参数说起,可以翻译为任务相关性。这个参数标识了一个Activity所需要的任务栈的名字,默认情况下,所有Activity所需任务栈的名字为应用的包名。当然,我们可以为每个Activity都单独指定TaskAffinity属性,这个属性值必须不能和包名相同,否则就相当于没有指定。TaskAffinity属性主要和singleTask启动模式或者allowTaskReparenting属性配对使用,在其他情况下,没有意义。另外,任务栈分为前台任务栈和后台任务栈,后台任务栈中的Activity位于暂停状态,用户可以通过切换将后台任务栈再次调到前台。
    当TaskAffinity和singleTask启动模式配对使用的时候,它是具有该模式的Activity的目前任务栈的名字,待启动的Activity会运行在名字和TaskAffinity相同的任务栈中。
    当TaskAffinity和allowTaskReparenting结合的时候,比较复杂,会产生特殊效果。举个例子,现在又两个应用A、B,B的allowTaskReparenting为true,A 启动了B的一个Activity C,然后按Home回到了桌面,然后再单击B的桌面图标,这个时候并不是启动了B的主Activity,而是重新显示了已经被A启动的Activity C,C从A任务栈转移到了B任务栈。当A启动B的时,B会创建自己的任务栈,当C发现原本需要的任务栈已经被创建了,所以C就转移了。

    如何给Activity指定启动模式?
    1.通过AndroidMenifest。

    <activity
                android:name=".view.PainActivity"
                android:label="@string/title_activity_pain"
                android:launchMode="singleTask"
                android:theme="@style/AppTheme.NoActionBar"/>
    

    2.通过Intent中设置标志位来为Activity指定启动模式。

    intent = new Intent(MainActivity.this,Handler_01.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(intent);
    

    两者区别:
    优先级:第二种方式的优先级要高于第一种;
    限定范围:第一种方式无法直接为Activity设定FLAG_ACTIVITY_CLEAR_TOP标识,而第二种无法为Activity指定singleInstance模式。

    查看Activity任务栈分配情况的命令:
    adb shell dumpsys activity

    Activity的Flags

    下面介绍几款比较常用的标记位:
    1.FLAG_ACTIVITY_NEW_TASK
    这个标记位的作用是为Activity指定singleTask启动模式,其效果和在XML中指定该启动模式相同。
    2.FLAG_ACTIVITY_SINGLE_TOP
    这个标记位的作用是为Activity指定singleTop启动模式,其效果和在XML中指定该启动模式相同。
    3.FLAG_ACTIVITY_CLEAR_TOP
    具有此标记位的Activity,当它启动时,在同一个任务栈中所有位于它上面的Activity都要出栈。
    4.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
    具有这个标记的Activity不会出现在历史Activity的列表中,当某些情况下我们不希望用户通过历史列表回到我们的Activity的时候这个标记比较有用。它等同于在XML中指定activity的属性android:excludeFromRecents="true"。

    IntentFilter的匹配规则

    启动Activity分为两种,隐式调用和显式调用,显式调用需要明确指定被启动对象的组件信息,包括包名和类名。而隐式调用则不需要明确指定组件信息。如果两者共存以显式调用为主。下面介绍一下隐式调用,隐式调用需要Intent能够匹配目标组件的IntentFilter中所设置的过滤信息,如果不匹配将无法启动目标Activity。
    为了匹配过滤列表,需要同时匹配过滤列表中的action、category、data信息,否则匹配失败。一个Activity可以有多个IntentFilter,一个Intent只要能匹配任何一组intent-filter即可成功启动应用的Activity。
    下面分析各种属性的匹配规则。

    1.action的匹配规则

    action的匹配要求Intent中的action存在且必须和过滤规则中的其中一个action相同,action区分大小写。

    2.category的匹配规则

    category的匹配要求Intent中可以没有category,但是一旦有,不管几个,每个都要能够和过滤规则中的任何一个category相同。为什么不设置也能匹配成功呢?因为系统在调用startActivity或者startActivityForResult的时候会默认加为Intent加上“android.intent.category.DEFAULT”这个category。所以为了我们activity能接受隐式调用,必须在intent-filter中指定“android.intent.category.DEFAULT”这个category。

    3.data的匹配规则

    data的匹配规则和action类似,如果过滤规则中定义了data,那么Intent中必须也要定义可匹配的data。在介绍data的匹配规则之前,我们需要先了解一下data的结构,因为data稍微有些复杂。
    data的语法:

    <data android:scheme="string"
        android:host="string"
        android:port="string"
        android:path="string"
        android:pathPattern="string"
        android:pathPrefix="string"
        android:mimeType="string"/>
    

    data由两部分组成,mimeType和URI。mimeType指媒体类型,比如image/jpeg、audio/mpeg4-generic和video/等,可以表示图片、文本、视频等不同媒体格式,而URI中包含的数据比较多,下面是URI的结构:
    <scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
    实际例子:
    content://com.example.project:200/folder/subfolder/etc
    http://www.baidu.com:80/search/info
    下面介绍下每个数据的含义:
    Scheme:URI的模式,比如http、file、content等,如果URI中没有指定scheme,那么整个URI的其他参数无效,这也意味着URI是无效的。
    Host:URI的主机名,比如www.baidu.com,如果host未指定,那么整个URI中的其他参数无效,这也意味着URI无效。
    Port:URI的端口号,比如80,仅当URI中指定了scheme和host参数的时候port参数才是有意义的。
    Path、pathPattern和pathPrefix:这三个参数表述路径信息,其中path表示完整的路径信息;pathPattern也表示完整的路径信息,但是它里面可以包含通配符“
    ”,“”表示0个或多个任意字符,需要注意的是,由于正则表达式的规范,如果想表示真实的字符串,那么“”要写成“\*",""要写成"\\";pathPrefix表示路径的前缀信息。

    data的过滤规则:
    (1)如下过滤规则:

    <intent-filter>
        <data android:mimeType="image/*"/>
        ...
    </intent-filter>
    

    这种规则指定了媒体类型为所有类型的图片,那么Intent中的minmeType属性必须为"image/*"才可以匹配,这种情况,虽然过滤规则没有指定URI,但是却有默认值,URI的默认值为content和file。也就是说,虽然没有指定URI,但是需要注意:Intent中的URI部分的schema必须为content或者file才能匹配。为了匹配以上规则,我们可以写:

    intent.setDataAndType(Uri.parse("file://abc"),"image/png");
    

    注意:
    如果要为Intent指定完整的data,必须要调用setDataAndType方法,不能先调用setData再调用setType,因为这两个方法批次会清除对方的值,源码:

    public Intent setData(Uri data){
      mData = data;
      mType = null;
      return this;
    }
    

    可以发现setData会把mimeType值清空为null,同理setType也是如此。
    (2)如下过滤规则

    <intent-filter>
                  <data android:mimeType="video/mpeg" android:scheme="http" .../>
                  <data android:mimeType="audio/mpeg" android:scheme="http" .../>
                  ...
    </intent-filter>
    

    这种指定了两种data规则,且每个data都指定了完整的属性值,既有URI又有mimeType。为了匹配(2)中规则,我们可以写出如下示例:
    intent.setDataAndType(Uri.parse("http://abc"),"video/mpeg")
    或者
    intent.setDataAndType(Uri.parse("http://abc"),"audio/mpeg")
    (3)特殊情况(与action不同的地方)

     <intent-filter>
                    <data android:scheme="file" android:host="www.baidu.com" .../>
            ...
    </intent-filter>
    

    <intent-filter>
                    <data android:scheme="file"/>
                    <data android:host="www.baidu.com"/>
                    ...
    </intent-filter>
    

    以上两种写法是一样的。

    IntentFilter匹配规则对于Service和BroadCastReceiver也是同样道理,不过系统建议对于Service还是尽量使用显示调用方式来启动服务。

    最后,当我们通过隐式启动一个Activity时,可以做一下判断,看是否有Activity能够匹配我们的隐式Intent,如果不做判断就有可能出错。
    判断的方法:
    -PackageManager的resolveActivity方法
    -Intent的resolveActivity方法
    如果他们找不到匹配的Activity就会返回空。
    PackageManager还提供了queryIntentActivities方法,这个方法和resolveActivity方法的区别是:它不是返回最佳匹配的Activity信息,而是返回所有成功匹配的Activity信息,方法如下:

    public abstract List<ResolveInfo> queryIntentActivities(Intent intent,int flags);
    public abstract ResolveInfo resolveActivities(Intent intent,int flags);
    上述方法第二个参数,我们使用MATCH_DEFAULT_ONLY这个标记位,含义是:仅仅匹配那些在intent-filter中声明了
    `<catgory android:name="android.intent.category.LAUNCHER" />`这个category的Activity,使用这个标记的意义在于,只要上述两个方法都不返回空,那么startActivity一定可以成功。否则会把不含category的那些Activity匹配出来,可能会导致startActivity失败。因为不含无法接受隐式调用。
    

    <action android:name="android.intent.action.MAIN" />

                <catgory android:name="android.intent.category.LAUNCHER" />
    
    这两比较重要,共同作用是用来标记一个入口Activity,并且会出现在系统的应用列表中,少了任何一个都没有实际意义,也无法出现在系统的应用列表中,也就是二者缺一不可。
    同样,针对Service和BroadCastReceiver,PackageManager同样提供了类似的方法去获取成功匹配的组件信息。
    

    相关文章

      网友评论

          本文标题:Activity知识点集合二

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