本文转自:https://www.jianshu.com/p/69703f4b830b
参考链接:https://www.jianshu.com/p/479b78235361
1. 背景
某天,测试提了一个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源码一探究竟。
2. 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(); } }
1. 设置当前ActivityRecord为finishing状态 2. 停止按键的事件分发 3. 检查ActivityResult状态,存在的话就加入到列表中 4. 执行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; } ...代码省略... }
这段代码最终会执行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; }
逻辑处理完以后接下来有这么一段代码:
// 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执行的代码)
3. 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; }
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); } } } ...代码省略... } }
4. 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(); } }
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的流程图
5. 总结
这里我们把整理的流程梳理一遍
- 上一个页面执行了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操作。
6. 解决方案
讲完了原因,下面就给出三个解决方案:
- 在页面进入onPause的时候可以暂停Animation,然后在onResume的时候发送一个延迟的消息执行Animation,这样Idler就会有空隙执行Activity的onStop生命周期。不过由于这种方案不可靠,因为手机性能的原因,所以有可能存在发送的延迟消息已经开始执行了,Idler对象仍然还没有被post到messagequeue当中。
- Animation替换成ValueAnimation,属性动画不会导致Activity的生命周期延迟执行(具体原因本文不做详细分析, 后期有时间可以详细研究),不过这种方案也只是解决了本文中的场景,没办法处理所有导致Activity生命周期延迟10s执行的操作。
- 我们经常会在onStop和onDestroy方法中做资源释放的操作,但是由于这个原因的存在可能会出现资源释放不及时导致的bug,那我们可以在onPause的时候判断当前finishing状态,如果是true,证明Activity即将关闭,那么可以直接释放资源。这种做法也比较合理,可以适用于各种场景。