背景
某天,测试提了一个bug,说当前页面关闭了以后回到了上一个页面,但是对应的音乐并没有立刻停止,而是过了一段时间才停止。于是翻阅了一下代码:
@Override
protected void onStop() {
super.onStop();
if (mIsLoading) {
mAudioTool.pausePlayAudio();
}
}
mAudioTool.pausePlayAudio方法是停止播放音频的代码,似乎并没有什么问题。而且这段代码已经运行了好久,之前测试也是通过的,为何这个版本会出现这个问题?
所以首先我debug一下当前页面的onStop方法,结果发现页面关闭的时候onStop方法并没有被执行,然后过了差不多10s左右再被执行,感觉又是踩到了某个坑里面去了,仔细回想了最近改了的代码,问题可能出在了上一个页面的下面这段代码:
/**
* 开始抖动
*/
private fun startShake() {
if(mRotateAnim == null){
mRotateAnim = RotateAnimation(-2f, 2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)
}
if (mRotateAnim!!.hasStarted() && !mRotateAnim!!.hasEnded()) {
//当前动画已经开始并且没有结束
return
}
//从左向右
mRotateAnim!!.duration = 50
mRotateAnim!!.repeatMode = Animation.REVERSE
mRotateAnim!!.repeatCount = Animation.INFINITE
val smallAnimationSet = AnimationSet(false)
smallAnimationSet.addAnimation(mRotateAnim)
cl_wrong_book.startAnimation(smallAnimationSet)
}
由于设置的repeatCount是INFINITE,所以动画是一直在执行中的。
不过还是有点纳闷,动画跟Activity生命周期有啥关系?为了印证下到底是不是Animation影响的当前页面生命周期调用异常,于是将这段代码进行了删除操作。结果Activity的onStop生命周期还真的就正常执行了。然后自己又做了一个demo,来查看下onStop的执行时间。
(这段是没有动画的)
2020-12-14 12:40:17.334 24575-24575/com.example.demo I/MainActivity: onCreate
2020-12-14 12:40:17.663 24575-24575/com.example.demo I/MainActivity: onStart
2020-12-14 12:40:17.670 24575-24575/com.example.demo I/MainActivity: onResume
2020-12-14 12:40:20.818 24575-24575/com.example.demo I/MainActivity: onPause
2020-12-14 12:40:20.836 24575-24575/com.example.demo I/SecondActivity: onCreate
2020-12-14 12:40:20.857 24575-24575/com.example.demo I/SecondActivity: onStart
2020-12-14 12:40:20.858 24575-24575/com.example.demo I/SecondActivity: onResume
2020-12-14 12:40:21.406 24575-24575/com.example.demo I/MainActivity: onStop
2020-12-14 12:40:22.930 24575-24575/com.example.demo I/SecondActivity: onPause
2020-12-14 12:40:22.936 24575-24575/com.example.demo I/MainActivity: onStart
2020-12-14 12:40:22.937 24575-24575/com.example.demo I/MainActivity: onResume
2020-12-14 12:40:23.439 24575-24575/com.example.demo I/SecondActivity: onStop
2020-12-14 12:40:23.440 24575-24575/com.example.demo I/SecondActivity: onDestroy
(这段是加上动画的)
2020-12-14 12:38:06.392 24278-24278/com.example.demo I/MainActivity: onCreate
2020-12-14 12:38:06.563 24278-24278/com.example.demo I/MainActivity: onStart
2020-12-14 12:38:06.565 24278-24278/com.example.demo I/MainActivity: onResume
2020-12-14 12:38:23.940 24278-24278/com.example.demo I/MainActivity: onPause
2020-12-14 12:38:23.964 24278-24278/com.example.demo I/SecondActivity: onCreate
2020-12-14 12:38:23.980 24278-24278/com.example.demo I/SecondActivity: onStart
2020-12-14 12:38:23.980 24278-24278/com.example.demo I/SecondActivity: onResume
2020-12-14 12:38:24.544 24278-24278/com.example.demo I/MainActivity: onStop
2020-12-14 12:38:28.111 24278-24278/com.example.demo I/SecondActivity: onPause
2020-12-14 12:38:28.117 24278-24278/com.example.demo I/MainActivity: onStart
2020-12-14 12:38:28.118 24278-24278/com.example.demo I/MainActivity: onResume
2020-12-14 12:38:38.153 24278-24278/com.example.demo I/SecondActivity: onStop
2020-12-14 12:38:38.155 24278-24278/com.example.demo I/SecondActivity: onDestroy
demo的log也显示了在加上动画以后,确实onStop和onDestroy就会被延迟执行,而且试了多次,发现每次延迟的时间都是10s左右。所以猜测一定存在某种定时执行onStop操作的场景,不然不可能每次都这么凑巧。
既然踩到坑,那么我就得想办法搞清楚为什么会出现这种情况。所以还是跟以前一样,查阅Android源码一探究竟。
finish()操作
/**
* Finishes the current activity and specifies whether to remove the task associated with this
* activity.
*/
@UnsupportedAppUsage
private void finish(int finishTask) {
if (mParent == null) {
int resultCode;
Intent resultData;
synchronized (this) {
resultCode = mResultCode;
resultData = mResultData;
}
if (false) Log.v(TAG, "Finishing self: token=" + mToken);
try {
if (resultData != null) {
resultData.prepareToLeaveProcess(this);
}
if (ActivityTaskManager.getService()
.finishActivity(mToken, resultCode, resultData, finishTask)) {
mFinished = true;
}
} catch (RemoteException e) {
// Empty
}
} else {
mParent.finishFromChild(this);
}
// Activity was launched when user tapped a link in the Autofill Save UI - Save UI must
// be restored now.
if (mIntent != null && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) {
getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_RESTORE,
mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN));
}
}
/**
* Call this when your activity is done and should be closed. The
* ActivityResult is propagated back to whoever launched you via
* onActivityResult().
*/
public void finish() {
finish(DONT_FINISH_TASK_WITH_ACTIVITY);
}
Activity的finish方法会调用自身带有参数的finish方法,然后通过Binder会执行ActivityTaskManagerService的finishActivity方法。
@Override
public final boolean finishActivity(IBinder token, int resultCode, Intent resultData,
int finishTask) {
...代码省略...
synchronized (mGlobalLock) {
...代码省略...
try {
boolean res;
final boolean finishWithRootActivity =
finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
|| (finishWithRootActivity && r == rootR)) {
...代码省略
} else {
res = tr.getStack().requestFinishActivityLocked(token, resultCode,
resultData, "app-request", true);
if (!res) {
Slog.i(TAG, "Failed to finish by app-request");
}
}
return res;
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
由于finishTask是DONT_FINISH_TASK_WITH_ACTIVITY类型,所以会走else分支,tr.getStack()得到的是ActivityStack对象,所以接下来执行的就是ActivityStack的requestFinishActivityLocked方法。
/**
* @return Returns true if this activity has been removed from the history
* list, or false if it is still in the list and will be removed later.
*/
final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
String reason, boolean oomAdj, boolean pauseImmediately) {
if (r.finishing) {
//这个判断条件是为了防止多次进入,做了一道屏障
Slog.w(TAG, "Duplicate finish request for " + r);
return false;
}
mWindowManager.deferSurfaceLayout();
try {
//这个方法是为了将finishing设置为true
r.makeFinishingLocked();
final TaskRecord task = r.getTaskRecord();
EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
r.mUserId, System.identityHashCode(r),
task.taskId, r.shortComponentName, reason);
final ArrayList<ActivityRecord> activities = task.mActivities;
final int index = activities.indexOf(r);
if (index < (activities.size() - 1)) {
task.setFrontOfTask();
if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
// If the caller asked that this activity (and all above it)
// be cleared when the task is reset, don't lose that information,
// but propagate it up to the next activity.
ActivityRecord next = activities.get(index+1);
next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
}
}
//停止按键的事件分发
r.pauseKeyDispatchingLocked();
adjustFocusedActivityStack(r, "finishActivity");
//检查是否有设置ActivityResult,如果存在则加入列表中
finishActivityResultsLocked(r, resultCode, resultData);
final boolean endTask = index <= 0 && !task.isClearingToReuseTask();
final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE;
//当前页面处于Resume状态,所以会进入此分支
if (mResumedActivity == r) {
if (DEBUG_VISIBILITY || DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
"Prepare close transition: finishing " + r);
if (endTask) {
mService.getTaskChangeNotificationController().notifyTaskRemovalStarted(
task.getTaskInfo());
}
getDisplay().mDisplayContent.prepareAppTransition(transit, false);
// Tell window manager to prepare for this one to be removed.
r.setVisibility(false);
if (mPausingActivity == null) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish needs to pause: " + r);
if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
"finish() => pause with userLeaving=false");
//当前页面还没有进入pause状态,所以会调用此方法
startPausingLocked(false, false, null, pauseImmediately);
}
if (endTask) {
mService.getLockTaskController().clearLockedTask(task);
}
} else if (!r.isState(PAUSING)) {
// If the activity is PAUSING, we will complete the finish once
// it is done pausing; else we can just directly finish it here.
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish not pausing: " + r);
if (r.visible) {
prepareActivityHideTransitionAnimation(r, transit);
}
final int finishMode = (r.visible || r.nowVisible) ? FINISH_AFTER_VISIBLE
: FINISH_AFTER_PAUSE;
final boolean removedActivity = finishCurrentActivityLocked(r, finishMode, oomAdj,
"finishActivityLocked") == null;
// The following code is an optimization. When the last non-task overlay activity
// is removed from the task, we remove the entire task from the stack. However,
// since that is done after the scheduled destroy callback from the activity, that
// call to change the visibility of the task overlay activities would be out of
// sync with the activitiy visibility being set for this finishing activity above.
// In this case, we can set the visibility of all the task overlay activities when
// we detect the last one is finishing to keep them in sync.
if (task.onlyHasTaskOverlayActivities(true /* excludeFinishing */)) {
for (ActivityRecord taskOverlay : task.mActivities) {
if (!taskOverlay.mTaskOverlay) {
continue;
}
prepareActivityHideTransitionAnimation(taskOverlay, transit);
}
}
return removedActivity;
} else {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish waiting for pause of: " + r);
}
return false;
} finally {
mWindowManager.continueSurfaceLayout();
}
}
- 设置当前ActivityRecord为finishing状态
- 停止按键的事件分发
- 检查ActivityResult状态,存在的话就加入到列表中
- 执行startPausingLocked方法。
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;
}
...代码省略...
}
这里面有一大段代码都需要管,重点关注里面的mService.getLifecycleManager().scheduleTransaction这个方法。在这篇文章里面已经了解过了在Android9.0之后生命周期交给了LifecycleItem处理,是通过TransactionExecutor进行调度的。
这段代码最终会执行ActivityThread里面的下面这段处理:
ActivityThread.java
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
mTransactionExecutor.execute(transaction);
if (isSystem()) {
// Client transactions inside system process are recycled on the client side
// instead of ClientLifecycleManager to avoid being cleared before this
// message is handled.
transaction.recycle();
}
// TODO(lifecycler): Recycle locally scheduled transactions.
break;
TransactionExecutor.java
private void executeLifecycleState(ClientTransaction transaction) {
final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
if (lifecycleItem == null) {
// No lifecycle request, return early.
return;
}
final IBinder token = transaction.getActivityToken();
final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
if (DEBUG_RESOLVER) {
Slog.d(TAG, tId(transaction) + "Resolving lifecycle state: "
+ lifecycleItem + " for activity: "
+ getShortActivityName(token, mTransactionHandler));
}
if (r == null) {
// Ignore requests for non-existent client records for now.
return;
}
// Cycle to the state right before the final requested state.
cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */, transaction);
// Execute the final transition with proper parameters.
lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
}
我们是执行了startPausingLocked方法, 那么此处最后自然会执行到PauseActivityItem的execute方法里面去的。execute方法就是简单的做了pause的生命周期方法,重点关注下postExecute方法,这里简单梳理下postExecute的调用链
- PauseActivityItem#poseExecute()
- ActivityTaskManagerService#activityPaused()
- ActivityStack#activityPausedLocked()
- ActivityStack#finishCurrentActivityLocked()
- RootActivityContainer#resumeFocusedStacksTopActivities()
- ActivityStack#resumeTopActivityUncheckedLocked()
- ActivityStack#resumeTopActivityInnerLocked()
@GuardedBy("mService")
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
...代码省略...
if (next.attachedToProcess()) {
...代码省略...
try {
final ClientTransaction transaction =
ClientTransaction.obtain(next.app.getThread(), next.appToken);
// Deliver all pending results.
ArrayList<ResultInfo> a = next.results;
if (a != null) {
final int N = a.size();
if (!next.finishing && N > 0) {
if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
"Delivering results to " + next + ": " + a);
transaction.addCallback(ActivityResultItem.obtain(a));
}
}
if (next.newIntents != null) {
transaction.addCallback(
NewIntentItem.obtain(next.newIntents, true /* resume */));
}
// Well the app will no longer be stopped.
// Clear app token stopped state in window manager if needed.
next.notifyAppResumed(next.stopped);
EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.mUserId,
System.identityHashCode(next), next.getTaskRecord().taskId,
next.shortComponentName);
next.sleeping = false;
mService.getAppWarningsLocked().onResumeActivity(next);
next.app.setPendingUiCleanAndForceProcessStateUpTo(mService.mTopProcessState);
next.clearOptionsLocked();
transaction.setLifecycleStateRequest(
ResumeActivityItem.obtain(next.app.getReportedProcState(),
getDisplay().mDisplayContent.isNextTransitionForward()));
mService.getLifecycleManager().scheduleTransaction(transaction);
if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed "
+ next);
} catch (Exception e) {
...代码省略...
return true;
}
// From this point on, if something goes wrong there is no way
// to recover the activity.
try {
next.completeResumeLocked();
} catch (Exception e) {
// If any exception gets thrown, toss away this
// activity and try the next one.
Slog.w(TAG, "Exception thrown during resume of " + next, e);
requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
"resume-exception", true);
return true;
}
} else {
// Whoops, need to restart this activity!
if (!next.hasBeenLaunched) {
next.hasBeenLaunched = true;
} else {
if (SHOW_APP_STARTING_PREVIEW) {
next.showStartingWindow(null /* prev */, false /* newTask */,
false /* taskSwich */);
}
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
}
if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Restarting " + next);
mStackSupervisor.startSpecificActivityLocked(next, true, true);
}
return true;
}
这段代码在浅谈APP的回收和重启机制这篇文章中有提及,next.attachedToProcess()可以判断next页面是否已经被系统回收,如果是false就代表页面被回收了走else分支,调用StackSupervisor的startSpecificActivityLocked重启页面。如果是true,那么就会通过TransactionExecutor调度执行ResumeActivityItem的execute方法,执行前一个页面的onResume方法。
逻辑处理完以后接下来有这么一段代码:
// From this point on, if something goes wrong there is no way
// to recover the activity.
try {
next.completeResumeLocked();
} catch (Exception e) {
// If any exception gets thrown, toss away this
// activity and try the next one.
Slog.w(TAG, "Exception thrown during resume of " + next, e);
requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
"resume-exception", true);
return true;
}
我们看下completeResumeLocked的逻辑:
void completeResumeLocked() {
...代码省略...
// Schedule an idle timeout in case the app doesn't do it for us.
mStackSupervisor.scheduleIdleTimeoutLocked(this);
...代码省略...
}
void scheduleIdleTimeoutLocked(ActivityRecord next) {
if (DEBUG_IDLE) Slog.d(TAG_IDLE,
"scheduleIdleTimeoutLocked: Callers=" + Debug.getCallers(4));
Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG, next);
mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
}
主要就是发送一个延时的message,message主要就是用了执行Activity的onStop和onDestroy生命周期,而IDLE_TIMEOUT正是10s。
当前页面的Activity执行了finish操作,并且onStop已经调用,然后上一个页面的onResume方法也已经调用。至此finish相关的操作逻辑告一段落,但是似乎没有看到执行onStop和onDestroy地方(只有一个延迟10s执行的代码)
handleResumeActivity的处理
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
...代码省略...
//这段代码最终会执行Activity的onResume方法
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
...代码省略...
if (r.window == null && !a.mFinished && willBeVisible) {
//这个判断条件代表当前页面是第一次进入,条件里面的处理就是初始化ViewRootImpl,将View添加在Window上面,执行View的第一次绘制操作
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
} else {
// The activity will get a callback for this {@link LayoutParams} change
// earlier. However, at that time the decor will not be set (this is set
// in this method), so no action will be taken. This call ensures the
// callback occurs with the decor set.
a.onWindowAttributesChanged(l);
}
}
// If the window has already been added, but during resume
// we started another activity, then don't yet make the
// window visible.
} else if (!willBeVisible) {
if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
...代码省略...
r.nextIdle = mNewActivities;
mNewActivities = r;
if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);
Looper.myQueue().addIdleHandler(new Idler());
}
handleResumeActivity的过程自然也不是本文的重点,重点关注最后一句话Looper.myQueue().addIdleHandler(new Idler()),这句话什么作用呢?这里就要重点看下addIdleHandler方法到底添加了什么东西。
MessageQueue.java
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
很简单的代码,只是将IdleHandler添加到mIdleHandlers的列表中,那么什么时候会使用这个对象呢。查看了mIdleHandlers的调用地方
发现除了添加和删除IdleHandler对象以外只有MessageQueue的next方法里面有调用的地方。
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// 获取当前时间
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
//target为空的情况下,才会进入此条件
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
//遍历messageQueue里面的所有消息,如果存在message是异步的,那么返回给调用者调用。如果不存在异步消息,那么等遍历完退出循环
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
//when主要是postDelay设置的,通过postDelay可以延迟执行,如果当前时间小于when,那么该消息不会被执行。
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
这里主要讲下next的大致流程
- 获取当前时间
- 判断当前message的target对象是否为空,如果是空的会进入if条件中(什么情况下message的target为空呢,答案是调用了postSyncBarrier方法,什么作用呢?主要是ViewRootImpl做同步屏障用的,为了可以让UI事件优先被处理,会设置一个同步屏障,然后发送异步消息处理UI。最大可能保证UI的流畅性)
- 获取异步消息(如果存在)
- 判断当前消息的when参数是否大于now(now为当前时间,如果执行postDelay的操作,when是有可能会大于now的,那么这个时候消息将不会执行)
- 如果当前消息需要立刻执行,将消息从链表中取出,然后设置标志位,交给Looper执行dispatch方法
以上就是消息的主要处理流程。
那么一旦MessageQueue里面的message没了,会怎么样呢?接下来就会从mIdleHandlers获取idleHandler对象,然后调用queueIdle方法。这里的IdleHandler对象其实就是ActivityThread的Idle类的实例化对象
private class Idler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
ActivityClientRecord a = mNewActivities;
boolean stopProfiling = false;
if (mBoundApplication != null && mProfiler.profileFd != null
&& mProfiler.autoStopProfiler) {
stopProfiling = true;
}
if (a != null) {
mNewActivities = null;
IActivityTaskManager am = ActivityTaskManager.getService();
ActivityClientRecord prev;
do {
if (localLOGV) Slog.v(
TAG, "Reporting idle of " + a +
" finished=" +
(a.activity != null && a.activity.mFinished));
if (a.activity != null && !a.activity.mFinished) {
try {
am.activityIdle(a.token, a.createdConfig, stopProfiling);
a.createdConfig = null;
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
prev = a;
a = a.nextIdle;
prev.nextIdle = null;
} while (a != null);
}
if (stopProfiling) {
mProfiler.stopProfiling();
}
applyPendingProcessState();
return false;
}
}
这里面获取了ActivityTaskManagerService对象,然后调用了activityIdle方法。下面给出简单的调用链
- ActivityTaskManagerService#activityIdle()
- ActivityStackSupervisor#activityIdleInternalLocked()
@GuardedBy("mService")
final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
boolean processPausingActivities, Configuration config) {
if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + token);
ArrayList<ActivityRecord> finishes = null;
ArrayList<UserState> startingUsers = null;
int NS = 0;
int NF = 0;
boolean booting = false;
boolean activityRemoved = false;
ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r != null) {
if (DEBUG_IDLE) Slog.d(TAG_IDLE, "activityIdleInternalLocked: Callers="
+ Debug.getCallers(4));
mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
...代码省略...
}
...代码省略...
// Atomically retrieve all of the other things to do.
final ArrayList<ActivityRecord> stops = processStoppingActivitiesLocked(r,
true /* remove */, processPausingActivities);
NS = stops != null ? stops.size() : 0;
...代码省略...
// 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) {
//之前调用finish的时候会将此变量置为true
stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false,
"activityIdleInternalLocked");
} else {
stack.stopActivityLocked(r);
}
}
}
...代码省略...
return r;
}
ActivityStackSupervisor#activityIdleInternalLocked首先获取对应的ActivityRecord,然后移除掉对应的IDLE_TIMEOUT_MSG(这个Message正是之前延时10s执行的message)。然后获取到即将进入stop的Activity,判断当前Activity是否finishing,我们当前页面执行了finish方法,自然是true,所以会调用ActivityStack#finishCurrentActivityLocked()方法。这个方法看上去是否很熟悉?没错,之前在执行startPauseLocked的时候也调用了这个方法,只不过那个时候传入的第二个参数值是FINISH_AFTER_VISIBLE,而现在传入的第二个参数值是FINISH_IMMEDIATELY。那么这个值什么作用呢,进去再看下:
final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj,
String reason) {
...代码省略...
if (mode == FINISH_IMMEDIATELY
|| (prevState == PAUSED
&& (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode()))
|| finishingInNonFocusedStackOrNoRunning
|| prevState == STOPPING
|| prevState == STOPPED
|| prevState == ActivityState.INITIALIZING) {
r.makeFinishingLocked();
boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm:" + reason);
if (finishingInNonFocusedStackOrNoRunning) {
// Finishing activity that was in paused state and it was in not currently focused
// stack, need to make something visible in its place. Also if the display does not
// have running activity, the configuration may need to be updated for restoring
// original orientation of the display.
mRootActivityContainer.ensureVisibilityAndConfig(next, mDisplayId,
false /* markFrozenIfConfigChanged */, true /* deferResume */);
}
if (activityRemoved) {
mRootActivityContainer.resumeFocusedStacksTopActivities();
}
if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS,
"destroyActivityLocked: finishCurrentActivityLocked r=" + r +
" destroy returned removed=" + activityRemoved);
return activityRemoved ? null : r;
}
// Need to go through the full pause cycle to get this
// activity into the stopped state and then finish it.
if (DEBUG_ALL) Slog.v(TAG, "Enqueueing pending finish: " + r);
mStackSupervisor.mFinishingActivities.add(r);
r.resumeKeyDispatchingLocked();
mRootActivityContainer.resumeFocusedStacksTopActivities();
// If activity was not paused at this point - explicitly pause it to start finishing
// process. Finishing will be completed once it reports pause back.
if (r.isState(RESUMED) && mPausingActivity != null) {
startPausingLocked(false /* userLeaving */, false /* uiSleeping */, next /* resuming */,
false /* dontWait */);
}
return r;
}
当mode是FINISH_IMMEDIATELY时,会进入上面代码的if条件中,执行destroyActivityLocked的方法。至于里面的细节不是本文终点,不在继续探究。
至此Activity的onStop方法和onDestroy方法就执行完毕了。从上面代码可以看出正常情况下,在执行到此方法的时候,会移除掉10s delay的message,onStop和onDestroy会在预期范围内执行。
所以如果Activity延迟10s执行,应该是ActivityThread的Idler#queueIdle方法没有被执行,什么情况下不会被执行呢?我们重新看下MessageQueue的next()方法
@UnsupportedAppUsage
Message next() {
...代码省略...
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
...代码省略...
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
...代码省略...
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
...代码省略...
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
...代码省略...
}
}
如果每次遍历的时候都能拿到message,那么idler的queueIdle就永远也执行不了了。为什么messageQueue里面的message取不完呢。就只能去看Animation到底是怎么运行的了。
Animation的执行流程
首先从View的startAnimation开始说起:
public void startAnimation(Animation animation) {
animation.setStartTime(Animation.START_ON_FIRST_FRAME);
setAnimation(animation);
invalidateParentCaches();
invalidate(true);
}
发现View启动了Animation以后会执行invalidate方法。下面是invalidate的调用链
- View#invalidate()
- View#invalidateInternal()
- ViewGroup#invalidateChild()
- ViewGroup#invalidateChildInParent()
- ViewRootImpl#invalidateChildInParent()
- ViewRootImpl#invalidateRectOnScreen()
- ViewRootImpl#scheduleTraversals()
最终会调用到ViewRootImpl的scheduleTraversals方法中
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
这里设置了同步屏障,然后发射异步消息目的就是为了让UI消息第一时间能够得到处理,提高用户体验。此处post一个message消息,后续逻辑不是本文重点,不做详细说明。最后会调用mTraversalRunnable的run方法
void doTraversal() {
if (mTraversalScheduled) {
//此处做了屏障,目的是使得一次Vsync信号只能绘制一次,
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
//执行performTraversals方法
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
performTraversals相信都比较熟悉了,这个方法最终会调用performDraw方法然后最终执行ViewGroup的dispatchdraw方法,r然后调用ViewGroup的drawChild方法,最后执行到View的draw方法,这里的draw方法携带有三个参数
ViewGroup.java
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated();
...代码省略...
if (a != null) {
more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
concatMatrix = a.willChangeTransformationMatrix();
if (concatMatrix) {
mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
}
transformToApply = parent.getChildTransformation();
} else {
if ((mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_TRANSFORM) != 0) {
// No longer animating: clear out old animation matrix
mRenderNode.setAnimationMatrix(null);
mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
}
if (!drawingWithRenderNode
&& (parentFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
final Transformation t = parent.getChildTransformation();
final boolean hasTransform = parent.getChildStaticTransformation(this, t);
if (hasTransform) {
final int transformType = t.getTransformationType();
transformToApply = transformType != Transformation.TYPE_IDENTITY ? t : null;
concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
}
}
}
...代码省略...
return more;
}
这个方法非常长,我们只需要关心applyLegacyAnimation这个方法即可
private boolean applyLegacyAnimation(ViewGroup parent, long drawingTime,
Animation a, boolean scalingRequired) {
...代码省略...
boolean more = a.getTransformation(drawingTime, t, 1f);
...代码省略...
if (more) {
if (!a.willChangeBounds()) {
...代码省略...
} else {
if (parent.mInvalidateRegion == null) {
parent.mInvalidateRegion = new RectF();
}
final RectF region = parent.mInvalidateRegion;
a.getInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop, region,
invalidationTransform);
// The child need to draw an animation, potentially offscreen, so
// make sure we do not cancel invalidate requests
parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
final int left = mLeft + (int) region.left;
final int top = mTop + (int) region.top;
parent.invalidate(left, top, left + (int) (region.width() + .5f),
top + (int) (region.height() + .5f));
}
}
return more;
}
首先我们来看下getTransformation方法,这个方法会返回一个boolean值,那么我们看下到底返回的是什么值。
public boolean getTransformation(long currentTime, Transformation outTransformation) {
...代码省略...
final long startOffset = getStartOffset();
final long duration = mDuration;
float normalizedTime;
if (duration != 0) {
normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
(float) duration;
} else {
// time is a step-change with a zero duration
normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;
}
final boolean expired = normalizedTime >= 1.0f || isCanceled();
mMore = !expired;
...代码省略...
if (expired) {
if (mRepeatCount == mRepeated || isCanceled()) {
if (!mEnded) {
mEnded = true;
guard.close();
fireAnimationEnd();
}
} else {
if (mRepeatCount > 0) {
mRepeated++;
}
if (mRepeatMode == REVERSE) {
mCycleFlip = !mCycleFlip;
}
mStartTime = -1;
mMore = true;
fireAnimationRepeat();
}
}
return mMore;
}
其他处理都是本篇重点,我们只需要关注上面这个逻辑,返回值mMore跟expired有关,而expired跟后面两个判断有关,由于我们的动画并没有调用cancel方法, 所以isCanceled必然是false。normalizedTime表示当前的进度,举个栗子:如果duration是200ms,mStartTime是0ms,currentTime是100ms,那么最后获取到的normalizedTime就是0.5f。
所以这个时候有两种情况
- 动画还在一个duration之内,那么normalizedTime >= 1.0f为false,这个时候expired为false,那么mMore就为true。我们暂且不管mMore的作用,后面会说明
- 动画已经超过了一个duration(即一次动画执行完毕),那么normalizedTime >= 1.0f为true,这个时候expired为true,而mMore为false。这个时候下面这段逻辑就会进去:
if (expired) {
if (mRepeatCount == mRepeated || isCanceled()) {
if (!mEnded) {
mEnded = true;
guard.close();
fireAnimationEnd();
}
} else {
if (mRepeatCount > 0) {
mRepeated++;
}
if (mRepeatMode == REVERSE) {
mCycleFlip = !mCycleFlip;
}
mStartTime = -1;
mMore = true;
fireAnimationRepeat();
}
}
又因为本地动画设置的是mRepeat为INFINITE即-1,所以mRepeatCount == mRepeated不可能会相等,isCanceled又是false,所以会进入else分支,最后发现mMore依然会是true;
综上所述,当前代码下面,mMore永远都为true,接下来看下true的作用,我们回到View的applyLegacyAnimation方法中,看下面这段代码:
if (more) {
if (!a.willChangeBounds()) {
if ((flags & (ViewGroup.FLAG_OPTIMIZE_INVALIDATE | ViewGroup.FLAG_ANIMATION_DONE)) ==
ViewGroup.FLAG_OPTIMIZE_INVALIDATE) {
parent.mGroupFlags |= ViewGroup.FLAG_INVALIDATE_REQUIRED;
} else if ((flags & ViewGroup.FLAG_INVALIDATE_REQUIRED) == 0) {
// The child need to draw an animation, potentially offscreen, so
// make sure we do not cancel invalidate requests
parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
parent.invalidate(mLeft, mTop, mRight, mBottom);
}
} else {
if (parent.mInvalidateRegion == null) {
parent.mInvalidateRegion = new RectF();
}
final RectF region = parent.mInvalidateRegion;
a.getInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop, region,
invalidationTransform);
// The child need to draw an animation, potentially offscreen, so
// make sure we do not cancel invalidate requests
parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
final int left = mLeft + (int) region.left;
final int top = mTop + (int) region.top;
parent.invalidate(left, top, left + (int) (region.width() + .5f),
top + (int) (region.height() + .5f));
}
}
当more为true的时候,会进入if条件中,因为Animation#willChangeBounds为true,所以会进入else分支,后面会执行parent.invalidate方法,这个其实就是View的invalidate方法。而invalidate又会发一次message给messageQueue。导致MessageQueue永远不可能为空。下面给出一张Animation的流程图
总结
这里我们把整理的流程梳理一遍
- 上一个页面执行了Animation动画,由于动画是无限循环的,所以Animation会无限循环的往MessageQueue发送绘制UI的消息。
- 当前页面执行finish操作,Activity进入onPause状态
- 然后寻找下一个即将resume的Activity,进入resume状态
- 发送一个延迟10s的消息进入messagequeue中,这个消息用来执行Activity的onStop和onDestroy操作
- 发送一个Idler对象,旨在MessageQueue空闲的时候执行Activity的onStop和onDestroy操作,并且移除掉第4步发送的延迟消息
-
由于MessageQueue一直有消息在执行,所以Idler对象没有执行的时机,10s之后,延迟的消息会执行onStop操作。
解决方案
讲完了原因,下面就给出三个解决方案:
- 在页面进入onPause的时候可以暂停Animation,然后在onResume的时候发送一个延迟的消息执行Animation,这样Idler就会有空隙执行Activity的onStop生命周期。不过由于这种方案不可靠,因为手机性能的原因,所以有可能存在发送的延迟消息已经开始执行了,Idler对象仍然还没有被post到messagequeue当中。
- Animation替换成ValueAnimation,属性动画不会导致Activity的生命周期延迟执行(具体原因本文不做详细分析, 后期有时间可以详细研究),不过这种方案也只是解决了本文中的场景,没办法处理所有导致Activity生命周期延迟10s执行的操作。
- 我们经常会在onStop和onDestroy方法中做资源释放的操作,但是由于这个原因的存在可能会出现资源释放不及时导致的bug,那我们可以在onPause的时候判断当前finishing状态,如果是true,证明Activity即将关闭,那么可以直接释放资源。这种做法也比较合理,可以适用于各种场景。