大家好,我是考100分的小小码 ,祝大家学习进步,加薪顺利呀。今天说一说activity的生命周期分为几种状态_安卓activity的生命周期,希望您对编程的造诣更进一步.
一、前言
对于 Android 开发者而言,Activity 生命周期执行顺序并不陌生。但总是如此吗?它的生命周期会不会在某些场景下发生改变呢?下面我们一起来看看,这些熟悉的东西为何不一样,我们又有什么应对策略。
二、常见的生命周期函数
Google 官网提供的 Activity 生命周期流程如图 2-1 所示:
图 2-1 Activity 生命周期流程图[1] 首先我们来看看常见的生命周期函数,有 onCreate()、onStart()、onResume()、onPause()、onStop()、onDestory() 等:
-
onCreate() 是在系统首次创建 Activity 时触发,该回调在 Activity 的整个生命周期中只应该发生一次。onCreate() 方法执行完成后,Activity 进入 “已开始” 状态,系统会触发 onStart() 回调;
-
onStart() 表示 “已开始” 状态。一旦该方法被调用,代表 Activity 对用户可见,应用会为 Activity 进入前台并且与用户互动做准备。onStart() 会非常快速的完成,一旦结束此回调,Activity 便会进入 “已恢复” 状态,系统将调用 onResume() 方法;
-
onResume() 是应用与用户互动的状态,我们称为 “已恢复” 状态。应用会一直保持这种状态,直到某些事情发生,让焦点远离应用。此类事件包括:接到来电、用户导航到另一个 Activity、设备屏幕关闭等。当发生中断事件时,Activity 进入 “已暂停” 状态,系统调用 onPause() 回调;
-
onPause() 视为用户将要离开 Activity 的第一个标志(尽管这并不总是意味着 Activity 会被销毁),onPause() 方法的完成并不意味着 Activity 离开 “已暂停” 状态。相反,Activity 会保持此状态,直到其恢复或变成对用户完全不可见。如果 Activity 恢复,系统将再次调用 onResume() 回调。如果 Activity 从 “已暂停” 状态返回 “已恢复” 状态,系统会让 Activity 实例继续驻留在内存中,并调用该实例的 onResume() 方法。如果 Activity 变为完全不可见,系统会调用 onStop() 方法;
-
如果 Activity 不再对用户可见,说明进入了 “已停止” 状态,因此系统将触发 onStop() 回调。例如:当新启动的 Activity 覆盖整个屏幕时,可能会发生这种情况。在 onStop() 方法中,应用应释放或调整对用户不可见时的无用资源,同时还应在 onStop() 中执行 CPU 相对密集的关闭操作。进入“已停止” 状态后,Activity 要么返回与用户互动,要么结束运行并消失。如果 Activity 返回,系统将调用 onRestart()。如果 Activity 结束运行,系统将调用 onDestroy();
-
在销毁 Activity 之前,系统会先调用 onDestroy()。如果 Activity 即将结束,onDestroy() 是 Activity 收到的最后一个生命周期回调。如果由于配置变更而调用 onDestroy(),系统会立即新建 Activity 实例,然后在新配置中为新实例调用 onCreate()。
上面介绍了常见情况下 Activity 的生命周期,但当我们使用 TabActivity 时却不一样,下面我们一起来看看。
三、TabActivity
3.1 如何使用
TabActivity 作用主要是使每个 Tab 能对应一个 Activity(当然现在一般是使用 Fragment),效果如图 3-1 所示:
图 3-1 使用 TabActivity 效果图 我们现在来看如何使用 TabActivity。
首先,定义一个页面 ParentActivity,让它继承 TabActivity,然后获取 TabActivity 中的 TabHost,示例代码如下:
public class ParentActivity extends TabActivity {
private TabHost mTabHost;
private final static String TAG = "SA.ParentActivity";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate");
//获取 TabActivity 中的 TabHost
mTabHost = getTabHost();
//这里用于添加具体的 Tabs,并用 Tab 切换到对应的 Activity
addFirstTab();
addSecondTab();
addThirdTab();
addFourthTab();
}
public void addFirstTab(){
Intent intent = new Intent();
intent.setClass(ParentActivity.this, FirstChildActivity.class);
TabHost.TabSpec spec = mTabHost.newTabSpec("One");
spec.setIndicator("one", null);
spec.setContent(intent);
mTabHost.addTab(spec);
}
public void addSecondTab(){
Intent intent = new Intent();
intent.setClass(ParentActivity.this, SecondChildActivity.class);
TabHost.TabSpec spec = mTabHost.newTabSpec("Two");
spec.setIndicator("two", null);
spec.setContent(intent);
mTabHost.addTab(spec);
}
public void addThirdTab(){
Intent intent = new Intent();
intent.setClass(ParentActivity.this, ThirdChildActivity.class);
TabHost.TabSpec spec = mTabHost.newTabSpec("Three");
spec.setIndicator("three", null);
spec.setContent(intent);
mTabHost.addTab(spec);
}
public void addFourthTab(){
Intent intent = new Intent();
intent.setClass(ParentActivity.this, FourthChildActivity.class);
TabHost.TabSpec spec = mTabHost.newTabSpec("Four");
spec.setIndicator("four", null);
spec.setContent(intent);
mTabHost.addTab(spec);
}
}
然后,创建 FirstChildActivity 等页面,这里正常创建即可,不再赘述。
现在进入正题,我们在 ParentActivity 及每个 ChildActivity(FirstChildActivity、SecondChildActivity 等每个子页面的统称)中的生命周期函数打印日志。
当我们切换 ChildActivity 时,会看到生命周期与正常情况下打开 Activity 的生命周期不一样,下面我们进一步分析原因。
3.2生命周期详情
我们来看看在不同情况下,ParentActivity 和每个 ChildActivity 的生命周期。在冷启动时,默认选中 FirstChildActivity,生命周期流程如图 3-2 所示:
图 3-2 冷启动生命周期流程图
可以看到,冷启动时两个 Activity 的生命周期交替执行。我们再切换到 SecondChildActivity,生命周期流程如图 3-3 所示:
图 3-3 首次切换到 SecondActivity 生命周期流程图
此时 FirstChildActivity 的 onStop 生命周期就不会执行了,最后我们再从 SecondChildActivity 切回到 FirstChildActivity,如图 3-4 所示:
这个时候 FirstChildActivity 的 onStart 生命周期不会执行,SecondChildActivity 的 onStop 也不会执行。明显与正常情况下的生命周期不一致,接下来我们一起分析原因。
3.3源码分析
为什么使用 TabActivity 时,会有不一样的 Activity 生命周期呢?下面我们一起通过源码分析下原因。首先来看看 TabActivity 的源码。
public class TabActivity extends ActivityGroup {
private TabHost mTabHost;
// ...
@Override
public void onContentChanged() {
super.onContentChanged();
// 1.初始化 TabHost
mTabHost = findViewById(com.android.internal.R.id.tabhost);
if (mTabHost == null) {
throw new RuntimeException(
"Your content must have a TabHost whose id attribute is " +
"'android.R.id.tabhost'");
}
// 2.TabHost 与 LocalActivityManager 绑定
mTabHost.setup(getLocalActivityManager());
}
private void ensureTabHost() {
if (mTabHost == null) {
this.setContentView(com.android.internal.R.layout.tab_content);
}
}
@Override
protected void onPostCreate(Bundle icicle) {
super.onPostCreate(icicle);
ensureTabHost();
if (mTabHost.getCurrentTab() == -1) {
// 3.默认选中第一个 Tab
mTabHost.setCurrentTab(0);
}
}
public TabHost getTabHost() {
ensureTabHost();
return mTabHost;
}
}
在 TabActivity 中,有三个关键点需要注意:
-
在 onContentChanged() 方法中初始化了 TabHost;
-
通过 getLocalActivityManager() 方法获取父类 ActivityGroup 中初始化的 LocalActivityManager,并且在 TabActivity 中通过 TabHost#setup() 方法绑定了 TabHost 和 LocalActivityManager;
-
在 onPostCreate() 方法中,通过 mTabHost.setCurrentTab(0) 默认选中了第一个 Tab。
这里我们仍然没有看到 Activity 生命周期不一样的原因,其父类 ActivityGroup 也只是初始化了 LocalActivityManager 对象,接下来重点看看 mTabHost.setCurrentTab(0) 这一步做了什么。
public class TabHost extends FrameLayout implements ViewTreeObserver.OnTouchModeChangeListener {
//...
public void setCurrentTab(int index) {
// ...
// 关掉上一个页面
if (mCurrentTab != -1) {
mTabSpecs.get(mCurrentTab).mContentStrategy.tabClosed();
}
mCurrentTab = index;
// 获取当前页面的 view
mCurrentView = spec.mContentStrategy.getContentView();
if (mCurrentView.getParent() == null) {
mTabContent
.addView(
mCurrentView,
new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
}
// ...
}
}
这里利用 TabHost.IntentContentStrategy#getContentView() 获取了当前页面的 View,那么 getContentView() 中又做了什么呢,我们来看看它的源码。
public View getContentView() {
if (mLocalActivityManager == null) {
throw new IllegalStateException("Did you forget to call 'public void setup(LocalActivityManager activityGroup)'?");
}
// 利用 LocalActivityManager 启动了 Activity
final Window w = mLocalActivityManager.startActivity(
mTag, mIntent);
final View wd = w != null ? w.getDecorView() : null;
if (mLaunchedView != wd && mLaunchedView != null) {
if (mLaunchedView.getParent() != null) {
mTabContent.removeView(mLaunchedView);
}
}
mLaunchedView = wd;
// ...
return mLaunchedView;
}
利用 LocalActivityManager 启动了 Activity,我们接下来看看 LocalActivityManager 的 startActivity() 方法是如何实现的。
public Window startActivity(String id, Intent intent) {
// ...
// 在 ActivityGroup 初始化 LocalActivityManager 传入变量 mSingleMode
if (mSingleMode) {
LocalActivityRecord old = mResumed;
if (old != null && old != r && mCurState == RESUMED) {
// 改变 FirstChildActivity 页面的状态
moveToState(old, STARTED);
}
}
// ...
// 改变 SecondChildActivity 页面的状态
moveToState(r, mCurState);
if (mSingleMode) {
mResumed = r;
}
return r.window;
}
上面展示了部分 startActivity 的源码。其中,mSingleMode 是在 ActivityGroup 初始化 LocalActivityManager 时传入的变量,且始终为 true。假设现在从 FirstChildActivity 切换到了 SecondChildActivity,那么首先利用 moveToState() 方法改变了 FirstChildActivity 的状态,紧接着修改了 SecondChildActivity 状态。
真相应该在 moveToState() 方法中了,我们来看看 moveToState() 的实现。
private void moveToState(LocalActivityRecord r, int desiredState) {
//...
// 如果是第一次启动 ChildActivity
if (r.curState == INITIALIZING) {
// 启动当前 ChildActivity
r.activity = mActivityThread.startActivityNow(
mParent, r.id, r.intent, r.activityInfo, r, r.instanceState, instance);
if (r.activity == null) {
return;
}
r.curState = STARTED;
// ...
return;
}
// 假设从 FirstChildActivity 切换到了 SecondChildActivity
switch (r.curState) {
//...
//SecondChildActivity 的状态,由 STARTED 到 RESUMED
case STARTED:
if (desiredState == RESUMED) {
// Need to resume it...
if (localLOGV) Log.v(TAG, r.id + ": resuming");
mActivityThread.performResumeActivity(r, true, "moveToState-STARTED");
r.instanceState = null;
r.curState = RESUMED;
}
// ...
return;
// FirstChildActivity 的状态,由 RESUMED 到 STARTED
case RESUMED:
if (desiredState == STARTED) {
if (localLOGV) Log.v(TAG, r.id + ": pausing");
performPause(r, mFinishing);
r.curState = STARTED;
}
// ...
return;
}
}
在 moveToState() 方法中,分为首次启动当前 ChildActivity 还是非首次启动的情况:
-
在首次启动时,调用 ActivityThread#startActivityNow() 方法启动 Activity;
-
当非首次进入 ChildActivity 时,还是假设从 FirstChildActivity 切换到了 SecondChildActivity。FirstChildActivity 会调用 performPause 方法并触发 onPause() 生命周期,而 SecondChildActivity 会调用 performResumeActivity 方法,只会触发 onResume() 生命周期。
看到这里,就解释了为什么利用 TabActivity 的生命周期会如此不一样,原来都是 ActivityGroup 中 LocalActivityManager 的 “错”。
3.4 小结
我们来总结一下,为什么在 Activity 的不同阶段,会触发 Activity 的生命周期函数。通过我们上面的分析,也可以窥知一二,原因都是系统调用了触发生命周期的相关方法。如果在某些情况下,系统没有调用这些方法,那么生命周期函数就 “失灵” 了,这也是使用 TabActivity 导致生命周期不一致的根本原因。
TabActivity 如此特别,我们有方法判断它吗?答案是有的。
我们可以回顾一下 LocalActivityManager 启动 Activity 的过程:调用 ActivityThread#startActivityNow() 方法时,传入了 ParentActivity 参数。而我们平常利用 ContextWrapper#startActivity() 的方式启动 Activity,此时的 Activity 是没有 ParentActivity 的。因此,可以利用 Activity#getParent() 方法判断当前 Activity 有无 Parent,从而判断出此种特殊情况。
四、总结
本文介绍了 Activity 的生命周期,然后以 TabActivity 为例介绍了一种不一样的生命周期。最后,也欢迎大家一起讨论、学习。所谓君子之学,未尝离行以为知也,必矣。
参考文献:
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/13708.html