Android四大组件知识点梳理一:Activity那点事

1、前言

activity对于大家再熟悉不过了,可是你真的了解它多少?
掌握activity无非是activity的生命周期、加载模式、匹配规则、加载过程。
如果把这些都弄懂了,那么activity你才算掌握。

2、详细理解

2-1:activity生命周期

2-1-1:正常情况下的生命周期分析

名称 描述
onCreate 1、生命周期第一个函数,表示activity正在被创建,只调用一次。2、做一些加载界面和初始化activity数据等操作。
onRestart 1、表示activity正在被重新启动,一般在activity从不可见到可见状态时回调2、比如从另一个activity回来,或者从home重新切换到当前activity
onstart 表示当前activity已经启动了但是还没有到前台,还在后台
onResume 表示当前activity已经启动了并且已经到前台(已经可见)
onPause 1、表示activity正在停止,紧接着onStop就会调用。2、此时若用户再快速的回来那么onResume就会被调用。3、只有当前activity的onPause调用后下一个的activity的onCreate才会被调用。
onStop 表示activity即将停止,可做一些回收工作
onDestory 表示activity即将被销毁,只回调一次

1. 针对一个特定的activity,第一次启动回调:onCreate–>onStart–>onResume
2. 当用户切换另一个activity或者返回到界面时回调:onPause–>onStop,如果新activity采用透明主题,那么onStop就不会调用。
3. 当用户在此回到原activity时回调:onRestart–>onStart–>onResume
4. 当用户按back键回退时回调:onPause–>onStop–>onDestory
5. onCreate和onDestory是一对:代表activity创建和销毁,只会调用一次。
onStart和onStop是一对:代表activity是否可见。
onPause和onResume是一对:代表activity是否在前台。

2-1-2:异常情况下的生命周期分析

  • 异常情况:系统资源发生改变(比如locate(语言)、orientation(屏幕方向)、keybroadHidden(键盘发生改变)等)或者资源内存不足导致低优先级的activity被杀死等情况。
  • 在这种情况下activity会被重新创建,并在onStop前回调onSaveInstanceState方法(和onPause没有明显的时序关系,也可以在onPause前也可能在onPause后)。在onStart后回调onRestoreInstanceState方法,来保存和恢复activity的状态(也可在onCreate中恢复)。
  • 这两个方法只会在activity异常情况下回调,正常情况下不会回调。
  • 当activity在异常情况下需要重新创建时,系统会默认为我们保存当前activity的视图结构,并在activity重启后为我们恢复这些数据。比如文本框中用户的输入数据和listview的滚动位置等。
  • 首先activity被以外终止时,activity会调用onSaveInstanceState方法,activity会委托window去保存数据,然后window再委托顶层容器(DecorView)去保存数据,然后DecorView再一一通知子View去保存数据。
  • 当不想让activity重新创建,我们可以设置在xml中设置configChanges。如在activity声明的地方android:configChanges=“orientation | screenSize”,当屏幕旋转或者屏幕大小发生改变时不让activity重新创建。此时activity没有重建也并没有回调onSaveInstanceState和onRestoreInstanceState方法,相反回调了onConfigurationChanged方法。

2-2:activity启动模式

了解activity四个启动模式前先来了解两个内容:

  • activity任务栈:
    1、activity任务栈是标准的栈结构,具有“First In Last Out”的特性
    2、我们每次打开一个activity时此activity就会添加到任务栈,每次去通过back键回退时都会将当前activity出栈。任意时刻只有位于栈顶的activity才能和用户交互。
    3、同一时刻,android系统可能有多个任务栈,每个任务栈可能存在多个activity,这些activity可能来自同一个应用程序也可能来自多个应用程序;同一个activity可能有一个实例也可能有多个实例,而且这些实例可能位于同一个任务栈,也可能位于不同的任务栈。
    4、多个任务栈同时只能有一个任务栈位于前台即前台任务栈,其余的都只能位于后台即后台任务栈;后台任务栈的activity处于暂停状态,用户可以通过唤起后台任务栈中的任意Activity,将后台任务栈切换到前台。
  • android:taskAffinity属性
    android:taskAffinity是Activity的一个属性,表示该Activity期望的任务栈的名称。默认情况下,一个应用程序中所有Activity的taskAffinity都是相同的,即应用程序的包名。当然,我们可以在配置文件中为每个Activity指定不同的taskAffinity(只有和已有包名不同,才有意义)。一般情况下,该属性主要和SingleTask启动模式或者android:allowTaskReparenting属性结合使用(下面会详细介绍),在其他情况下没有意义。

activity四种启动模式

了解了上面的基础,接下来我们具体谈谈四大启动模式
两种设置方式:

1、在AndroidManifest配置文件中设置activity的启动模式,比如下面

<activity
   android:name=".FirstActivity"
   android:launchMode="standard"/>

2、也可以通过设置Intent的某些标志位来达到相同的效果,关于这些和Activity启动模式相关的标志位

Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

注意:查看任务栈命令:adb shell dumpsys activity activities

Standard(标准模式)
标准模式:也是系统默认模式。在该模式下,每次启动activity,都会创建一个新的实例,并且将其加入到启动此activity的那个activity所在的栈中,所以目标activity有多个实例可以位于不同的栈中。例如:ActivityA启动了标准模式的ActivityB,那么ActivityB就会在ActivityA所在的任务栈中。
关于标准模式的Activity,有一个很经典的异常:
当我们通过非Activity的Context(例如:Service)启动标准模式的Activity时,就会有以下异常:

Caused by: Android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

这是因为标准模式的Activity会进入启动它的Activity的任务栈中,但是非Activity的Context又没有任务栈,所以就出错了。解决方案在异常信息中已经给出了:在Intent中添加FLAG_ACTIVITY_NEW_TASK标志位,这样就会为目标Activity创建新的任务栈。

下面我们通过例子来验证一下Standard模式:

  1. 我们创建FirstActivity和SecondActivity,在他们的onCreate方法中打印出他们的任务栈Id,在onNewInstance方法中打印“onNewInstance”字符串。
  2. 设置FirstActivity和SecondActivity的启动模式为Standard
  3. 首先启动FirstActivity,然后通过FirstActivity再启动SecondActivity,再通过SecondActivity启动FirstActivity。

    我们看一下当前堆栈情况
    这里写图片描述
    activity的打印情况
    这里写图片描述

通过上面的任务栈和日志可知:每次启动FirstActivity,都创建了新的实例,且每个实例都在一个任务栈中(任务栈ID都是311)。

SingleTop(栈顶复用模式)
栈顶复用模式。该模式下,若目标Activity的实例已经存在,但是没有位于栈顶,那么仍然会创建新的实例,并添加到任务栈;若目标Activity的实例已经存在,且位于栈顶,那么就不会创建新的实例,而是复用已有实例,并依次调用目标Activity的onPause -> onNewIntent -> onResume方法。

实例1

  1. 创建FirstActivity,并且在onCreate方法中打印出当前Activity所在的任务栈ID,在onNewIntent方法中打印出”onNewIntent”字符串。
  2. 设置FirstActivity的启动模式为SingleTop。
  3. 通过FirstActivity不断启动FirstActivity本身。

经过上述步骤,最终任务堆栈如下所示:
这里写图片描述
再看FirstActivity打印情况
这里写图片描述

通过上面的任务栈和日志可知:尽管多次启动了FirstActivity,但只在第一次的时候创建了实例,后续的多次启动,仅仅调用了已有FirstActivity实例的onNewIntent方法。最终任务栈(TaskId为313)内只有一个FirstActivity实例。

此外,还有一点需要注意:
多次启动处于栈顶的SingleTop模式的Activity时,其回调函数的顺序是onPause -> onNewIntent -> onResume。

实例2

  1. 创建FirstActivity,并且在onCreate方法中打印出当前Activity所在的任务栈ID,在onNewIntent方法中打印出”onNewIntent”字符串。
  2. 创建SecondActivity,并且在onCreate方法中打印出当前Activity所在的任务栈ID,在onNewIntent方法中打印出”onNewIntent”字符串。
  3. 设置FirstActivity的启动模式为SingleTop,设置SecondActivity的启动模式为Standard。
  4. 首先启动FirstActivity,然后在FirstActivity中启动SecondActivity,然后在SecondActivity中再启动FirstActivity,如此反复。

通过上面系列操作后堆栈情况:
这里写图片描述

再看activity中打印日志:
这里写图片描述

通过上面的任务栈和日志可知:尽管FirstActivity的启动模式为SingleTop,但是当它不位于栈顶时,仍然会创建新的实例。最终任务栈中会出现多个FirstActivity和 SecondActivity,且自始至终,都只有一个任务栈(TaskId为313)。

SingleTask(栈内复用模式)
栈内复用模式。该模式是对SingleTop的进一步加强,若Activity实例已经存在,则不管是不是在栈顶,都不会创建新的实例,而是复用已有Activity实例,即清除任务栈中目标Activity之上的所有Activity,使其位于栈顶,同时也会调用其onNewIntent方法;若Activity实例不存在,系统首先会确认是否有目标Activity期望的任务栈,如果没有,就首先创建目标Activity期望的任务栈,然后创建目标Activity实例并添加到期望的任务栈中;相反,若存在期望的任务栈,那么就直接创建目标Activity实例并将其添加到期望的任务栈。

而Activity期望的任务栈名称就是通过上面介绍的android:taskAffinity属性进行设置的。

实例1
两个Activity的taskAffinity属性相同,具体步骤如下所示:
1. 创建FirstActivity,并且在onCreate方法中打印出当前Activity所在的任务栈ID,在onNewIntent方法中打印出”onNewIntent”字符串。
2. 创建SecondActivity,并且在onCreate方法中打印出当前Activity所在的任务栈ID,在onNewIntent方法中打印出”onNewIntent”字符串。
3. 设置FirstActivity的启动模式为standard,设置SecondActivity的启动模式为singleTask
4. 首先启动FirstActivity,然后在FirstActivity中启动SecondActivity,然后在SecondActivity中再启动FirstActivity,如此反复。

当第一次启动SecondActivity后,任务栈如下所示:
这里写图片描述

可见,因为两个Activity期望的任务栈相同,因此FirstActivity和SecondActivity处于相同的任务栈中(TaskId为317)。

然后再SecondActivity中启动FirstActivity,任务栈如下:
这里写图片描述
因为FirstActivity为standard,所以在此启动FirstActivity就会又创建个实例

最后再次通过FirstActivity启动SecondActivity,任务栈如下:
这里写图片描述
可见并没有创建SecondActivity实例,而是把SecondActivity以上的实例出栈,然后复用SecondActivity。

activity中打印日志如下:
这里写图片描述

通过上面的任务栈和日志可知:当两个Activity的taskAffinity相同时,第一次启动SecondActivity时,是直接将其实例入栈到FirstActivity所在的任务栈(其实FirstActivity所在的任务栈就是SecondActivity期望的任务栈);当第二次启动SecondActivity时,则是直接复用已有SecondActivity实例,同时调用其onNewIntent方法。

实例2
两个Activity的taskAffinity属性不同,具体步骤如下所示:

  1. 创建FirstActivity,并且在onCreate方法中打印出当前Activity所在的任务栈ID,在onNewIntent方法中打印出”onNewIntent”字符串。
  2. 创建SecondActivity,并且在onCreate方法中打印出当前Activity所在的任务栈ID,在onNewIntent方法中打印出”onNewIntent”字符串。
  3. 设置FirstActivity的启动模式为standard,设置SecondActivity的启动模式为singleTask。另外设置SecondActivity的taskAffinity属性为:com.lonbon.myviewtest1。而FirstActivity不变(磨人为程序的包名)。
  4. 首先启动FirstActivity,然后在FirstActivity中启动SecondActivity,然后在SecondActivity中再启动FirstActivity,如此反复。

FirstActivity第一次启动SecondActivity时,任务栈如下:
这里写图片描述
可见,因为两个Activity期望的任务栈不同,因此系统会为SecondActivity创建新任务栈(TaskId为320)。

然后SecondActivity中启动FirstActivity时,任务栈如下:
这里写图片描述
可见,因为FirstActivity为standard模式,所以FirstActivity入栈到启动它的SecondActivity的任务栈中。

最后再由FirstActivity启动SecondActivity,任务栈如下所示:
这里写图片描述
可见,并没有为SecondActivity创建新的实例,而是把SecondActivity之上的FirstActivity出栈,直接复用已有SecondActivity实例。

整个过程日志如下:
注意:这里因为我重新运行了,所以和上面的taskID不一样,请忽略

通过上面的任务栈和日志可知:和案例1的唯一不同就是第一次启动SecondActivity时,系统会为其创建新的任务栈。

  1. 当FirstActivity和SecondActivity的taskAffinity属性相同时:第一次启动SecondActivity时,并不会启动新的任务栈,而是直接将SecondActivity添加到FirstActivity所在的任务栈;否则,将SecondActivity所在任务栈中位于SecondActivity之上的全部Activity都删除,直接跳转到SecondActivity中。
  2. 当FirstActivity和SecondActivity的taskAffinity属性不同时:第一次启动SecondActivity时,会创建新的任务栈,然后将SecondActivity添加到新的任务栈中;否则,将SecondActivity所在任务栈中位于SecondActivity之上的全部Activity都删除,直接跳转到SecondActivity中。

另外,当目标Activity处于栈顶时,启动SingleTask模式的目标Activity,其回调函数的顺序是onPause -> onNewIntent -> onResume,和SingleTop模式相同。

而当目标Activity存在,但是不位于栈顶时,启动SingleTask模式的目标Activity,其回调函数的顺序是onNewIntent -> onRestart -> onStart -> onResume。

SingleInstance(单实例模式)
单实例模式。该模式是SingleTask的强化,除了具有SingleTask的所有特性外,还强调任意时刻只允许存在唯一的Activity实例,且该Activity实例独自占有一个任务栈。即该任务栈只能容纳该Activity实例,不能再添加其他Activity实例到该任务栈,如果该Activity实例已经存在于某个任务栈,则直接跳转到该任务栈。

OK,下面我们根据实例分析SingleInstance模式:

  1. 创建FirstActivity,并且在onCreate方法中打印出当前Activity所在的任务栈ID,在onNewIntent方法中打印出”onNewIntent”字符串。
  2. 创建SecondActivity,并且在onCreate方法中打印出当前Activity所在的任务栈ID,在onNewIntent方法中打印出”onNewIntent”字符串。
  3. 设置FirstActivity的启动模式为standard,设置SecondActivity的启动模式为SingleInstance。
  4. 首先启动FirstActivity,然后在FirstActivity中启动SecondActivity,然后在SecondActivity中再启动FirstActivity,如此反复。

当第FirstActivity第一次启动SecondActivity时:任务栈如下:
这里写图片描述
可见,此时SecondActivity存在于单独的栈中。

然后SecondActivity启动FirstActivity时:任务栈如下:
这里写图片描述
可见,新创建的FirstActivity实例不是位于SecondActivity实例所处的任务栈,而是位于之前FirstActivity实例所处的任务栈(TaskId为326)。这也间接证明了SingleInstance模式的Activity是独占一个任务栈的。

最后再通过FirstActivity启动SecondActivity时:任务栈如下:
这里写图片描述
可见并没有为SecondActivity创建实例,而是直接把SecondActivity所在的栈切换到前台

activity的log打印如下:
这里写图片描述

通过上面的任务栈和日志可知:尽管多次启动了SingleInstance模式的SecondActivity,但只在第一次的时候创建了Activity实例,并且为该实例创建了新的任务栈,后续的多次启动,仅仅调用了已有SecondActivity实例的onNewIntent方法,并将其任务栈切换到了前台。同时,SecondActivity所处任务栈的TaskId为327,FirstActivity所处任务栈的TaskId为326,FirstActivity的实例永远不可能位于SecondActivity所处的任务栈中,即SingleInstance模式的Activity独占一个任务栈。
此外,还有一点需要注意:
当目标Activity实例已经存在时,启动SingleInstance模式的目标Activity,其回调函数的顺序是onNewIntent -> onRestart -> onStart -> onResume。

特殊案例:(针对SingleTask模式的进一步强化)
在特殊使用场景下的启动模式:
假设现在有两个任务栈:前台任务栈中包含ActivityA和ActivityB,后台任务栈中包含ActivityC和ActivityD,且前台任务栈中Activity的启动模式均为Standard,后台任务栈中Activity的启动模式均为SingleTask,两个任务栈的栈名是不同的。
场景一:
场景1:当通过前台任务栈中的ActivityB启动后台任务栈中ActivityD时,系统会将后台任务栈会切换到前台。此时,当用户通过“back”键退出时,整个退出顺序应该是ActivityD -> ActivityC -> ActivityB -> ActivityA。
分析:开始a(AB)栈在前面,b(CD)在后面,由于activity B去启动activity D,所以b栈调到了前面,由于D是singleTask模式,所以不会重新新建实例,且把D移动到栈顶,回退是先回退b栈中的activity,回退完后再会去找下一个栈。
场景二:
场景2:当通过前台任务栈中的ActivityB启动后台任务栈中ActivityC时,系统会将后台任务栈会切换到前台,同时把ActivityC之上的ActivityD出栈。此时,当用户通过“back”键退出时,整个退出顺序应该是ActivityC -> ActivityB -> ActivityA。

这里我们通过具体案例验证下场景1(场景2比较类似,不再赘述),具体步骤如下所示:

  1. 首先创建包名为leon.com.launchmodeab的App1,包含ActivityA和ActivityB,设置其启动模式为Standard,然后创建包名为leon.com.launchmodecd的App2,包含ActivityC和ActivityD,设置其启动模式为SingleTask。
  2. 接着启动App2,并依次启动ActivityC和ActivityD,然后通过“home”键切换到后台。
  3. 然后启动App1,并依次启动ActivityA和ActivityB。
  4. 最后,通过ActivityB启动ActivityD,并通过“back”键依次退出,观察Activity的退出顺序。

当完成步骤1~3时,任务栈如下所示:
这里写图片描述
可知,TaskIdId为2146的任务栈包含ActivityA和ActivityB,TaskId为2145的任务栈包含ActivityA和ActivityB。此时,若是直接通过“back”键退出,那么退出顺序是ActivityB -> ActivityA。

当通过ActivityB启动ActivityD后,任务栈如下所示:
这里写图片描述
可知,包含ActivityD的任务栈被切换到了前台,虽然ActivityC和ActivityD被隔开了,但是从TaskId和栈名来看,他们还是是同一个任务栈,而ActivityA和ActivityB所在的栈则在后面。此时,若通过“back”键退出,那么退出顺序是ActivityD -> ActivityC -> ActivityB -> ActivityA。

这里就介绍完了四中启动模式!别急,关于启动模式,下面还有………………..

Intent与启动模式相关的Flag简介

  1. FLAG_ACTIVITY_NEW_TASK
    在google的官方文档中介绍,它与launchMode=”singleTask”具有相同的行为。实际上,并不是完全相同!很少单独使用FLAG_ACTIVITY_NEW_TASK,通常与FLAG_ACTIVITY_CLEAR_TASK或FLAG_ACTIVITY_CLEAR_TOP联合使用。因为单独使用该属性会导致奇怪的现象,通常达不到我们想要的效果!通常情况下会在非activity的content启动activity中(比如service中启动activity)。

  2. FLAG_ACTIVITY_SINGLE_TOP
    在google的官方文档中介绍,它与launchMode=”singleTop”具有相同的行为。实际上,的确如此!单独的使用FLAG_ACTIVITY_SINGLE_TOP,就能达到和launchMode=”singleTop”一样的效果。

  3. FLAG_ACTIVITY_CLEAR_TOP
    顾名思义,FLAG_ACTIVITY_CLEAR_TOP的作用清除”包含Activity的task”中位于该Activity实例之上的其他Activity实例。FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_NEW_TASK两者同时使用,就能达到和launchMode=”singleTask”一样的效果!

  4. FLAG_ACTIVITY_CLEAR_TASK
    FLAG_ACTIVITY_CLEAR_TASK的作用包含Activity的task。使用FLAG_ACTIVITY_CLEAR_TASK时,通常会包含FLAG_ACTIVITY_NEW_TASK。这样做的目的是启动Activity时,清除之前已经存在的Activity实例所在的task;这自然也就清除了之前存在的Activity实例!

注意:当同时使用launchMode和上面的FLAG_ACTIVITY_NEW_TASK等标签时,以FLAG_ACTIVITY_NEW_TASK为标准。也就是说,代码的优先级比manifest中配置文件的优先级更高!

  1. FLAG_ACTIVITY_NEW_TASK标签测试

    实例1
    当FirstActivity和SecondActivity的android:taskAffinity相同的情况下
    1、创建FirstActivity,并且在onCreate方法中打印出当前Activity所在的任务栈ID,在onNewIntent方法中打印出”onNewIntent”字符串。
    2、 创建SecondActivity,并且在onCreate方法中打印出当前Activity所在的任务栈ID,在onNewIntent方法中打印出”onNewIntent”字符串。
    3、在FirstActivity的跳转加入flag FLAG_ACTIVITY_NEW_TASK,然后SecondActivity正常跳转(默认standard模式)
    4、先从FirstActivity跳转到SecondActivity,然后再从SecondActivity跳转到FirstActivity,如此反复。

    通过上面的操作我们看任务栈:
    这里写图片描述
    再来看日志打印情况
    这里写图片描述
    我们看到,并没有什么效果啊,这和不设置flag FLAG_ACTIVITY_NEW_TASK并没有区别啊。
    事实上,在相互跳转的两个Activity的android:taskAffinity相同的情况下,单独使用FLAG_ACTIVITY_NEW_TASK不会产生任何效果!

实例2
当FirstActivity和SecondActivity的android:taskAffinity不同的情况下
上述步骤是一样的,只是让FirstActivity和SecondActivity的android:taskAffinity不同,这里就不重复写了。

当FirstActivity第一次切换到SecondActivity看任务栈:
这里写图片描述

然后从SecondActivity跳回FirstActivity后继续看任务栈
这里写图片描述

最后当我们再次从FirstActivity切换到SecondActivity的时候出问题了,页面不跳转了!!!!!这是为什么??
是因为此时SecondActivity实例已经存在,但是它所在的task的栈顶是FirstActivity;而单独的添加FLAG_ACTIVITY_NEW_TASK又不会”删除task中位于SecondActivity之上的Activity实例”,所以就没有发生跳转!
接下来我们看一下FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TOP一起使用的情况。

2、FLAG_ACTIVITY_SINGLE_TOP测试
它与launchMode=”singleTop”具有一样的效果,这里就不测试了。

3、FLAG_ACTIVITY_CLEAR_TOP测试
实例1
当FirstActivity和SecondActivity的android:taskAffinity相同的情况下
1、创建FirstActivity,并且在onCreate方法中打印出当前Activity所在的任务栈ID,在onNewIntent方法中打印出”onNewIntent”字符串。
2、 创建SecondActivity,并且在onCreate方法中打印出当前Activity所在的任务栈ID,在onNewIntent方法中打印出”onNewIntent”字符串。
3、在FirstActivity的跳转加入flag FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TOP,然后SecondActivity正常跳转(默认standard模式)
4、先从FirstActivity跳转到SecondActivity,然后再从SecondActivity跳转到FirstActivity,如此反复。
完成上面操作后下面我们来看一下任务栈

第一次FirstActivity跳转到SecondActivity任务栈:
这里写图片描述
然后SecondActivity跳回FirstActivity任务栈:
这里写图片描述
最后FirstActivity再次跳回SecondActivity任务栈
这里写图片描述
log打印日志如下:
这里写图片描述

总结:我们发现,activity被销毁又重建了

当FirstActivity和SecondActivity的android:taskAffinity不相同的情况下
在注册SecondActivity时加上android:taskAffinity=”com.lonbon.myviewtest1”,其它步骤和上面相同,这里不重复写了。
当第一次FirstActivity切换到SecondActivity时任务栈:
这里写图片描述
然后SecondActivity切换回FirstActivity时任务栈:
这里写图片描述
最后FirstActivity再次切换回SecondActivity时任务栈:
这里写图片描述
log日志如下:
这里写图片描述

总结:FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_NEW_TASK两者同时使用,就能达到和launchMode=”singleTask”一样的效果。

4、FLAG_ACTIVITY_CLEAR_TASK测试
实例1
当FirstActivity和SecondActivity的android:taskAffinity相同的情况下
1、创建FirstActivity,并且在onCreate方法中打印出当前Activity所在的任务栈ID,在onNewIntent方法中打印出”onNewIntent”字符串。
2、 创建SecondActivity,并且在onCreate方法中打印出当前Activity所在的任务栈ID,在onNewIntent方法中打印出”onNewIntent”字符串。
3、在FirstActivity的跳转加入flag FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TASK,然后SecondActivity正常跳转(默认standard模式)
4、先从FirstActivity跳转到SecondActivity,然后再从SecondActivity跳转到FirstActivity,如此反复。
第一次FirstActivity跳转到SecondActivity任务栈:
这里写图片描述

SecondActivity跳转回FirstActivity任务栈情况:
这里写图片描述

FirstActivity再次跳转到SecondActivity任务栈情况:
这里写图片描述

实例2
当FirstActivity和SecondActivity的android:taskAffinity不同的情况下
1、创建FirstActivity,并且在onCreate方法中打印出当前Activity所在的任务栈ID,在onNewIntent方法中打印出”onNewIntent”字符串。
2、 创建SecondActivity,并且在onCreate方法中打印出当前Activity所在的任务栈ID,在onNewIntent方法中打印出”onNewIntent”字符串。
3、在FirstActivity的跳转加入flag FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TASK,然后SecondActivity正常跳转(默认standard模式)
4、先从FirstActivity跳转到SecondActivity,然后再从SecondActivity跳转到FirstActivity,如此反复。
第一次FirstActivity跳转到SecondActivity任务栈:
这里写图片描述

SecondActivity跳回FirstActivity任务栈:
这里写图片描述

FirstActivity再次跳转到SecondActivity任务栈:
这里写图片描述

日志打印情况
这里写图片描述

总结:通过上述两个两个例子验证,FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TASK一起使用时,清空当前栈然后重新创建activity实例放入栈内

2-3:activity匹配规则

启动activity分为两种,显式调用和隐式调用。
显式调用:需要明确指定被启动对象的组件信息,包括包名和类名。
隐式调用:不需要明确指定组件信息。
原则上一个Intent不应该既有显式调用又有隐式调用,如果二者并存的话,以显示调用为主。
显式调用很简单大家也都很熟悉,这里不说了。
隐式调用需要Intent能够匹配目标组件的IntentFilter中所设置的过滤信息,如果不匹配将无法启动目标activity。
IntentFilter中的过滤信息有action、category、data。
只有一个Intent同时匹配action类别、category类别、data类别才算完全匹配,只有完全匹配才能成功启动目标activity。

  • action的匹配规则
    系统预定义了一些匹配规则,当然我们也可以定义自己的匹配规则。
    一个过滤规则中有多个action,只要Intent中的action能够和过滤规则中的任何一个action相同即可匹配成功。
    action匹配规则要求Intent中的action存在且必须和过滤规则中的其中一个action相同。
    action区分大小写。

  • category的匹配规则
    系统预定义了一些category,我们也可以定义自己的category
    Intent可以没有category,但是如果一旦有category,不管是几个,每个都要能够和过滤规则中的任何一个category相同。
    为什么不设置category也可以匹配呢?原因是系统在调用startActivity或者startActivityForResult的时候会默认为Intent加上android.intent.category.DEFAULT这个category。

  • data的匹配规则
    data的匹配规则和action类似,如果过滤规则中定义了data,那么Intent中必须也要定义可匹配的data。
    data由两部分组成,mimeType和URI。
    mimeType指媒体类型,比如image/jpeg、audio/mpeg4-generic和video/*等,可以表示图片、文本、视频等不同的媒体格式。
    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也表示完整的路径信息,但是它里面可以包含通配符;pathPrefix表示路径的前缀信息。
    如果一个data生效,必须包含Scheme和Host,其它可有可无

举个例子:

<receiver
            android:name="com.example.common.receiver.MasterReceiver"
            android:enabled="true"
            android:exported="false">
            <intent-filter>
                <action android:name="com.lonbon.intent.action.MAIN" />

                <category android:name="android.intent.category.DEFAULT" />

                <data
                    android:host="NLV-15"
                    android:scheme="bobo" />
                <data
                    android:host="NLV-30"
                    android:scheme="bobo" />
                <data
                    android:host="NLV-60"
                    android:scheme="bobo" />
                <data
                    android:host="NLV-90"
                    android:scheme="bobo" />
                <data
                    android:host="NLV-120"
                    android:scheme="bobo" />
                <data
                    android:host="NLV-300"
                    android:scheme="bobo" />
                <data
                    android:host="NLV-600"
                    android:scheme="bobo" />
                <data
                    android:host="NLV-10000"
                    android:scheme="bobo" />
            </intent-filter>
        </receiver>
Intent newIntent = new Intent("com.lonbon.intent.action.MAIN");
                newIntent.setData(Uri.parse( "bobo://NLV-600));
                sendBroadcast(newIntent);

上面例子指定了action,省略了category,定义了多个data。

2-4:activity工作过程

猜你喜欢

转载自blog.csdn.net/u013277209/article/details/75338198