还记得刚学Android那会,经常会说起Activity的七大生命周期:
onCreate,onRestart,onStart,onResume,onPause,onStop,onDestroy。
- Activity启动的时候会执行onCreate->onStart->onResume
- Activity被遮挡,但是依然可见时会执行onPause
- Activity不可见时,会执行onStop
- Activity在即将关闭的情况下会执行onDestroy方法。
所以会有相当一部分的Android开发者相信在Dialog弹出的时候Activity会执行onPause的生命周期(Dialog弹出的时候Activity确实被遮挡,并且是可见状态),那么到底是不是这样,我们可以做个demo验证一下这种case:
package com.netease.myapplication;
import android.app.Dialog;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG,"onCreate");
setContentView(R.layout.activity_main);
}
@Override
protected void onRestart() {
super.onRestart();
Log.i(TAG,"onRestart");
}
@Override
protected void onStart() {
super.onStart();
Log.i(TAG,"onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.i(TAG,"onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.i(TAG,"onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.i(TAG,"onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG,"onDestroy");
}
public void showDialog(View view) {
Dialog dialog = new Dialog(this);
TextView textView = new TextView(this);
textView.setText("这是一个弹窗");
dialog.addContentView(textView,new ViewGroup.LayoutParams(200,200));
dialog.show();
}
}
首先是建了一个Activity,在七大生命周期上面都打出log, 然后写一个Button的click方法,主要就是为了弹出Dialog。那我们先看Activity创建的生命周期:
很简单的一个过程,生命周期如下:
2021-01-23 16:42:57.413 10039-10039/com.netease.myapplication I/MainActivity: onCreate
2021-01-23 16:42:57.615 10039-10039/com.netease.myapplication I/MainActivity: onStart
2021-01-23 16:42:57.617 10039-10039/com.netease.myapplication I/MainActivity: onResume
嗯,预料之中的结果。接下来我们再弹出Dialog试试:
就只是简单的弹出一个Dialog(无视如此丑的弹窗),然后再看下生命周期:
发现Activity并没有执行任何的生命周期。既然不会调用生命周期,那么以前听说的弹出一个dialog会调用onPause的说法是从哪来的?这个问题暂且不论,我们先来看下Dialog的过程:
Dialog dialog = new Dialog(this);
TextView textView = new TextView(this);
textView.setText("这是一个弹窗");
dialog.addContentView(textView,new ViewGroup.LayoutParams(200,200));
dialog.show();
我们可以看到Dialog主要做了三步操作:
创建一个Dialog对象
将View通过addContentView添加进去
显示Dialog。
1. 首先第一步,创建一个Dialog对象
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
if (createContextThemeWrapper) {
if (themeResId == Resources.ID_NULL) {
final TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
themeResId = outValue.resourceId;
}
mContext = new ContextThemeWrapper(context, themeResId);
} else {
mContext = context;
}
//获取WindowManagerService
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
//初始化Dialog的window
final Window w = new PhoneWindow(mContext);
mWindow = w;
//给Window设置callback
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setOnWindowSwipeDismissedCallback(() -> {
if (mCancelable) {
cancel();
}
});
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
Dialog的构造方法里面主要做了以下几件事情:
- 创建Dialog的context
- 获取WindowManagerImpl
- 初始化Dialog的window,设置callback,将WindowManagerImpl和Dialog的Window关联起来(这里Dialog的window跟Activity的Window是不一致的,所以在一旦Dialog弹出来的时候,触摸事件就会被Dialog所在的Window所响应,这里不做展开)
- 初始化ListenersHandler,Dialog的操作会通过这个Handler响应。
2. Dialog的addContentView的操作:
public void addContentView(@NonNull View view, @Nullable ViewGroup.LayoutParams params) {
mWindow.addContentView(view, params);
}
会通过Window将当前的View add进去,PhoneWindow主要是初始化了DecorView,然后通过DecorView获取了里面的mContentParent控件,再将View添加到mContentParent里面。
3. Dialog的show过程
public void show() {
if (mShowing) {
//做一个屏障,防止多次弹出弹窗
if (mDecor != null) {
if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
}
mDecor.setVisibility(View.VISIBLE);
}
return;
}
mCanceled = false;
if (!mCreated) {
dispatchOnCreate(null);
} else {
// Fill the DecorView in on any configuration changes that
// may have occured while it was removed from the WindowManager.
final Configuration config = mContext.getResources().getConfiguration();
mWindow.getDecorView().dispatchConfigurationChanged(config);
}
//Dialog的onStart生命周期
onStart();
mDecor = mWindow.getDecorView();
...代码省略...
//将DecorView加入到WindowManagerImpl中
mWindowManager.addView(mDecor, l);
...代码省略...
//发送Show的消息
sendShowMessage();
}
show操作主要是将DecorView添加到WindowManagerImpl里面,然后调用WindowManagerGlobal,会创建ViewRootImpl的对象,最后执行ViewRootImpl的performMeasure,performLayout和performDraw方法,最终会将Dialog展示到屏幕上面。而最后一步就是执行onShow的回调方法。没有其他额外操作
至此Dialog的显示过程全部结束,流程比较简单。我们发现这里面并没有涉及到ActivityManagerService。另外对于Activty启动流程熟悉的也知道Activity的生命周期必须通过ActivityManagerService来分配调用。所以Dialog的显示自然不可能会影响Activity的生命周期。
既然如此,那么为什么会有这种说法呢?这事先从Activity的跳转到第二个Activity说起,关于Activity的具体启动流程这里不再赘述,在经过一系列的方法调用以后,最终会调用ActivityStarter#startActivity方法,那么就从这个方法开始说起:
private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
ActivityRecord[] outActivity, boolean restrictedBgActivity) {
int result = START_CANCELED;
final ActivityStack startedActivityStack;
try {
mService.mWindowManager.deferSurfaceLayout();
result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, doResume, options, inTask, outActivity, restrictedBgActivity);
} finally {
final ActivityStack currentStack = r.getActivityStack();
startedActivityStack = currentStack != null ? currentStack : mTargetStack;
if (ActivityManager.isStartResultSuccessful(result)) {
if (startedActivityStack != null) {
// If there is no state change (e.g. a resumed activity is reparented to
// top of another display) to trigger a visibility/configuration checking,
// we have to update the configuration for changing to different display.
final ActivityRecord currentTop =
startedActivityStack.topRunningActivityLocked();
if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) {
mRootActivityContainer.ensureVisibilityAndConfig(
currentTop, currentTop.getDisplayId(),
true /* markFrozenIfConfigChanged */, false /* deferResume */);
}
}
} else {
...代码省略...
}
mService.mWindowManager.continueSurfaceLayout();
}
postStartActivityProcessing(r, result, startedActivityStack);
return result;
}
这个方法主要做了两件事情:
启动下一个Activity (startActivityUnchecked)
根据实际情况设置其他Activity的状态是否为不可见 (mRootActivityContainer.ensureVisibilityAndConfig)
我们先来看下第一步操作startActivityUnchecked,这个方法接下来会调用RootActivityContainer#resumeFocusedStacksTopActivities方法(RootActivityContainer是Android10以后新增的类),然后调用ActivityStack#resumeTopActivityUncheckedLocked->调用ActivityStack#resumeTopActivityInnerLocked方法:
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
...代码省略...
boolean pausing = getDisplay().pauseBackStacks(userLeaving, next, false);
if (mResumedActivity != null) {
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityLocked: Pausing " + mResumedActivity);
pausing |= startPausingLocked(userLeaving, false, next, false);
}
...代码省略...
return true;
}
这个方法非常的长,这里只需要关注startPausingLocked即可,这个方法首先会调用startPausingLocked方法pause掉当前的Activity,然后后面会启动下一个Activity。
final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
ActivityRecord resuming, boolean pauseImmediately) {
...代码省略...
if (prev.attachedToProcess()) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
try {
EventLogTags.writeAmPauseActivity(prev.mUserId, System.identityHashCode(prev),
prev.shortComponentName, "userLeaving=" + userLeaving);
mService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
prev.configChangeFlags, pauseImmediately));
} catch (Exception e) {
// Ignore exception, if process died other code will cleanup.
Slog.w(TAG, "Exception thrown during pause", e);
mPausingActivity = null;
mLastPausedActivity = null;
mLastNoHistoryActivity = null;
}
} else {
mPausingActivity = null;
mLastPausedActivity = null;
mLastNoHistoryActivity = null;
}
...代码省略...
}
这段代码就比较常见了,通过Android9.0 Activity启动原理差异解析这篇文章可知,最终会指定PauseActivityItem的execute方法,然后执行对应的postExecute方法。当然这里采用的是Binder机制进行调用,属于异步的调用,但是result会直接返回,正常情况下result都会是true。那么start结束以后,在回过头看刚才说的RootActivityContainer#ensureVisibilityAndConfig方法,下面给出调用链
RootActivityContainer#ensureActivitiesVisible
ActivityDisplay#ensureActivitiesVisible
ActivityStack#ensureActivitiesVisibleLocked
final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
boolean preserveWindows, boolean notifyClients) {
...代码省略...
// If the top activity is not fullscreen, then we need to
// make sure any activities under it are now visible.
boolean aboveTop = top != null;
final boolean stackShouldBeVisible = shouldBeVisible(starting);
boolean behindFullscreenActivity = !stackShouldBeVisible;
boolean resumeNextActivity = isFocusable() && isInStackLocked(starting) == null;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
final ArrayList<ActivityRecord> activities = task.mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
if (r.finishing) {
continue;
}
final boolean isTop = r == top;
if (aboveTop && !isTop) {
continue;
}
aboveTop = false;
// Check whether activity should be visible without Keyguard influence
final boolean visibleIgnoringKeyguard = r.shouldBeVisibleIgnoringKeyguard(
behindFullscreenActivity);
final boolean reallyVisible = r.shouldBeVisible(behindFullscreenActivity);
if (visibleIgnoringKeyguard) {
behindFullscreenActivity = updateBehindFullscreen(!stackShouldBeVisible,
behindFullscreenActivity, r);
}
if (reallyVisible) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make visible? " + r
+ " finishing=" + r.finishing + " state=" + r.getState());
// First: if this is not the current activity being started, make
// sure it matches the current configuration.
if (r != starting && notifyClients) {
r.ensureActivityConfiguration(0 /* globalChanges */, preserveWindows,
true /* ignoreStopState */);
}
if (!r.attachedToProcess()) {
if (makeVisibleAndRestartIfNeeded(starting, configChanges, isTop,
resumeNextActivity, r)) {
if (activityNdx >= activities.size()) {
// Record may be removed if its process needs to restart.
activityNdx = activities.size() - 1;
} else {
resumeNextActivity = false;
}
}
} else if (r.visible) {
// If this activity is already visible, then there is nothing to do here.
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
"Skipping: already visible at " + r);
if (r.mClientVisibilityDeferred && notifyClients) {
r.makeClientVisible();
}
if (r.handleAlreadyVisible()) {
resumeNextActivity = false;
}
if (notifyClients) {
r.makeActiveIfNeeded(starting);
}
} else {
r.makeVisibleIfNeeded(starting, notifyClients);
}
// Aggregate current change flags.
configChanges |= r.configChangeFlags;
} else {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make invisible? " + r
+ " finishing=" + r.finishing + " state=" + r.getState()
+ " stackShouldBeVisible=" + stackShouldBeVisible
+ " behindFullscreenActivity=" + behindFullscreenActivity
+ " mLaunchTaskBehind=" + r.mLaunchTaskBehind);
makeInvisible(r);
}
}
...代码省略...
}
看代码注释,当top Activity不是全屏Activity的时候,我们需要保证任何在这个非全屏Activity下面的Activity都是visible状态,所以对应的ActivityRecord的visible为true,那么什么作用呢?下面接着看客户端这边PauseActivityItem的postExecute方法
@Override
public void postExecute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
if (mDontReport) {
return;
}
try {
// TODO(lifecycler): Use interface callback instead of AMS.
ActivityTaskManager.getService().activityPaused(token);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
execute方法就是将客户端的Activity状态设置成onPause,而postExecute方法就是将服务端对应的ActivityRecord的状态设置成pause。然后通过AMS调用到ActivityStack#activityPausedLocked方法:
final void activityPausedLocked(IBinder token, boolean timeout) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE,
"Activity paused: token=" + token + ", timeout=" + timeout);
final ActivityRecord r = isInStackLocked(token);
if (r != null) {
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
if (mPausingActivity == r) {
if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSED: " + r
+ (timeout ? " (due to timeout)" : " (pause complete)"));
mService.mWindowManager.deferSurfaceLayout();
try {
completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
} finally {
mService.mWindowManager.continueSurfaceLayout();
}
return;
} else {
EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
r.mUserId, System.identityHashCode(r), r.shortComponentName,
mPausingActivity != null
? mPausingActivity.shortComponentName : "(none)");
if (r.isState(PAUSING)) {
r.setState(PAUSED, "activityPausedLocked");
if (r.finishing) {
if (DEBUG_PAUSE) Slog.v(TAG,
"Executing finish of failed to pause activity: " + r);
finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false,
"activityPausedLocked");
}
}
}
}
mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
}
上面这段代码执行完毕以后,服务端的对应的ActivityRecord也正是进入了pause状态,然后最顶上的Activity正式启动,会一次调用onCreate,onStart,onResume。根据这篇文章可知,当ActivityThread调用handleResumeActivity的时候,在最后会add一个IdleHandler用来stop上一个已经处于pause状态的Activity。下面给出调用链:
ActivityTaskManagerService#activityIdle
ActivityStackSuperVisor#activityIdleInternalLocked
final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
boolean processPausingActivities, Configuration config) {
...代码省略...
// Atomically retrieve all of the other things to do.
final ArrayList<ActivityRecord> stops = processStoppingActivitiesLocked(r,
true /* remove */, processPausingActivities);
NS = stops != null ? stops.size() : 0;
if ((NF = mFinishingActivities.size()) > 0) {
finishes = new ArrayList<>(mFinishingActivities);
mFinishingActivities.clear();
}
if (mStartingUsers.size() > 0) {
startingUsers = new ArrayList<>(mStartingUsers);
mStartingUsers.clear();
}
// Stop any activities that are scheduled to do so but have been
// waiting for the next one to start.
for (int i = 0; i < NS; i++) {
r = stops.get(i);
final ActivityStack stack = r.getActivityStack();
if (stack != null) {
if (r.finishing) {
stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false,
"activityIdleInternalLocked");
} else {
stack.stopActivityLocked(r);
}
}
}
...代码省略...
return r;
}
这里会获取对应的stops列表,然后对列表中的所有ActivityRecord进行判断,如果finishing状态是true的那么执行finish操作,如果是false的就执行stop操作。那么我们再来看下stops列表是如何获取的
final ArrayList<ActivityRecord> stops = processStoppingActivitiesLocked(r,
true /* remove */, processPausingActivities);
调用的processStoppingActivitiesLocked方法获取的,最终是从mStoppingActivities里面获取的。那么mStoppingActivities什么时候会把ActivityRecord add进来呢?搜索了一下这个对象的使用地方:
发现只有一处地方有add操作,那么点过去看下代码:
private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed,
String reason) {
if (!mStackSupervisor.mStoppingActivities.contains(r)) {
EventLog.writeEvent(EventLogTags.AM_ADD_TO_STOPPING, r.mUserId,
System.identityHashCode(r), r.shortComponentName, reason);
mStackSupervisor.mStoppingActivities.add(r);
}
// If we already have a few activities waiting to stop, then give up
// on things going idle and start clearing them out. Or if r is the
// last of activity of the last task the stack will be empty and must
// be cleared immediately.
boolean forceIdle = mStackSupervisor.mStoppingActivities.size() > MAX_STOPPING_TO_FORCE
|| (r.frontOfTask && mTaskHistory.size() <= 1);
if (scheduleIdle || forceIdle) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Scheduling idle now: forceIdle="
+ forceIdle + "immediate=" + !idleDelayed);
if (!idleDelayed) {
mStackSupervisor.scheduleIdleLocked();
} else {
mStackSupervisor.scheduleIdleTimeoutLocked(r);
}
} else {
checkReadyForSleep();
}
}
这个方法判断当前mStoppingActivities列表里面是否包含指定的ActivityRecord,如果不包含那就添加进来,很明显了只要执行了这段代码,那么mStoppingActivities肯定就会添加这个ActivityRecord,然后必然会在前面ActivityStackSuperVisor#activityIdleInternalLocked的方法中执行对应的stop方法。那么接下来就看什么时候这个addToStopping执行了。我们可以发现下面这个方法:
private void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
...代码省略。。。
if (prev != null) {
prev.setWillCloseOrEnterPip(false);
final boolean wasStopping = prev.isState(STOPPING);
prev.setState(PAUSED, "completePausedLocked");
if (prev.finishing) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev);
prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false,
"completePausedLocked");
} else if (prev.hasProcess()) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev
+ " wasStopping=" + wasStopping + " visible=" + prev.visible);
if (prev.deferRelaunchUntilPaused) {
// Complete the deferred relaunch that was waiting for pause to complete.
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Re-launching after pause: " + prev);
prev.relaunchActivityLocked(false /* andResume */,
prev.preserveWindowOnDeferredRelaunch);
} else if (wasStopping) {
// We are also stopping, the stop request must have gone soon after the pause.
// We can't clobber it, because the stop confirmation will not be handled.
// We don't need to schedule another stop, we only need to let it happen.
prev.setState(STOPPING, "completePausedLocked");
} else if (!prev.visible || shouldSleepOrShutDownActivities()) {
// Clear out any deferred client hide we might currently have.
prev.setDeferHidingClient(false);
// If we were visible then resumeTopActivities will release resources before
// stopping.
addToStopping(prev, true /* scheduleIdle */, false /* idleDelayed */,
"completePauseLocked");
}
} else {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "App died during pause, not stopping: " + prev);
prev = null;
}
// It is possible the activity was freezing the screen before it was paused.
// In that case go ahead and remove the freeze this activity has on the screen
// since it is no longer visible.
if (prev != null) {
prev.stopFreezingScreenLocked(true /*force*/);
}
mPausingActivity = null;
}
...代码省略...
}
- 首先prev并没有调用finish方法, 所以finishing状态肯定是false。
- 然后根据浅谈APP的回收和重启机制可知,只有在系统回收当前Activity的时候
prev.hasProcess()才会是false, 所以正常情况下这个值必然是true。 - prev并没有重新启动,当前状态也不是stopping,所以就看最后一个判断逻辑。
- shouldSleepOrShutDownActivities()方法判断当前手机是否关机或者锁屏,很明显这里的判断是false
- 而一旦将要启动的Activity是非全屏的Activity的时候,prev.visible会是true,所以(!prev.visible || shouldSleepOrShutDownActivities())这个条件不满足,也就是不会指定addToStopping方法,那么根据上面的结论,prev对应的Activity也就不会执行onStop方法了。
总结
所以Dialog弹出时是不会影响Activity生命周期的,Activity也就不会进入所谓的onPause状态。所谓的可见状态下进入onPause其实说的是透明背景的Activity或者是Dialog主题的Activity弹出来的时候,那么前一个页面确实只会调用onPause方法。