温故而知新:重新认识 Activity 的生命周期
7. onResume
说明:
这个方法应该很常用了,此方法会在 onStart 方法之后执行,这是唯一确定的执行顺序,如果是首次创建会执行它,当然当 Activity 经历 onPause 或 onStop 之后也会执行到此方法。如果首次创建时有这几个方法 onRestoreInstanceState、onPostCreate、onStateNotSaved,那么 onStart 会在他们之后执行。
/**
* Called after {@link #onRestoreInstanceState}, {@link #onRestart}, or
* {@link #onPause}, for your activity to start interacting with the user.
* This is a good place to begin animations, open exclusive-access devices
* (such as the camera), etc.
*
* Keep in mind that onResume is not the best indicator that your activity
* is visible to the user; a system window such as the keyguard may be in
* front. Use {@link #onWindowFocusChanged} to know for certain that your
* activity is visible to the user (for example, to resume a game).
*
* Derived classes must call through to the super class’s
* implementation of this method. If they do not, an exception will be
* thrown.
*
*/
官方注释说明明确指出,不能以此方法作为判断标志来确定当前 activity 是都是对用户可见的,因为可能有其他的系统弹窗在该 activity 前面,比如系统软键盘(我想官方这里的意思可能是不一定完全对用户可见,但是经过 onResume 之后 setContentView 设置的内容就会显示出来)。同样,子类必须调用 super 方法,否则会报异常。(尽量保证 super 在第一行)
虽然官方建议 onResume 不能作为用户可见的依赖方法,但实际当中基本上我们会在此方法中恢复一些之前停止的状态,如恢复之前在 onPause 中停止的动画、视频播放、Camera 预览等,因为只要 onResume 被调用了说明用户之前的 activity 回到前台(有可能不是完全可见,只有一部分可见,但用户肯定是能看到它的,至少是部分),从体验角度来讲是需要这样处理的。
此外,不能在 onResume 方法中做额外的耗时任务,如数据保存数据库,I/O 读写等,这将导致 Activity 的卡顿,或者用户重新回来的时候卡顿,影响交互体验。
8. onPostResume
说明:
此方法跟 onPostCreate 方法类似,会紧跟在 onResume 方法之后被调用。
/**
* Called when activity resume is complete (after {@link #onResume} has
* been called). Applications will generally not implement this method;
* it is intended for system classes to do final setup after application
* resume code has run.
*
* Derived classes must call through to the super class’s
* implementation of this method. If they do not, an exception will be
* thrown.
*
*/
同样,从官方的注释说明来看它目前并没有什么卵用。。
9. onAttachedToWindow
说明:
该方法是 Window 中 Window.Callback 接口中的方法,该方法会在 window 被添加的 WindowManager 上时被调用。从调用顺序上看,它会在 onPostResume 方法之后被调用,但是只有 Activity 首次创建才会调用。
/**
* Called when the main window associated with the activity has been
* attached to the window manager.
* See {@link View#onAttachedToWindow() View.onAttachedToWindow()}
* for more information.
* @see View#onAttachedToWindow
*/
官方注释让看 View#onAttachedToWindow()方法,但是我看了下 View 的这个方法中并没有找到调用 Window.Callback 的 onAttachedToWindow 方法,于是后来到 DectorView 类中终于找到了该方法的回调,我们知道 DectorView 是最顶层的 View,当我们设置给 PhoneWindow 对象的 contentView 最终都会被加到 DectorView 上,当然如果你的 Activity 没有调用 setContentView 的话,也是会有默认的 DectorView 对象的。所以,实际上是 DectorView 被添加到 PhoneWindow 时会调用此方法。因此 onAttachedToWindow 在 Activity 创建后一定被执行的,并且从该方法开始 View 才真正被绘制在窗口之上,也是我们最终看到的东西。所以严格意义来讲,经过该方法之后 Activity 才处于可以与用户进行交互的状态。这也是为什么在阿里的 android 开发手册当中推荐你在 onAttachedToWindow 方法之后再去创建显示弹窗。
10. onPause
说明:
此方法也是高频使用的方法,通常在 Activity 不在前台(被其他 Activity 的页面完全遮挡,或者部分可见)时被调用。
/**
* Called as part of the activity lifecycle when an activity is going into
* the background, but has not (yet) been killed. The counterpart to
* {@link #onResume}.
*
* When activity B is launched in front of activity A, this callback will
* be invoked on A. B will not be created until A’s {@link #onPause} returns,
* so be sure to not do anything lengthy here.
*
* This callback is mostly used for saving any persistent state the
* activity is editing, to present a “edit in place” model to the user and
* making sure nothing is lost if there are not enough resources to start
* the new activity without first killing this one. This is also a good
* place to do things like stop animations and other things that consume a
* noticeable amount of CPU in order to make the switch to the next activity
* as fast as possible, or to close resources that are exclusive access
* such as the camera.
*
* In situations where the system needs more memory it may kill paused
* processes to reclaim resources. Because of this, you should be sure
* that all of your state is saved by the time you return from
* this function. In general {@link #onSaveInstanceState} is used to save
* per-instance state in the activity and this method is used to store
* global persistent data (in content providers, files, etc.)
*
* After receiving this call you will usually receive a following call
* to {@link #onStop} (after the next activity has been resumed and
* displayed), however in some cases there will be a direct call back to
* {@link #onResume} without going through the stopped state.
*
* _Derived classes must call through to the super class’s
* implementation of this method. If they do not, an exception will be
* thrown._
*/
官方注释说明中提到,当另一个 Activity B 需要被创建来到 Activity A 的前台时,只有当 Activity A 的 onPause 方法执行完毕后,才会执行 Activity B 的创建生命周期方法。所以不应该在该方法中做任何耗时任务。点击查看:Activity启动另一个Activity并返回的完整生命周期
onPause 方法被调用时,我们应该释放或者停止任何消耗 CPU 资源的行为,以便下一个 Activity 能得到快速的切换显示,比如结束或者暂停正在执行的动画、正在播放的视频、Camera 预览、正在使用的系统服务(如 GPS 或其他传感器)等等。如有必要,需要在 onPause 方法中对任何用户正在编辑的信息进行持久化的保存,以便用户再次回来时能恢复之前的状态。
对系统而言,如果你的应用不是在前台,也就是经过 onPause 之后的(当然是 onPause 之后的那个 Activity 不属于你的应用的情况),会变成一个后台进程,而后台进程在内存不足时会被系统杀死。因此 onPause 方法是我们保存全局持久化数据的一个重要方法。虽然在 onResume 之后会调用 onSaveInstanceState 方法,在 onSaveInstanceState 方法中也可以来保存数据,但是该方法的时机较晚,主要用来保存一些 UI 状态的数据。
注意,当调用 finish 方法或被系统杀死的时候,onPause 是不会被调用的。另外子类一定要调用 super 方法,否则会报异常。(并尽量保证在第一行)还有就是如果被自己 Activity show 出来的弹窗遮挡部分的话,onPause 也是不会被调用的,必须是被其他 Activity show 出来的弹窗才能满足条件。
11. onSaveInstanceState
说明:
此方法会在 Activity 被销毁时调用,可以在该方法中进行 UI 状态的保存,并在 onCreate 或者 onRestoreInstanceState 方法中进行恢复。
/**
* Called to retrieve per-instance state from an activity before being killed
* so that the state can be restored in {@link #onCreate} or
* {@link #onRestoreInstanceState} (the {@link Bundle} populated by this method
* will be passed to both).
*
* This method is called before an activity may be killed so that when it
* comes back some time in the future it can restore its state. For example,
* if activity B is launched in front of activity A, and at some point activity
* A is killed to reclaim resources, activity A will have a chance to save the
* current state of its user interface via this method so that when the user
* returns to activity A, the state of the user interface can be restored
* via {@link #onCreate} or {@link #onRestoreInstanceState}.
*
* Do not confuse this method with activity lifecycle callbacks such as
* {@link #onPause}, which is always called when an activity is being placed
* in the background or on its way to destruction, or {@link #onStop} which
* is called before destruction. One example of when {@link #onPause} and
* {@link #onStop} is called and not this method is when a user navigates back
* from activity B to activity A: there is no need to call {@link #onSaveInstanceState}
* on B because that particular instance will never be restored, so the
* system avoids calling it. An example when {@link #onPause} is called and
* not {@link #onSaveInstanceState} is when activity B is launched in front of activity A:
* the system may avoid calling {@link #onSaveInstanceState} on activity A if it isn’t
* killed during the lifetime of B since the state of the user interface of
* A will stay intact.
*
* The default implementation takes care of most of the UI per-instance
* state for you by calling {@link android.view.View#onSaveInstanceState()} on each
* view in the hierarchy that has an id, and by saving the id of the currently
* focused view (all of which is restored by the default implementation of
* {@link #onRestoreInstanceState}). If you override this method to save additional
* information not captured by each individual view, you will likely want to
* call through to the default implementation, otherwise be prepared to save
* all of the state of each view yourself.
*
* If called, this method will occur before {@link #onStop}. There are
* no guarantees about whether it will occur before or after {@link #onPause}.
*
* @param outState Bundle in which to place your saved state.
*
*/
一般情况下它会在 onPause 方法之后被调用,但是不能指望一定是在 onPause 方法之后被调用,最简单的情况比如打开一个 Activity,然后按 back 键关闭它,这时系统就不会调用该方法,或者直接调用 finish 方法也不会调用该方法。当系统内存不足时可能会直接调用该方法,而不会调用其他任何生命周期方法。总之该方法跟生命周期的依赖不是固定的。
目前能确定调用该方法的场景有:按下 home 键、锁屏、跳转到其他 Activity、横竖屏切换。能确定的一点是只要该方法被调用到,那么一定是在 onStop 之前,跟 onPause 方法的先后顺序不确定。
实际上即便你不实现该方法,该方法的父类默认实现页会将视图树中拥有 id 的每个控件的 UI 状态以及获得视图焦点的 id 进行保存,并在 onRestoreInstanceState 方法中来为你恢复每个实例的 UI 状态。但是如果你有额外的被 UI 控件持有的信息需要保存,你可能需要重写此方法。
此外,我们应该只在该方法中进行 view 状态的保存,而不应该依赖该方法做额外的持久化数据保存如数据库保存,上传服务器等等。
12. onStop
说明:
此方法当 Activity 对用户不可见时被调用,可以在该方法中进行数据持久化保存。
/**
* Called when you are no longer visible to the user. You will next
* receive either {@link #onRestart}, {@link #onDestroy}, or nothing,
* depending on later user activity.
*
* _Derived classes must call through to the super class’s
* implementation of this method. If they do not, an exception will be
* thrown._
*
*/
类似 onPause 方法,此方法中也可以进行资源的释放,因为经过 onStop 之后该 Activity 也不在前台了,有可能是按了 Home 键或者其他 Activity 来到了前台。假如是按了 Home 键或其他 app 来到前台,那么该 Activity 所处进程变成后台进程有可能被系统杀死,所以需要释放那些系统共享资源如 GPS、Camera,或者暂停动画、视频播放等销毁 CPU 的资源。同时时我们可以在 onPause 方法保存用户数据,以做现场恢复用。
在 onStop 方法之后,要么是经历 onDestroy 方法销毁 Avtivity,要么是会在稍后重新返回 Activity 执行 onRestart 方法。
在子类中必须调用 super 方法,否则会抛出异常。(尽量保证在第一行)
13. onRestart
说明:
此方法是当前 Activity 被其他 Activity 完全遮挡再返回时调用,也就是经过 onStop 之后,此方法之后会紧接着调用 onStart 方法。
/**
* Called after {@link #onStop} when the current activity is being
* re-displayed to the user (the user has navigated back to it). It will
* be followed by {@link #onStart} and then {@link #onResume}.
*
* For activities that are using raw {@link Cursor} objects (instead of
* creating them through
* {@link #managedQuery(android.net.Uri , String[], String, String[], String)},
* this is usually the place
* where the cursor should be requeried (because you had deactivated it in
* {@link #onStop}.
*
* _Derived classes must call through to the super class’s
* implementation of this method. If they do not, an exception will be
* thrown._
*
*/
官方注释提到如果你使用原生的 Cursor 对象(而不是通过 managedQuery 方法),可以在该方法中恢复 Cursor 请求(因为有可能你在 onStop 中停止了)Cursor 请求。但是我看官方的Cursor#requery()
和Cursor#deactivate()
方法都已经弃用,该方法会导致 UI 线程的 ANR。
同样,如果子类想要覆写此方法的话,要调用 super 方法,否则抛异常。(并尽量放在第一行)
14. onDestroy
说明:
此方法是 Activity 被销毁的时候调用,一般是调用了 finish 方法, 也有可能是系统销毁。
/**
* Perform any final cleanup before an activity is destroyed. This can
* happen either because the activity is finishing (someone called
* {@link #finish} on it, or because the system is temporarily destroying
* this instance of the activity to save space. You can distinguish
* between these two scenarios with the {@link #isFinishing} method.
*
* _Note: do not count on this method being called as a place for
* saving data! For example, if an activity is editing data in a content
* provider, those edits should be committed in either {@link #onPause} or
* {@link #onSaveInstanceState}, not here._ This method is usually implemented to
* free resources like threads that are associated with an activity, so
* that a destroyed activity does not leave such things around while the
* rest of its application is still running. There are situations where
* the system will simply kill the activity’s hosting process without
* calling this method (or any others) in it, so it should not be used to
* do things that are intended to remain around after the process goes
* away.
*
* _Derived classes must call through to the super class’s
* implementation of this method. If they do not, an exception will be
* thrown._
*
*/
在此方法中需要进行所有资源的释放操作,如释放 Camera、GPS, 停止正在运行的 Thread、移除正在处理以及尚未处理的 Handler 消息,停止 Service、反注册 Receiver 等。此方法不能被用做保存用户数据的依赖方法,因为某些情况下(如内存吃紧)系统杀死 Activity 的宿主进程时不会调用 onDestroy。最典型的就是用户从 recent used app menu list(最近使用的 app 菜单列表)中手动杀死 Activity 时,onDestroy 很大概率上基本不会被调用。所以不能将此方法作为 Activity 销毁的标志依赖方法。
可以使用 isFinishing()方法来判断当前 activity 是否正在销毁过程中,isFinishing()返回 true 的情况下一般是某个用户主动调用了 finish 方法,这样在 onDestroy 方法中就可以判断到底是由系统终止的还是用户主动终止的了。
在子类中必须调用 super 方法,否则会抛出异常。(尽量保证在第一行)
15. onDetachedFromWindow
说明:
该方法也是 Window 中 Window.Callback 接口中的方法,该方法会在 Activity 从 WindowManager 上时 detach 被调用。从调用顺序上看,它会在 onDestroy 方法之后被调用。
/**
* Called when the main window associated with the activity has been
* detached from the window manager.
* See {@link View#onDetachedFromWindow() View.onDetachedFromWindow()}
* for more information.
* @see View#onDetachedFromWindow
*/
该方法其实也是 DectorView 被从 PhoneWindow 对象上移除时会调用。同样,你不能把它作为 Activity 被销毁的依赖方法,因为它比 onDestroy 方法还不靠谱,有些情况下 onDestroy 方法被调用了,但是此方法不会被调用。
Activity 的状态
Activity 在系统中是由 Activity 栈来管理的。当一个 Activity 启动之后,它会被放在栈顶(压栈)即成为正在运行的那个 Activity, 之前的 Activity 则会保存在栈顶之下并且直到新的 Activity 被启动之前不会再回到前台。
Activity 四种基本状态:
运行状态(running): Activity 在屏幕的最前台(位于栈顶), 一般经过 onResume 方法之后进入该状态。
暂停状态(paused):Activity 失去焦点但是仍然可见(有可能是部分可见,如被非全屏或透明的 Activity 遮挡)。一个被暂停的 Activity 是完全活跃的(在内存中会保持所有的信息并且仍然被 attached 在 window manager 之上),但是在低内存的极端情况下一个暂停的 Activity 仍然可能被系统杀死。一般经过 onPause 方法后进入该状态。
停止状态(stoped):被其他 Activity 完全遮挡,并且对用户完全不可见。虽然此时内存中还会保持它的信息,但这种情况下经常会被系统低内存时优先杀死。一般经过 onStop 方法后进入该状态。
死亡状态(destroyed):Activity 实例已被销毁。通常有几种情况:1.主动调用 finish 结束掉 2.系统内存不足时杀掉 3.用户手动杀死了应用进程。如果是处于暂停或停止状态的 Activity 随时有可能被系统或用户杀死。一般进入该状态会经过 onDestroy 方法(但不确保)。
可见生命周期与前台生命周期
完整生命周期(entire lifetime): 从 onCreate() 方法开始到 onDestroy()结束的整个过程。要求在 onDestroy()中释放所有潜在的执行任务和线程等。
可见生命周期(visible lifetime): 从 onStart() 方法开始到 onStop()方法被调用之间的过程。在这个过程中用户可以在屏幕上看到该 Activity(但不一定可以交互,例如被东西遮挡),需要开发者维护这期间的需要的用户资源,比如在 onStart()中注册一个 BroadcastReceiver,在 Stop()中反注册该 BroadcastReceiver。
前台生命周期(foreground lifetime):从 onResume() 方法开始到 onPause()方法被调用之间的过程。这时 Activity 处于所有其它 Activity 的前台,并且与用户处于可以交互状态。在整个生命周期当中,前后台切换是发生频率最高的,因此不能在这两个方法中执行特别耗时的工作,否则会造成 Activity 切换时的卡顿,影响体验。
Activity 中的其他方法
下面的方法是 Activity 中的一部分,严格来说这些方法不属于生命周期方法,只是满足特定条件下会被触发,但是有时跟生命周期方法又有着特定关系或者会参与到生命周期当中,你可以重写这些方法来做一些需求处理。
onWindowFocusChanged():当 Activity 所在的窗口获得或者失去焦点时被触发。也就是当焦点发生变化时触发。这个方法可以用来当做 Activity 是否对用户可见的标识。该方法是独立于 Activity 生命周期之外的,但是经过 onResume 之后一般都会调用走该方法,这时 Activity 会重新获得焦点,除非有其他 dialog 或 popup 弹出。系统的一些弹窗或通知栏面板也会导致该方法被回调。
正常启动 Activity 时,它的顺序是:*
onCreate–>onStart–>onResume–>onAttachedToWindow–>onWindowFocusChanged*, 很显然这时它是最后一步被调用,说明最后才会获得焦点,当按下 Home 键时:onWindowFocusChanged–>onPause–>onStop,当按下 back 键时:onWindowFocusChanged–>onPause–>onStop–>onDestroy–>onDetachedFromWindow, 很显然这时它是第一个被调用的,说明首先会失去焦点,其他页面返回时:onRestart–>onStart–>onResume–>onWindowFocusChanged, 锁屏时:onPause–>onStop–>onWindowFocusChanged, 可以看出跟生命周期的关系不大,所以不能依赖它在 ac 生命周期中的执行顺序来作为执行逻辑依据,还有下拉通知栏也会触发 onWindowFocusChanged,但是此时不会触发任何其他的 ac 生命周期。
onConfigurationChanged(): 当正在运行的 Activity 的设备的配置信息发生变化时,由系统调用该方法。你可以在 AndroidManifest.xml 中为 Activity 配置该属性
android:configChanges=""
,这样配置以后就可以在 onConfigurationChanged()方法中获取到这些配置发生变化时的值。如果没有在 Manifest 文件中配置的项发生,那么系统将会先杀死 Activity 并重新创建(也就是重新走一遍生命周期)。
其中,android:configChanges=""
可配置的属性如下:
mcc : SIM 卡唯一标识 IMSI(国际移动用户识别码)中的国家代码,由三位数字组成,中国为 460。此项标识 mcc 代码发生了改变
mnc:SIM 卡唯一标识 IMSI(国际移动用户识别码)中的运营商代码,由两位数字组成,中国移动 TD 系统为 00,中国联通为 01,中国电信为 03。此项标识 mnc 发生了改变
locale:设备的本地位置发生了改变,一般指切换了系统语言
touchscreen:触摸屏发生了改变,这个很费解,正常情况下无法发生,可以忽略它
keyboard:键盘类型发生了改变,比如用户使用了外插键盘
keyboardHide:键盘的可访问性发生了改变,比如用户调出了键盘
navigation:系统导航方式发生了改变,比如采用了轨迹球导航,这个有点费解,很难发生,可以忽略它
screenLayout:屏幕布局发生了改变,很可能是用户激活了另外一个显示设备
fontScale:系统字体缩放比例发生了改变,比如用户选择了一个新字号
uiMode:用户界面模式发生了改变,比如是否开启了夜间模式(API 8 添加)
orientation:屏幕方向发生了改变,这个是最常用的,比如旋转了手机屏幕
screenSize:当屏幕的尺寸信息发生了改变,当旋转设备屏幕时,屏幕尺寸会发生变化,这个选项比较特殊,它和编译选项有关,当编译选项中的 minSdkVersion 和 TargetVersion 均低于 13 时,此选项不会导致 Activity 重启,否则会导致 Activity 重启(API 13 新添加)
smallestScreenSize:设备的物理屏幕尺寸发生改变,这个项目和屏幕的方向没有关系,仅仅表示在实际的物理屏幕的尺寸改变的时候发生,比如用户切换到了外部的显示设备,这个选项和 screenSize 一样,当编译选项中的 minSdkVersion 和 TargetVersion 均低于 13 时,此选项不会导致 Activity 重启,否则会导致 Activity 重启(API 13 新添加)
layoutDirection:当布局方向发生改变,这个属性用的比较少,正常情况下无须修改布局的 layoutDirection 属性(API 17 新添加)
对于转屏的情况,测试发现需要配置android:configChanges="orientation|screenSize"
才可以避免转屏时的重建,少一个也不行,另外现在高版本上的横竖屏切换都会只走一遍生命周期,并没有像以前说的竖切横会走一遍,横切竖会走两遍,可能以前低版本会有这个效果。
onBackPressed():这个方法就是用户按下手机的 Back 返回键时被触发。默认实现是直接 finish 掉当前 Activity,当然你可以选择覆写该方法来实现你想要的结果。例如,很多 app(如微信)采取的效果都是是点击 Back 键直接回到桌面但是不会 finish 掉当前 Activity,然后再点击应用图标时,会直接回到之前的 Activity 界面,这种效果是怎么实现的呢?
@Override
public void onBackPressed() {
Intent home = new Intent(Intent.ACTION_MAIN);
home.addCategory(Intent.CATEGORY_HOME);
startActivity(home);
}
这种方式是通过模拟 Home 键效果强行影响到 Back 键对 Activity 生命周期的影响。也可以使用下面的方式:
@Override
public void onBackPressed() {
moveTaskToBack(true);
}
moveTaskToBack()是将当前 Activity 所在的 Task 移到后台,同时保留 activity 顺序和状态。
onKeyDown(): 这个方法是当系统按键被按下时触发,在 Activity 当中开发中最主要的作用是用来拦截 back 返回键(虽然 KeyEvent 中有提供其他按键的 Code 值,但一般除了 back 键以外其他系统键基本无法拦截比如 Home 键、音量键、导航键等)。
常见的两次按下 back 键退出应用(如今日头条)的监听代码:
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
long currentTime = System.currentTimeMillis();
// 如果时间间隔大于 2 秒,不处理
if ((currentTime - preTime) > 2000) {
Toast.makeText(this, "再按一次退出程序!", Toast.LENGTH_SHORT).show();
//更新时间
preTime = currentTime;
//截获事件,不再处理
return true;
}
}
System.exit(0);
return super.onKeyDown(keyCode, event);
}
其实在 onKeyDown()的源码中默认实现会调用 onBackPressed(),所以如果你在 onKeyDown()方法中拦截了 back 键的话,onBackPressed()方法就没必要重写了。
onUserInteraction():这个方法是用户与 Activity 有交互时会被触发,当 Activity 处于前台时,任意的系统按键(Home 键、Back 键、音量键等)、Touch 事件以及其他会被分发到 Activity 上的事件触发时会回调这个方法。在 Activity 的源码中搜索一下你会发现,其实在每个 dispatchXXXEvent 的方法中会先调用这个方法。官方注释说这个方法是为了让你更加智能的处理通知栏的通知,比如用户与 Activity 有交互时在该方法中取消通知。该方法会伴随 onUserLeaveHint 方法一起调用,如果触发 onUserLeaveHint 一定会先调用该方法。另外,需要注意的是在 Touch 事件中,只有 Action Down 才会触发该方法,Action Move 和 Action Up 不会触发。但是对于系统按键按下电源键熄屏/开屏时并不会触发该方法。总之,这个方法为我们提供了一种依据:用户正在与我们的应用进行着交互。
onUserLeaveHint():这个方法是用户主动选择让当前 Activity 回到后台的时候被调用,比如用户按下 Home 键回到桌面。注意这个方法只有是由于用户的主动选择而导致当前 Activity 回到后台时才会调用,由于系统导致而非用户主动选择导致的回到后台的情况不会调用,比如系统来电时会将电话页面带到当前 Activity 前台,这时不会调用该方法,或者被系统的其他弹窗遮挡也不会触发该方法。还有就是这个方法是针对 Activity 而不是整个 app, 当你在当前 Activity 启动另一个 Activity 时,当前 Activity 会调用该方法。当你调用 finish 或按 back 键时,也不会调用该方法,因为这时不是回到后台而是直接干掉了。能确定的一点是该方法被调用时它一定是在 onUserInteraction 方法之后,并在 onPause 方法之前被调用。对的,它会伴随 onUserInteraction 一起调用。如果我们想用这个方法来大致的判断 Home 键是否被按下,需要在我们自己应用内 startActivity 的时候给 Intent 加上
Intent.FLAG_ACTIVITY_NO_USER_ACTION
标志就可以,这样当你 startActivity 时当前 Activity 就不会触发 onUserLeaveHint 方法,从而可以大致的判断是由于用户按下了 Home 键导致的。但是这个判断只是大致,并不能精确的判断 Activity 回到后台只是由于用户按下 Home 键才导致的,比如我们下拉通知栏,从通知栏中点开一个新的页面也会触发该方法,还有用户在当前 Activity 打开系统最近使用过的 app 菜单时也会触发该方法。onNewIntent():这个方法跟 Activity 的启动模式有关,只有在 AndroidManifest.xml 中配置了
android:launchMode="singleTop"
或android:launchMode="singleTask"
或android:launchMode="singleInstance"
,或者在 startActivity 的时候 Intent 添加了Intent.FLAG_ACTIVITY_SINGLE_TOP
或Intent.FLAG_ACTIVITY_NEW_TASK
或Intent.FLAG_ACTIVITY_CLEAR_TOP
等标志的时候才可能会调用,关于四种启动模式这里就不展开了。如果一个 Activity 配置了 singleTask 或 singleInstance,那么只要在栈中已经存在该实例了,再次 startActivity 打开该 Activity 时就不会走 onCreate 而会走 onNewIntent 方法;如果一个 Activity 配置了 singleTop,再次 startActivity 打开该 Activity 时如果该 Activity 实例已经在栈顶,那么也会走 onNewIntent 方法而非 onCreate。这时的生命周期会按照:onNewIntent()—>onRestart()—>onStart()—>onResume()或者 onNewIntent()—>onResume() 的顺序调用(取决于再次回来前是否是完全可见)。需要注意的是,该方法被调用时,通过 getIntent 方法获取到的还是原始的第一次创建传递的 Intent,这也是为啥有的道友在 onStart 或者 onResume 方法中死活拿不到新传递的 intent 的原因了(其实这点还是蛮坑人的),当然官方注释也说明了解决方法,在 onNewIntent 方法中调用 setIntent 方法来更新 intent 就可以了,像下面这样:
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
看了下 setIntent 方法内部是直接赋值的,如果你还想使用最原始的 Intent 中的信息,最好将原始的 Intent 保存一份。
onLowMemory():当系统低内存时可能会调用该方法,具体的调用时机未知,一般是系统所有的后台进程都被杀死时,你需要在该方法中进行一些缓存资源或任何非必要资源的释放,在该方法之后系统的垃圾回收器会为你释放这些内存。当然从 Android 4.0 开始为了避免这种情况,可以选择在 AndroidManifest.xml 中 Application 标签上添加
android:largeHeap="true"
属性,这样的话系统会增大 app 的最大允许使用的内存峰值,可以使用的最大内存值由系统决定的,具体的是在 /system/build.prop 文件中的 dalvik.vm.heapsize 配置值决定的。onTrimMemory():这个方法是跟 onLowMemory()差不多,是从 API level 14 Android 4.0 开始添加的,只不过这个方法有一个 level 参数,更加明确了内存不足的状态等级,你可以在该方法中根据不同的内存状态等级来选择清理内存或者资源释放。OnTrimMemory 的触发比 onLowMemory()会更加的频繁,满足不同等级的条件时都会触发。
onTrimMemory 的 level 参数是一个 int 数值,代表不同的内存状态, 取值为下面的常量:
TRIM_MEMORY_COMPLETE // 内存不足,并且该进程在后台进程 LRU 列表的最后一个,马上就要被清理
TRIM_MEMORY_MODERATE // 内存不足,并且该进程在后台进程 LRU 列表的中部
TRIM_MEMORY_BACKGROUND // 内存不足,该进程已进入后台进程 LRU 列表
TRIM_MEMORY_UI_HIDDEN // 内存不足,该进程的 UI 对用户已经不可见,需要清理 UI 资源
TRIM_MEMORY_RUNNING_CRITICAL // 内存极度不足,无法保持任何后台进程,之后会调用 onLowMemory()
TRIM_MEMORY_RUNNING_LOW // 内存不足,低度,需要清理无用内存资源
TRIM_MEMORY_RUNNING_MODERATE // 内存不足,适中,需要清理无用内存资源
finalize():这个方法其实是 Java 的 Object 对象的一个方法,之所以在这里列出来是因为可以通过该方法检测你的 Activity 是否有内存泄漏。当系统虚拟机认为一个对象没有被任何对象引用,也没有被任何线程引用的必要时,垃圾回收器就会回收该对象,这时就会调用此方法。该方法被调用时不一定在 UI 线程。对同一个对象而言该方法至多只会被调用一次,而且一定是最后被执行的方法,在该方法之后直到垃圾回收器回收该对象之前不会再有其他行为了。该方法的设计意图是为了使得用户可以 clean up 一些资源。因为一个对象如果被系统回收的话一定会最终调用该方法,而所有的对象都是 Object 的子类,Activity 自然也是 Object 的子类,因此可借此来检测 Activity 是否有内存泄漏,可以在该方法中加 log, 如果发现你的 Activity 关闭之后最终该方法没有被调用,那么你就要注意了。
常见 Activity 生命周期的场景
1.启动 Activity:
**onCreate —> onContentChanged —> onStart —> onPostCreate —> onStateNotSaved
—> onResume —> onPostResume —> onAttachedToWindow —> onWindowFocusChanged**
2.启动 Activity 后按返回键或调 finish 关闭:
**onUserInteraction —> onUserInteraction —> onWindowFocusChanged —> onPause —> onStop
—> onDestroy —> onDetachedFromWindow —> finalize**
3.按下 Home 键:
**onWindowFocusChanged —> onUserInteraction —> onUserLeaveHint —> onPause
—> onSaveInstanceState —> onStop**
4.按下 Home 键再返回来:
**onStateNotSaved —> onRestart —> onStart —> onResume —> onPostResume
—> onWindowFocusChanged**
5.从当前 Activity 点击按钮启动另一个 Activity:
先走当前 Activity:
**onUserInteraction —> onWindowFocusChanged —> onUserInteraction —> onUserLeaveHint
—> onPause**
再走另一个 Activity:
**onCreate —> onContentChanged —> onStart —> onPostCreate —> onStateNotSaved
—> onResume —> onPostResume —> onAttachedToWindow —> onWindowFocusChanged**
最后再走当前 Activity:
onSaveInstanceState —> onStop
6.当前 Activity 启动另一个 Activity 后从另一个 Activity 按 back 键回来:
评论