博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android进阶之Activity生命周期+Activity难点
阅读量:2192 次
发布时间:2019-05-02

本文共 8768 字,大约阅读时间需要 29 分钟。

1 概要

Activity是一种展示型组件,主要是向用户展示一个界面,并且可以接收用户的输入信息从而和用户进行交互。对用户来说,Activity就是Android应用的全部,因为其他三大组件对用户来说是不可感知的。

2 Activity的生命周期图(正常生命周期)

只有Activity之间的转换才会调用生命周期函数。

在这里插入图片描述

2.1 启动一个新的 Activity 后,onStart ——> onResume 都会执行,那什么时候会执行 onStart,什么时候接着执行 onResume 呢?

(1)Activity 启动后,我们会看到界面,然后可以点击界面上的按钮,这时候是不是分成了二大块:

  1. 看得到我们写 Activity 的界面
  2. 然后可以操作我们的界面

所以 (onStart - onStop) 和界面 是否可见 有关(onResume - onPause)和界面 是否位于前台、是否可以操作 有关

(2)onRestart 表示 Activity 重新启动,当前 Activity 从不可见重新变为可见状态时,onRestart() 会被回调。例如:用户按Home切换到桌面。

2.2 假设当前Activity为A,这时用户打开一个新的Activity B,那么B的onResume和A的onPause哪个先执行呢?

在这里插入图片描述

(1)简单理解,启动Activity的请求会由Instrumentation来处理,然后他通过Binder向AMS发请求,AMS内部维护着一个ActivityStack并负责栈内的Activity的状态同步,AMS通过ActivityThread去同步Activity的状态从而完成生命周期方法的调用。

(2)在ActivityStack中的resumeTopActivityLnnerLocked方法中,有这么这段代码,总结:在新 Activity 启动之前,栈顶的Activity 需要先调用 onPause 后,新的Activity才能启动

// we need to start pausing the current activity so the top one can be resumedboolean dontWaitForPause = (next.info.flags& ActivityInfo.FLAG_RESUME_WHILE_PAUSING)!=0;boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, KeyStore.TrustedCertificateEntry,dontWaitForPause); if(mResumedActivity != null){
pausing != startPausingLocked(userLeaving,false,true,dontWaitForPause); if(DEBUG_STATES){
Slog.d(TAG,"resumeTopActivityLocked:pausing" + mResumedActivity); }}

(3)最终,在 ActvityStackSupervisor 中的 realStartActivityLocked 方法中,会调用如下代码:

app.thread.scheduleLaunchActivity(new Intent(r.intent),r.appToken,System.identityHashCode(r),r.info,new Configuration(mService.mConfiguration)        ,r.compat,r.task.voiceInteractor,app.repProcState,r.icicle,r.persistentState,results,        new Intents,!andResume,mService.isNextTransitionForward() ,profilerInfo);

在这个 app.thread 的类型是 IApplicationThread 的具体实现是 ActivityTread 中的 ApplicationThread 。所以,这段代码实际上调用了 ActivityThread 当中的 scheduleLaunchActivity 方法,最终会完成新 Activity 的 onCreate、onStrrt、onResume 的调用过程。因此可以得出结论是:旧 Activity 先 onPause,然后新的 Activity 再启动

(4)至于 ApplicationThread 的 scheduleLaunchActivity 方法为什么会完成新 Activity 的生命周期,scheduleLaunchActivty() 最终调用 handlerLaunchActivity() 方法,请看代码:

private void handlerLaunchActivity(ActivityClientRecord r, Intent customIntent){        //if we are getting ready to gc after going to the background,well we are back active so skip it        unscheduleGcIdler();        mSomeActivitiesChanged =true;        if(r.profilerInfo != null){            mProfiler.setProfiler(r.profilerInfo);            mProfiler.startProfiling;        }        handlerConfigurationChanged(null,null);        if(localLOGV)Slog.v(TAG,"Handling launch of"+r);                // 在这里新Activity被创建出来,其onCreate和onStart被调用        Activity a = PerformLaunchActivity(r,customIntent);        if(a != null){            r.createdConfig = new Configuration(mConfiguration);            Bundle oldState = r.start;            // 在这里新Activity的onResume会被调用            handlerResumeActivity(r.token,false,r.isForward, !r.activity.mFinished && r.startsNotResumed);        }        //省略...}

2.4 如果从 A 这个 Activity ,跳到了 B 这个 Activity,那二个 Activity 的 (onStart - onStop) 和(onResume - onPause)又分别如何执行。

A(onPause) -> B(onCreate)->B(onStart) —> B(onResume) -> A(onStop)

如果从一个 Activity 跳转到另外一个 Activity 之前,要做一些操作的话,最好是放在 onStop 中,因为如果放在 onPause 中的话,会影响新的 Activity 启动速度

2.5 半遮挡情况

(1)特殊情况,在启动一个半遮挡/透明主题/透明的Dialog样式的Activity,当前 Activity 不会调用 onStop()

(2)注意:弹出当前 Activity 的 Dialog 对话框属于它的组件,是不会引起生命周期变化的

2.6 全遮挡情况

(1)用户正常打开新的 Activity 时,原 Activity 的回调是:onPause()–>onStop()

在这里插入图片描述
(2)用户从新 Activity 再次返回原 Activity 时,原 Activity 的回调是:onRestart()–>onStart()–>onResume()
在这里插入图片描述

2.7 按主菜单键情况

(1)按主菜单键返回桌面时,回调是:onPause()–>onStop()

(2)再点击 App 返回退出时的Activity,回调是:onRestart()–>onStart()–>onResume()

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

3.1 屏幕旋转等系统配置发生改变,导致 Activity 被杀死并重新创建

(1)异常生命周期如图

在这里插入图片描述

(2)保存和默认恢复 View 是如何实现的?

当 Activity 在异常情况下需要重新创建时,系统会默认保存当前的 Activity 视图架构,并且恢复这些数据。比如:EditText 中的数据、ListView滚动的位置,这些View相关的状态系统都会默认恢复。

系统的工作流程是这样的:Activity 被意外终止时,Activity 会调用 onSaveInstanceState 去保存数据,然后委托 Window 去保存数据,接着 Window 再委托它的顶级容器 DecorView(ViewGroup) 去保存数据,最后顶层容器再去通知它的子 View 来保存数据,这样整个数据保存过程就完成了,详细看 Activity 的 onSaveInstanceState 源码。同理,恢复数据也是类似的流程。

(3)onSaveInstanceState保存,onRestoreInstanceState 恢复数据的案例?

public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (savedInstanceState != null) {
String test = savedInstanceState.getString("extra_test"); Log.i(TAG, test); } } @Override public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState); String content = "test"; Log.d(TAG, "onSaveInstanceState extra_test:" + content); outState.putString("extra_test", content); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState); String test = savedInstanceState.getString("extra_test"); Log.d(TAG, test); }}
MainActivity: onConfigurationChanged, newOrientation:1MainActivity: onPauseMainActivity: onSaveInstanceState extra_test:testMainActivity: onStopMainActivity: onDestroyMainActivity: [onCreate]restore extra_test:testMainActivity: onStartMainActivity: [onRestoreInstanceState]restore extra_test:testMainActivity: onResume

(4)建议在 onCreate(Bundle savedInstanceState) 中恢复数据

常状态保存和恢复,是在 onSaveInstanceState 方法中保存 Activity 状态、数据;建议在onCreate(Bundle savedInstanceState) 中恢复,因为 onRestoreInstanceState 方法不一定执行。

(5)onSaveInstanceState 保存数据有限,不可作为持久化使用?

Activity的onSaveInstanceState()方法不是Activity生命周期方法,也不保证

一定会被调用。它是用来在Activity被意外销毁时保存UI状态的,只能用于保存临时性数据,例如UI控件的属性等,不能跟数据的持久化存储混为一谈。持久化存储应该在Activity的onPause()/onStop()中实行,在onCreate(Bundle savedInstanceState)中恢复

3.1.1 监听系统配置变化,并且不让 Activity 重新创建

(1)onConfigurationChanged(Configuration newConfig)方法的作用?(newConfig:新的设备配置信息)

只有在配置文件 AndroidManifest 中设置了 configChanges 属性对应的设备配置,当系统的配置信息发生改变时,系统会调用此方法。如果发生设备配置与在配置文件中设置的不一致,则Activity会被销毁重建。

(2)如何修改配置,不让 Activity 重新创建?

当屏幕方向发生改变时,Activity 默认会被销毁重建。如果AndroidManifest 文件中处理屏幕方向配置信息如下: configChanges = orientation,则 Activity 不会被销毁重建,而是调用 onConfigurationChanged 方法

在这里插入图片描述
如果 configChanges 只设置了 orientation,当其他设备配置信息改变时,Activity 依然会销毁重建,且不会调用 onConfigurationChanged 方法。例如,在上面的配置的情况下,如果语言改变了,Activity 就会销毁重建,且不会调用 onConfigurationChanged 方法。

(3)configChanges 的取值表

在这里插入图片描述

如果 targetSdkVersion 的值小于 13,则只要配置:

android:configChanges="orientation"

如果 targetSdkVersion 的值大于等于 13,则如下配置才会回调 onConfigurationChanged 方法,因此适配全面需要以下的配置:

// orientation横竖屏切换,screenSize屏幕大小变化android:configChanges="orientation|screenSize"

(4)扩展:接入一个外设键盘时,如何规避Activity 的销毁和创建了两次

当用户接入一个外设键盘时,其中一次的销毁重建是因为外设键盘的插入和拔出。当设置android:configChanges="keyboardHidden|keyboard"之后,就不会销毁重建,而是调用 onConfigurationChanged 方法。另一次是,当接入外设键盘时,除了键盘类型的改变,触摸屏也发生了变化。因为使用外设键盘,触摸屏不能使用了。

所以,要规避 Activity 的销毁和创建了两次,且回调 onConfigurationChanged 方法, configChanges 属性配置如下:

在这里插入图片描述
(5)学习链接

3.2 资源内存不足导致低优先级的 Activity 被杀死

(1)内存不足时候杀死优先级低的 Activity,这时候的数据存储和恢复过程和我们上面讲的也是一样的

(2)那 Activity 的具体的优先级怎么样的呢:

1、前台Activity:正在和用户交互的Activity,优先级最高2、可见但非前台Activity:比如对话框,导致Activity可见但是位于后台无法和用户直接交互3、后台Activity:已经被暂停的Activity,比如执行了onStop,优先级最低

(3)我们可以看到后台 Activity 很容易被杀死,所以一些后台工作更适合放到 Service 中去,这样保证优先级,不会轻易被系统杀死

4 setResult 方法难点

4.1 setResult 方法的调用时机

(1)调用时机分析

// 1、重写onBackPressed()方法,捕获BACK事件,捕获到之后先setResult@Overridepublic void onBackPressed() {	Log.i(TAG, "onBackPressed");	setResult(Const.LIVE_OK);	super.onBackPressed(); }
// 2、按点击事件中显式的调用finish()setResult(RESULT_OK);finish();
// ActivityB 回退到 ActivityA 过程中,执行过程是B---onBackPressedB---setResult B---finishB---onPause (前)A---onActivityResult  // A 的 onActivityResult 需要在 B 的 onPause 之后,A 的 onRestart 之前调用A---onRestartA---onStartA---onResumeB---onStopB---onDestroy

结论1:Activity A 的 onActivityResult 需要在 B 的 onPause 之后,A 的 onRestart 之前调用,所以 B 的 setResult 方法应该在 B 的 onPause 之前调用.

结论2:Activity A 回调结果是在被 finish 之后,也就是说 Activity B 调用 setResult 方法必须在 finish 之前。当然在 onCreate 就调用 setResult 肯定是在 finish 之前的,但是又不满足业务需要。

(2)学习链接

4.2 startActivityForResult 启动 singleTask 的 Activity,则 onActivitResult 方法立即回调且 resultCode 为 RESULT_CANCEL

4.2.1 问题分析与

(1)ActivityA–>XSTChatActivity,launchMode=“singleTask”(singleInstance),则会在 LaunchFlags 中加入FLAG_ACTIVITY_NEW_TASK 标志,启动后 A 的 onActivitResult 立即回调 resultCode 为 RESULT_CANCEL;原因–链接

(2)ActivityA–>XSTChatActivity,setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),A 的 onActivityResult 不会立即回调,但是在 setResult(RESULT_OK) + finish() 返回后,A 的 onActivitResult() 回调 resultCode 为 RESULT_CANCEL。

4.2.2 解决方案

(1)所以:在XSTChatActivity返回时,设置 setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP) 重新启动A,接着在 A 的 onNewIntent() 获取内容。

(2)其他解决方案:类似 EventBus 的观察者模式修改。

4.2.3 学习链接

转载地址:http://amcub.baihongyu.com/

你可能感兴趣的文章
作为一名软件测试工程师,需要具备哪些能力
查看>>
【Pyton】【小甲鱼】类和对象:一些相关的BIF(内置函数)
查看>>
【Pyton】【小甲鱼】魔法方法
查看>>
单元测试需要具备的技能和4大阶段的学习
查看>>
【Loadrunner】【浙江移动项目手写代码】代码备份
查看>>
Python几种并发实现方案的性能比较
查看>>
[Jmeter]jmeter之脚本录制与回放,优化(windows下的jmeter)
查看>>
Jmeter之正则
查看>>
【JMeter】1.9上考试jmeter测试调试
查看>>
【虫师】【selenium】参数化
查看>>
【Python练习】文件引用用户名密码登录系统
查看>>
学习网站汇总
查看>>
【Python】用Python打开csv和xml文件
查看>>
【Loadrunner】性能测试报告实战
查看>>
【自动化测试】自动化测试需要了解的的一些事情。
查看>>
【selenium】selenium ide的安装过程
查看>>
【手机自动化测试】monkey测试
查看>>
【英语】软件开发常用英语词汇
查看>>
Fiddler 抓包工具总结
查看>>
【雅思】雅思需要购买和准备的学习资料
查看>>