Android Activity生命周期、启动模式、Flags、IntentFilter

Activity直译为活动,在Android开发中,译为界面可能更便于理解。作为使用最频繁的四大组件之一,了解其生命周期有助于我们在实际开发中写出优秀的代码。
一、正常情况下的生命周期

(1)onCreate:
Activity正在被创建,这里可以做一些初始化工作,比如加载界面布局,初始化数据等。
(2)onRestart:
Activity正在被重新启动,从不可见到重新可见状态时会被调用。比如用户按Home键,或者打开了一个新的ActivityB,这时当前的ActivityA会执行onPause和onStop,当用户又回到这个ActivityA时,会调用此方法。
(3)onStart:
Activity正在被启动,此时Activity可见但是没有出现在前台,还无法和用户交互。
(4)onResume:
Activity已经可见在前台可交互。
(5)onPause:
Activity正在停止,这里可以做一些存储数据,停止动画等工作,但是不能太耗时,避免影响新的Activity显示。因为必须要onPause先执行完,新的Activity的onResume才会执行。
(6)onStop:
Activity即将停止,这里也可以做一些稍微重量级的回收工作,同样不能太耗时
(7)onDestroy:
Activity即将被销毁,生命周期最后一个回调,可以做回收工作和最终的资源释放。

Q&A

Q1、Activity第一次启动,回调过程?
Q2、用户打开新的Activity或者home键回到桌面,回调过程?
Q3、用户打开新的透明主题Activity,回调过程?
Q4、用户再次回到原Activity,回调过程?
Q5、用户back键回退,回调过程?
Q6、当前ActivityA打开新的ActivityB,B的onResume和A的onPause哪个先执行?

公布答案:

  1. onCreat->onStart->onResume
  2. onPause->onStop
  3. onPause(不会执行onStop)
  4. onRestart->onStart->onResume
  5. onPause->onStop->onDestroy
  6. 先执行A的onPause,再执行B的onResume。所以不要在onPause中做重量级的操作,使新的Activity可以尽快显示出来。
二、异常情况下生命周期

在实际开发中,除了用户操作导致的正常生命周期调度,还会有一些异常的情况,比如系统内存不足Activity可能被杀死,或者切换横竖屏导致Activity被杀死。
所以我们还要掌握异常情况下的生命周期调用,有助于我们写出更优秀的代码。

情况一、资源相关配置发生改变导致Activity被杀死并重新创建,以旋转屏幕为例
被杀死也就是Activity被销毁,onPause,onStop,onDestroy都会被调用的,除此之外,系统还会调用onSaveInstanceState,来保存当前Activity的状态,调用时机可能在onPause之前也可能在onStop之前。系统会为我们默认保存什么呢?比如当前Activity的视图结构,文本框中用户输入的数据,listview滚动滚动位置等。

当Activity重建的时候,系统会执行onRestoreInstanceState,把销毁时存在onSaveInstanceState里面的Bundle对象作为参数传递给onRestoreInstanceState和onCreate方法,所以我们取数据时候两个方法都可以取,区别是onRestoreInstanceState中取不需要判空,而onCreate中需要判断budle是否为空。

需要强调的点:系统只在Activity异常终止的时候会调用onSaveInstanceState和onRestoreInstanceState,正常情况不会触发这个过程。

情况二、资源内存不足导致低优先级Activity被杀死
数据存储和回复过程和情况一是一样,Activity的优先级从高到低可以分为如下三种:

  1. 前台Activity,也就是可以用户直接进行交互的,优先级是最高的
  2. 可见但是不在前台,比如说Activity中有个弹窗,Activity可见但是位于后台无法和用户直接交互。
  3. 后台的Activiyt,执行了onStop

当系统内存不足的时候,会按照优先级的顺序,先杀死优先级低的Activity。

三、Activity的启动模式

Android提供了四种启动模式,standard、singleTop、singleTask、singleInstance.
Android系统默认情况下(默认standard模式),每次打开同一个Activity都会创建一个实例放到任务栈中,当我们按back键的时候,栈中实例会一个一个回退,直到栈空为止,当栈中没有任何Activity实例之后,系统会回收这个任务栈。这种多次重复创建同一个Activity实例的模式,很多场景不适用,所以Android系统提供了其他几种。

1. standard:标准模式

系统默认就是标准模式,每次启动Activity,都会创建一个实例放到任务栈中,不管栈中是否已经有了该Activity的实例。也就是说,在该模式下,一个任务栈中可以有多个实例,每个实例也可以属于不同的任务栈。例如在该模式下,ActivityA打开了ActivityB(standard模式下),那么ActivityB 就会默认进入到A的栈中。
注意:Activity的Context有任务栈,非Activity类型的Context,比如ApplicationContext就没有任务栈,所以用ApplicationContext打开一个Activity会报错。解决办法是启动Activity的时候指定FLAG_ACTIVITY_NEW_TASK标记位,这样就会为这个Activity创建一个任务栈。

2.singleTop栈顶复用模式
Android Activity生命周期、启动模式、Flags、IntentFilter_第1张图片
D已经在栈顶,singleTop模式下打开D,栈内情况如上图。另外因为D Activity不会被重建,所以它的onCreate、onStart不会被调用,onNewIntent方法会被回调。
如果是以standard模式打开D,栈内情况如下:
Android Activity生命周期、启动模式、Flags、IntentFilter_第2张图片
3.singleTask栈内复用模式
三个例子了解singleTask模式

  • 在任务栈S1中,已经存在ABC,这时候以singleTask模式启动D,系统会创建D实例放进任务栈S1中。
  • 在任务栈S1中,已经存在ABC,这时D以singleTask模式请求启动,并设置了所需任务栈是S2,因为S2和D都是不存在的,所以系统会先创建任务栈S2,再创建D 的实例放进S2中。
    (上面提到的所需任务栈:我们可以为每个Activity单独设定TaskAffinity属性,该属性不能和包名相同,因为Activity默认的所需任务栈名字是包名,该属性主要和singleTask启动模式或者allowTaskReparenting属性配对使用,其他情况没有意义)
  • 在任务栈S1中,存在ADBC,这时启动D,因为栈中已经存在D实例,根据栈内复用原则,D不会重建,系统会清掉D之上的实例,把D调到栈顶并调用D的onNewIntent方法。最终S1情况是AD。
    Android Activity生命周期、启动模式、Flags、IntentFilter_第3张图片
    4.singleInstance 单实例模式
    加强版singleTask模式,具有singleTask模式的所有特性外,特殊的是独自拥有一个栈。
四、如何给Activity指定启动模式

启动方式有两种。
第一种:清单文件中指定

        

第二种:通过在Intent中设置标记位来指定

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

区别:
1、优先级方面,如果同时存在,以第二种方式为准,因为第二种方式优先级高于第一种
2、限定范围方面,第一种无法设定FLAG_ACTIVITY_CLEAR_TOP,第二种方式无法指定singleInstance模式。

五、Activity的Flags

大部分情况下,我们不需要为Activity指定标记位,所以对于标记位理解即可。列举几个比较常用的:
FLAG_ACTIVITY_NEW_TASK,为Activity指定singleTask模式
FLAG_ACTIVITY_SINGLE_TOP,为Activity指定singleTop模式
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
具有该标记的Activity不会出现在历史Activity列表中。当我们不希望用户通过历史列表回到这个Activity的时候使用。

六、IntentFilter

Activity的启动方式大致分两种,显式启动和隐式启动。
显示启动常见写法:

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

隐式启动:需要在清单文件中,在所需要打开的Activity中添加过滤器,也就是IntentFilter,举了例子:

        
            
                
                
                
            

            
                
                
                
                
                
            
        

这里梳理一下IntentFilter的匹配规则。

  • 一个Activity可以有多个IntentFilter,一个Intent只要能匹配任何一组intent-filter就可以成功启动Activity。
  • IntentFilter中的过滤信息有action,category,data
  • 一个IntengFilter过滤列表中可以有多个action,category,data,只有一个intent同时匹配action,category,data才算完全匹配。
  • action的匹配规则,action是一个字符串,action匹配要求是Intent中的action存在且必须和过滤规则中的其中一个action相同。action区分大小写
  • category的匹配规则,category是一个字符串,与action不同的是,action是要求Intent中必须有一个action且能和过滤规则中的某个action相同,而category要求Intent可以没有category,但是一旦有了,不管有几个,每一个都要和过滤规则中的category相同。
    为什么不写也可以匹配,因为不写系统默认为Intent加上"android.intent.category.DEFAULT",这也就是为什么必须在intent-filter中指定"android.intent.category.DEFAULT"这个category。
  • data的匹配规则,data的匹配规则和action类似,Intent中必须含有data数据,并且data数据能够完全匹配过滤规则中的某一个data。
    data的结构稍微有些复杂,不不是一个简单的字符串,而是有两部分组成,mimeType和URI。
    mimeType是媒体类型,比如image/jpeg,audio/mpeg4-generic和video/*等,可以表示图片,文本,视频等不同的格式。
    一个完整的完整的URI scheme协议格式由scheme、host、port、path和query组成,其结构如下所示:
://:/?

eg: http://www.baidu.com:80/search/info
注意intent设置的时候要用setDataAndType(),因为setData和setType会彼此清除对方的值。
过滤规则如果没有设置URI,是有默认值的,URI默认值是content和file。

以上知识点均整理来自任玉刚的《Android开发艺术探索》
推荐两篇启动模式相关的面试题文章:
程序员何苦为难程序员(上)
程序员何苦为难程序员(下)

你可能感兴趣的:(Android Activity生命周期、启动模式、Flags、IntentFilter)