Android开发艺术探索第一章:Activity生命周期以及启动模式

831 阅读7分钟

Activity生命周期

典型情况下的生命周期分析

Activity生命周期方法

  1. onCreate: 生命周期第一个方法,在这做初始化工作。
  2. onRestart: 表示Activity正在重新启动。当Activity从不可见重新变为可见状态时被调用。比如按Home键切换桌面,或者打开了另外一个新的Activity。
  3. onStart: 表示Activity正在启动,这种状态时,Activity可见但是没有出现在前台(无法与用户交互,用户还看不到)。
  4. onResume: 表示Activity已经可见(可以和用户进行交互)。和onStart对比,两个方法都表示Activity可见,但是onStart的时候Activity还在后台,onResume的时候,Activity才显示到前台。
  5. onPause: 表示Activity正在停止。此时可以做一些存储数据,停止动画等不耗时操作。如果做了耗时操作,会影响新Activity的显示。
  6. onStop: 表示Activity即将停止,在onPause后调用。可以做一些稍微重量级的回收工作,同样不能太耗时。
  7. onDestory:表示Activity即将被销毁,最后一个生命周期方法。可以做一些资源释放或者回收工作。

Activity生命周期图示

  1. 如果当前Activity采用了透明主题,那么当前Activity不会回调onStop。
  2. 当用户再次返回原Activity,回调如下:onRestart->onStart->onResume。
  3. onStart和onStop是从Activity是否可见角度来回调的。onResume和onPause是从Activity是否在前台(用户可交互)这个角度来回调的。除此之外没其他明显区别。
  4. 如果当前Activity为A,现在用户新打开了一个Activity-B,那么B的onResume是在A的onPause之后执行的。所以在onPause中不要做耗时操作,否则会影响新Activity的显示。

异常情况下的生命周期分析

情况1:资源相关的系统配置发生改变导致Activity被杀死并重新创建

  • 比如在Drawable中设置了横竖屏不同的图片。如果此时突然旋转屏幕,由于系统配置发生了改变,在默认情况下Activity就会销毁并且重新创建(可以在AndroidManifest.xml文件中配置不重建Activity),此时除开默认的生命周期外,系统会调用 onSaveInstanceState来保存Activity当前状态,调用实际在onStoop之前,但是和onPause没有前后关系,可能在onPause前,也可能在后。在Activity被重新创建后,系统会调用onRestoreInstanceState,并且把Activity销毁前onSaveInstanceState保存的Bundle对象作为参数传递给onRestoreInstanceState和onCreate方法。因此我们可以通过onRestoreInstanceState 和 onCreate方法(savedInstanceState参数不为空)判断当前Activity是否是重建的。如果是重建的我们就可以取出销毁前保存的数据并回复,onRestoreInstanceState调用时机在onStart之后。
  • 在onSaveInstanceState和onRestoreInstanceState中,系统会自动做一些保存回复工作,比如当前Activity的视图结构。每一个View也都有这两个方法,像ListView滚动位置,TextView的文本等,这些状态都会自动保存回复。所以自定义 View 中需要有重建后回复数据的需求的话,需要实现这两个方法。

情况2:资源内存不足导致低优先级的 Activity被杀死

Activity的优先级

  1. 前台Activity--正在和用户交互的Activity,优先级最高。
  2. 可见但非前台Activity--比如在Activity弹出一个对话框,此时Activity不能操作,但是可见。
  3. 后台Activity--已经被暂停的Activity,比如执行了onStop。

说明

  • 当系统内存不足,系统就会按照上面优先级杀死Activity所在的进程,并在后续通过onSaveInstanceState和onRestoreInstanceState来存储/恢复数据。如果一个进程中没有四大组件在执行,那么这个进制很快会被系统杀死,因此一些后台工作不适合脱离四大组件而独立允许在后台中。比较好的后台方法:在后台放入Service中,保证进程有一点优先级。
  • 如果不想再系统配置发生改变时,重建Activity。可以给Activity指定configChanges属性。比如 android:configChanges="orientation", 就表示屏幕旋转的时候,不重建Activity。常用configChanges值:orientation-屏幕方向发生改变,uiMod-屏幕状态发生改变,比如开启夜间模式,locale-一般为切换了系统语言,keyboardHidden-键盘可访问新发生了改变,比如弹出了键盘。

Activity启动模式

LainchMode

Standard 标准模式

  • 标准模式,是系统的默认模式。
  • 每次启动一个Activity都会创建一个新的实例,不管这个实例是否已经存在。
  • 谁启动了这个Activity,那么这个Activity就会允许在启动他的那个Activity所在的栈中。比如A启动了B(标准模式),B就会进入A所在的栈中。
  • 常见报错(Calling startActivity from XXX, Is this really what you want)。原因:使用非Activity类型的Context(如ApplicationContext)启动了标准模式的Activity就会报这个错。因为ApplicationContext没有任务栈,所以启动的 Activity不知道去哪儿。解决办法:将待启动的Activity指定 FLAG_ACTIVITY_NEW_TASK 标志位,这样启动的时候就会创建一个新的任务栈。

singleTop 栈顶复用模式

  • 如果新Activity位于任务栈的栈顶,那么此Activity就不会被创建。同时他的onNewIntent方法被回调,但是onCreate,onStart不会被系统调用,因为他并没有重新发生改变。
  • 如果新Activity已存在但是不是在栈顶,那么新Activity任然会创建。

singleTask 栈内复用模式

  • 单实例模式,只要Activity在一个栈中存在,那么启动此Activity就不会创建新实例。和singleTop一样,同样会回调onNewIntent方法。
  • singleTask 有cleanTop的效果。如果D所需任务栈为S1,S1有ADBC。C位于栈顶。那么此时D不会重新创建,系统会把D切换到栈顶并调用其OnNewIntent方法。同时栈内所有D上面的 Activity全部出栈。于是最终S1的情况是AD.

singleInstance 单实例模式

  • 加强singleTask,具有singleTask所有特性。
  • 除上面外,有此种模式的Activity只能单独位于一个任务栈。

Activity的Flags

常用的标识位

  • FLAG_ACTIVITY_NEW_TASK : 指定Activity启动模式为"sinleTask"
  • FLAG_ACTIVITY_SINGLE_TOP: 指定Activity启动模式为"singeTop"
  • FLAG_ACTIVITY_CLEAR_TOP: 具有此标志位的Activity启动时,他上面的Activity都要出栈。sinleTask默认具有这个特性。如果被启动的Activity启动模式为"standard 标准模式"同时具有这个Flag的话,那么它连同它之上的Activity都要出栈,同时系统会创建新的Activity实例并放入栈顶。
  • FLAG_ACTIVITY_EXCLUED_FROM_RECENTS: 具有这个标记的Activity不会出现在历史Activity列表中。也可以在XML指定Activity的属性android:excludeFromRecents="true"

IntentFilter的匹配规则

  • 如果显式调用和隐式调用都存在的话,以显示调用为准。
  • 只有一个Intent同时陪陪action,category,data才能启动目标Activity。一个Intent可以有很多组intent-filter,只要匹配上一组就可以启动对应的Activity。

action

  • action为字符串,系统预定义了一些,同时我们也可以自定义。
  • 匹配规则:intent中的action必须存在,且必须和过滤规则中的任意一个action相同,就算匹配成功,区分大小写。

category

  • category为字符串,系统预定义了一些,同时我们也可以自定义。
  • intent中不管有几个category,对于每个category来说,它必须示过滤规则中已经定义了的。intent中可以没有category。
  • 如果intent没有设置category,那么系统会默认加上 “android.intent.category.DEFAULT” 这个category。
  • 为了Activity能接受隐式调用,必须在intent-filter中指定“android.intent.category.DEFAULT” 这个category,原因示上条。

data

  • 如果过滤规则中定义了data,那么intent中必须也要定义可匹配的data。
  • data由两部分组成,mimeType(媒体类型,比如image/jpeg等)和 URI。
URI的结构: scheme://host:port/path
举例:content://com.joker.project:200/folder/etc
scheme表示 URI的模式

隐式调用需要注意的地方

  • 当通过隐式方式启动Activity的时候,需要判断一下是否由Activity能匹配。
  • 判断方法有两种,采用PackageManager的resolveActivit的方法或者Intent的resolveActivity方法。