本文共 8768 字,大约阅读时间需要 29 分钟。
Activity是一种展示型组件,主要是向用户展示一个界面,并且可以接收用户的输入信息从而和用户进行交互。对用户来说,Activity就是Android应用的全部,因为其他三大组件对用户来说是不可感知的。
只有Activity之间的转换才会调用生命周期函数。
(1)Activity 启动后,我们会看到界面,然后可以点击界面上的按钮,这时候是不是分成了二大块:
所以 (onStart - onStop) 和界面 是否可见 有关, (onResume - onPause)和界面 是否位于前台、是否可以操作 有关。
(2)onRestart 表示 Activity 重新启动,当前 Activity 从不可见重新变为可见状态时,onRestart() 会被回调。例如:用户按Home切换到桌面。
(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); } //省略...}
A(onPause) -> B(onCreate)->B(onStart) —> B(onResume) -> A(onStop)
如果从一个 Activity 跳转到另外一个 Activity 之前,要做一些操作的话,最好是放在 onStop 中,因为如果放在 onPause 中的话,会影响新的 Activity 启动速度。
(1)特殊情况,在启动一个半遮挡/透明主题/透明的Dialog样式的Activity,当前 Activity 不会调用 onStop()。
(2)注意:弹出当前 Activity 的 Dialog 对话框属于它的组件,是不会引起生命周期变化的。
(1)用户正常打开新的 Activity 时,原 Activity 的回调是:onPause()–>onStop():
(2)用户从新 Activity 再次返回原 Activity 时,原 Activity 的回调是:onRestart()–>onStart()–>onResume():(1)按主菜单键返回桌面时,回调是:onPause()–>onStop()。
(2)再点击 App 返回退出时的Activity,回调是:onRestart()–>onStart()–>onResume()。
(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)中恢复。(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)学习链接(1)内存不足时候杀死优先级低的 Activity,这时候的数据存储和恢复过程和我们上面讲的也是一样的。
(2)那 Activity 的具体的优先级怎么样的呢:
1、前台Activity:正在和用户交互的Activity,优先级最高2、可见但非前台Activity:比如对话框,导致Activity可见但是位于后台无法和用户直接交互3、后台Activity:已经被暂停的Activity,比如执行了onStop,优先级最低
(3)我们可以看到后台 Activity 很容易被杀死,所以一些后台工作更适合放到 Service 中去,这样保证优先级,不会轻易被系统杀死。
(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)学习链接
(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。
(1)所以:在XSTChatActivity返回时,设置 setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP) 重新启动A,接着在 A 的 onNewIntent() 获取内容。
(2)其他解决方案:类似 EventBus 的观察者模式修改。
转载地址:http://amcub.baihongyu.com/