背景
某天妖枚,測試提了一個(gè)bug廷臼,說當(dāng)前頁面關(guān)閉了以后回到了上一個(gè)頁面,但是對應(yīng)的音樂并沒有立刻停止绝页,而是過了一段時(shí)間才停止中剩。于是翻閱了一下代碼:
@Override
protected void onStop() {
super.onStop();
if (mIsLoading) {
mAudioTool.pausePlayAudio();
}
}
mAudioTool.pausePlayAudio方法是停止播放音頻的代碼,似乎并沒有什么問題抒寂。而且這段代碼已經(jīng)運(yùn)行了好久,之前測試也是通過的掠剑,為何這個(gè)版本會(huì)出現(xiàn)這個(gè)問題屈芜?
所以首先我debug一下當(dāng)前頁面的onStop方法,結(jié)果發(fā)現(xiàn)頁面關(guān)閉的時(shí)候onStop方法并沒有被執(zhí)行朴译,然后過了差不多10s左右再被執(zhí)行井佑,感覺又是踩到了某個(gè)坑里面去了,仔細(xì)回想了最近改了的代碼眠寿,問題可能出在了上一個(gè)頁面的下面這段代碼:
/**
* 開始抖動(dòng)
*/
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()) {
//當(dāng)前動(dòng)畫已經(jīng)開始并且沒有結(jié)束
return
}
//從左向右
mRotateAnim!!.duration = 50
mRotateAnim!!.repeatMode = Animation.REVERSE
mRotateAnim!!.repeatCount = Animation.INFINITE
val smallAnimationSet = AnimationSet(false)
smallAnimationSet.addAnimation(mRotateAnim)
cl_wrong_book.startAnimation(smallAnimationSet)
}
由于設(shè)置的repeatCount是INFINITE躬翁,所以動(dòng)畫是一直在執(zhí)行中的。
不過還是有點(diǎn)納悶盯拱,動(dòng)畫跟Activity生命周期有啥關(guān)系盒发?為了印證下到底是不是Animation影響的當(dāng)前頁面生命周期調(diào)用異常例嘱,于是將這段代碼進(jìn)行了刪除操作。結(jié)果Activity的onStop生命周期還真的就正常執(zhí)行了宁舰。然后自己又做了一個(gè)demo拼卵,來查看下onStop的執(zhí)行時(shí)間。
(這段是沒有動(dòng)畫的)
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
(這段是加上動(dòng)畫的)
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也顯示了在加上動(dòng)畫以后蛮艰,確實(shí)onStop和onDestroy就會(huì)被延遲執(zhí)行腋腮,而且試了多次,發(fā)現(xiàn)每次延遲的時(shí)間都是10s左右壤蚜。所以猜測一定存在某種定時(shí)執(zhí)行onStop操作的場景即寡,不然不可能每次都這么湊巧。
既然踩到坑袜刷,那么我就得想辦法搞清楚為什么會(huì)出現(xiàn)這種情況聪富。所以還是跟以前一樣,查閱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方法會(huì)調(diào)用自身帶有參數(shù)的finish方法善涨,然后通過Binder會(huì)執(zhí)行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類型草则,所以會(huì)走else分支钢拧,tr.getStack()得到的是ActivityStack對象,所以接下來執(zhí)行的就是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) {
//這個(gè)判斷條件是為了防止多次進(jìn)入源内,做了一道屏障
Slog.w(TAG, "Duplicate finish request for " + r);
return false;
}
mWindowManager.deferSurfaceLayout();
try {
//這個(gè)方法是為了將finishing設(shè)置為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);
}
}
//停止按鍵的事件分發(fā)
r.pauseKeyDispatchingLocked();
adjustFocusedActivityStack(r, "finishActivity");
//檢查是否有設(shè)置ActivityResult,如果存在則加入列表中
finishActivityResultsLocked(r, resultCode, resultData);
final boolean endTask = index <= 0 && !task.isClearingToReuseTask();
final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE;
//當(dāng)前頁面處于Resume狀態(tài)份殿,所以會(huì)進(jìn)入此分支
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");
//當(dāng)前頁面還沒有進(jìn)入pause狀態(tài)膜钓,所以會(huì)調(diào)用此方法
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();
}
}
- 設(shè)置當(dāng)前ActivityRecord為finishing狀態(tài)
- 停止按鍵的事件分發(fā)
- 檢查ActivityResult狀態(tài),存在的話就加入到列表中
- 執(zhí)行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;
}
...代碼省略...
}
這里面有一大段代碼都需要管颂斜,重點(diǎn)關(guān)注里面的mService.getLifecycleManager().scheduleTransaction這個(gè)方法。在這篇文章里面已經(jīng)了解過了在Android9.0之后生命周期交給了LifecycleItem處理拾枣,是通過TransactionExecutor進(jìn)行調(diào)度的沃疮。
這段代碼最終會(huì)執(zhí)行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);
}
我們是執(zhí)行了startPausingLocked方法, 那么此處最后自然會(huì)執(zhí)行到PauseActivityItem的execute方法里面去的梅肤。execute方法就是簡單的做了pause的生命周期方法司蔬,重點(diǎn)關(guān)注下postExecute方法,這里簡單梳理下postExecute的調(diào)用鏈
- 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的回收和重啟機(jī)制這篇文章中有提及,next.attachedToProcess()可以判斷next頁面是否已經(jīng)被系統(tǒng)回收姨蝴,如果是false就代表頁面被回收了走else分支俊啼,調(diào)用StackSupervisor的startSpecificActivityLocked重啟頁面。如果是true左医,那么就會(huì)通過TransactionExecutor調(diào)度執(zhí)行ResumeActivityItem的execute方法授帕,執(zhí)行前一個(gè)頁面的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);
}
主要就是發(fā)送一個(gè)延時(shí)的message,message主要就是用了執(zhí)行Activity的onStop和onDestroy生命周期豪墅,而IDLE_TIMEOUT正是10s泉手。
當(dāng)前頁面的Activity執(zhí)行了finish操作,并且onStop已經(jīng)調(diào)用偶器,然后上一個(gè)頁面的onResume方法也已經(jīng)調(diào)用斩萌。至此finish相關(guān)的操作邏輯告一段落,但是似乎沒有看到執(zhí)行onStop和onDestroy地方(只有一個(gè)延遲10s執(zhí)行的代碼)
handleResumeActivity的處理
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
...代碼省略...
//這段代碼最終會(huì)執(zhí)行Activity的onResume方法
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
...代碼省略...
if (r.window == null && !a.mFinished && willBeVisible) {
//這個(gè)判斷條件代表當(dāng)前頁面是第一次進(jìn)入屏轰,條件里面的處理就是初始化ViewRootImpl颊郎,將View添加在Window上面,執(zhí)行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的過程自然也不是本文的重點(diǎn)霎苗,重點(diǎn)關(guān)注最后一句話Looper.myQueue().addIdleHandler(new Idler())姆吭,這句話什么作用呢?這里就要重點(diǎn)看下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的列表中,那么什么時(shí)候會(huì)使用這個(gè)對象呢厘擂。查看了mIdleHandlers的調(diào)用地方
發(fā)現(xiàn)除了添加和刪除IdleHandler對象以外只有MessageQueue的next方法里面有調(diào)用的地方昆淡。
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) {
// 獲取當(dāng)前時(shí)間
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
//target為空的情況下,才會(huì)進(jìn)入此條件
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
//遍歷messageQueue里面的所有消息刽严,如果存在message是異步的昂灵,那么返回給調(diào)用者調(diào)用。如果不存在異步消息舞萄,那么等遍歷完退出循環(huán)
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
//when主要是postDelay設(shè)置的眨补,通過postDelay可以延遲執(zhí)行,如果當(dāng)前時(shí)間小于when倒脓,那么該消息不會(huì)被執(zhí)行撑螺。
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的大致流程
- 獲取當(dāng)前時(shí)間
- 判斷當(dāng)前message的target對象是否為空,如果是空的會(huì)進(jìn)入if條件中(什么情況下message的target為空呢崎弃,答案是調(diào)用了postSyncBarrier方法甘晤,什么作用呢?主要是ViewRootImpl做同步屏障用的吊履,為了可以讓UI事件優(yōu)先被處理,會(huì)設(shè)置一個(gè)同步屏障调鬓,然后發(fā)送異步消息處理UI艇炎。最大可能保證UI的流暢性)
- 獲取異步消息(如果存在)
- 判斷當(dāng)前消息的when參數(shù)是否大于now(now為當(dāng)前時(shí)間,如果執(zhí)行postDelay的操作腾窝,when是有可能會(huì)大于now的缀踪,那么這個(gè)時(shí)候消息將不會(huì)執(zhí)行)
- 如果當(dāng)前消息需要立刻執(zhí)行居砖,將消息從鏈表中取出,然后設(shè)置標(biāo)志位驴娃,交給Looper執(zhí)行dispatch方法
以上就是消息的主要處理流程奏候。
那么一旦MessageQueue里面的message沒了,會(huì)怎么樣呢唇敞?接下來就會(huì)從mIdleHandlers獲取idleHandler對象蔗草,然后調(diào)用queueIdle方法。這里的IdleHandler對象其實(shí)就是ActivityThread的Idle類的實(shí)例化對象
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對象疆柔,然后調(diào)用了activityIdle方法咒精。下面給出簡單的調(diào)用鏈
- 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) {
//之前調(diào)用finish的時(shí)候會(huì)將此變量置為true
stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false,
"activityIdleInternalLocked");
} else {
stack.stopActivityLocked(r);
}
}
}
...代碼省略...
return r;
}
ActivityStackSupervisor#activityIdleInternalLocked首先獲取對應(yīng)的ActivityRecord,然后移除掉對應(yīng)的IDLE_TIMEOUT_MSG(這個(gè)Message正是之前延時(shí)10s執(zhí)行的message)旷档。然后獲取到即將進(jìn)入stop的Activity模叙,判斷當(dāng)前Activity是否finishing,我們當(dāng)前頁面執(zhí)行了finish方法鞋屈,自然是true范咨,所以會(huì)調(diào)用ActivityStack#finishCurrentActivityLocked()方法。這個(gè)方法看上去是否很熟悉厂庇?沒錯(cuò)渠啊,之前在執(zhí)行startPauseLocked的時(shí)候也調(diào)用了這個(gè)方法,只不過那個(gè)時(shí)候傳入的第二個(gè)參數(shù)值是FINISH_AFTER_VISIBLE宋列,而現(xiàn)在傳入的第二個(gè)參數(shù)值是FINISH_IMMEDIATELY昭抒。那么這個(gè)值什么作用呢,進(jìn)去再看下:
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;
}
當(dāng)mode是FINISH_IMMEDIATELY時(shí)炼杖,會(huì)進(jìn)入上面代碼的if條件中灭返,執(zhí)行destroyActivityLocked的方法。至于里面的細(xì)節(jié)不是本文終點(diǎn)坤邪,不在繼續(xù)探究熙含。
至此Activity的onStop方法和onDestroy方法就執(zhí)行完畢了。從上面代碼可以看出正常情況下艇纺,在執(zhí)行到此方法的時(shí)候怎静,會(huì)移除掉10s delay的message,onStop和onDestroy會(huì)在預(yù)期范圍內(nèi)執(zhí)行黔衡。
所以如果Activity延遲10s執(zhí)行蚓聘,應(yīng)該是ActivityThread的Idler#queueIdle方法沒有被執(zhí)行,什么情況下不會(huì)被執(zhí)行呢盟劫?我們重新看下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);
}
}
}
...代碼省略...
}
}
如果每次遍歷的時(shí)候都能拿到message夜牡,那么idler的queueIdle就永遠(yuǎn)也執(zhí)行不了了。為什么messageQueue里面的message取不完呢侣签。就只能去看Animation到底是怎么運(yùn)行的了塘装。
Animation的執(zhí)行流程
首先從View的startAnimation開始說起:
public void startAnimation(Animation animation) {
animation.setStartTime(Animation.START_ON_FIRST_FRAME);
setAnimation(animation);
invalidateParentCaches();
invalidate(true);
}
發(fā)現(xiàn)View啟動(dòng)了Animation以后會(huì)執(zhí)行invalidate方法急迂。下面是invalidate的調(diào)用鏈
- View#invalidate()
- View#invalidateInternal()
- ViewGroup#invalidateChild()
- ViewGroup#invalidateChildInParent()
- ViewRootImpl#invalidateChildInParent()
- ViewRootImpl#invalidateRectOnScreen()
- ViewRootImpl#scheduleTraversals()
最終會(huì)調(diào)用到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();
}
}
這里設(shè)置了同步屏障,然后發(fā)射異步消息目的就是為了讓UI消息第一時(shí)間能夠得到處理蹦肴,提高用戶體驗(yàn)僚碎。此處post一個(gè)message消息,后續(xù)邏輯不是本文重點(diǎn)阴幌,不做詳細(xì)說明勺阐。最后會(huì)調(diào)用mTraversalRunnable的run方法
void doTraversal() {
if (mTraversalScheduled) {
//此處做了屏障,目的是使得一次Vsync信號(hào)只能繪制一次裂七,
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
//執(zhí)行performTraversals方法
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
performTraversals相信都比較熟悉了皆看,這個(gè)方法最終會(huì)調(diào)用performDraw方法然后最終執(zhí)行ViewGroup的dispatchdraw方法,r然后調(diào)用ViewGroup的drawChild方法,最后執(zhí)行到View的draw方法背零,這里的draw方法攜帶有三個(gè)參數(shù)
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;
}
這個(gè)方法非常長腰吟,我們只需要關(guān)心applyLegacyAnimation這個(gè)方法即可
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方法,這個(gè)方法會(huì)返回一個(gè)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;
}
其他處理都是本篇重點(diǎn),我們只需要關(guān)注上面這個(gè)邏輯侦镇,返回值mMore跟expired有關(guān)灵疮,而expired跟后面兩個(gè)判斷有關(guān),由于我們的動(dòng)畫并沒有調(diào)用cancel方法壳繁, 所以isCanceled必然是false震捣。normalizedTime表示當(dāng)前的進(jìn)度,舉個(gè)栗子:如果duration是200ms闹炉,mStartTime是0ms蒿赢,currentTime是100ms,那么最后獲取到的normalizedTime就是0.5f渣触。
所以這個(gè)時(shí)候有兩種情況
- 動(dòng)畫還在一個(gè)duration之內(nèi)羡棵,那么normalizedTime >= 1.0f為false,這個(gè)時(shí)候expired為false嗅钻,那么mMore就為true皂冰。我們暫且不管mMore的作用,后面會(huì)說明
- 動(dòng)畫已經(jīng)超過了一個(gè)duration(即一次動(dòng)畫執(zhí)行完畢)养篓,那么normalizedTime >= 1.0f為true秃流,這個(gè)時(shí)候expired為true,而mMore為false柳弄。這個(gè)時(shí)候下面這段邏輯就會(huì)進(jìn)去:
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();
}
}
又因?yàn)楸镜貏?dòng)畫設(shè)置的是mRepeat為INFINITE即-1舶胀,所以mRepeatCount == mRepeated不可能會(huì)相等,isCanceled又是false,所以會(huì)進(jìn)入else分支峻贮,最后發(fā)現(xiàn)mMore依然會(huì)是true;
綜上所述应闯,當(dāng)前代碼下面纤控,mMore永遠(yuǎn)都為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));
}
}
當(dāng)more為true的時(shí)候船万,會(huì)進(jìn)入if條件中,因?yàn)锳nimation#willChangeBounds為true骨田,所以會(huì)進(jìn)入else分支耿导,后面會(huì)執(zhí)行parent.invalidate方法,這個(gè)其實(shí)就是View的invalidate方法态贤。而invalidate又會(huì)發(fā)一次message給messageQueue舱呻。導(dǎo)致MessageQueue永遠(yuǎn)不可能為空。下面給出一張Animation的流程圖
總結(jié)
這里我們把整理的流程梳理一遍
- 上一個(gè)頁面執(zhí)行了Animation動(dòng)畫匕争,由于動(dòng)畫是無限循環(huán)的碟联,所以Animation會(huì)無限循環(huán)的往MessageQueue發(fā)送繪制UI的消息栋操。
- 當(dāng)前頁面執(zhí)行finish操作,Activity進(jìn)入onPause狀態(tài)
- 然后尋找下一個(gè)即將resume的Activity茬高,進(jìn)入resume狀態(tài)
- 發(fā)送一個(gè)延遲10s的消息進(jìn)入messagequeue中,這個(gè)消息用來執(zhí)行Activity的onStop和onDestroy操作
- 發(fā)送一個(gè)Idler對象假抄,旨在MessageQueue空閑的時(shí)候執(zhí)行Activity的onStop和onDestroy操作怎栽,并且移除掉第4步發(fā)送的延遲消息
-
由于MessageQueue一直有消息在執(zhí)行,所以Idler對象沒有執(zhí)行的時(shí)機(jī)宿饱,10s之后熏瞄,延遲的消息會(huì)執(zhí)行onStop操作。
解決方案
講完了原因刑棵,下面就給出三個(gè)解決方案:
- 在頁面進(jìn)入onPause的時(shí)候可以暫停Animation巴刻,然后在onResume的時(shí)候發(fā)送一個(gè)延遲的消息執(zhí)行Animation,這樣Idler就會(huì)有空隙執(zhí)行Activity的onStop生命周期蛉签。不過由于這種方案不可靠胡陪,因?yàn)槭謾C(jī)性能的原因,所以有可能存在發(fā)送的延遲消息已經(jīng)開始執(zhí)行了碍舍,Idler對象仍然還沒有被post到messagequeue當(dāng)中柠座。
- Animation替換成ValueAnimation,屬性動(dòng)畫不會(huì)導(dǎo)致Activity的生命周期延遲執(zhí)行(具體原因本文不做詳細(xì)分析片橡, 后期有時(shí)間可以詳細(xì)研究)妈经,不過這種方案也只是解決了本文中的場景,沒辦法處理所有導(dǎo)致Activity生命周期延遲10s執(zhí)行的操作。
- 我們經(jīng)常會(huì)在onStop和onDestroy方法中做資源釋放的操作吹泡,但是由于這個(gè)原因的存在可能會(huì)出現(xiàn)資源釋放不及時(shí)導(dǎo)致的bug骤星,那我們可以在onPause的時(shí)候判斷當(dāng)前finishing狀態(tài),如果是true爆哑,證明Activity即將關(guān)閉洞难,那么可以直接釋放資源。這種做法也比較合理揭朝,可以適用于各種場景队贱。