上篇文章分析完了task的創(chuàng )建或者復用,接下來(lái)接著(zhù)分析activity在啟動(dòng)過(guò)程中還有哪些工作需要去完成?首先給出整個(gè)activity的過(guò)程圖。 1. Starting Window 當該activity運行在新的task中或者進(jìn)程中時(shí),需要在activity顯示之前顯示一個(gè)Starting Window。如上圖所示的setAppStartingWindow()方法,這個(gè)Starting Window上并沒(méi)有繪制任何的view,它就是一個(gè)空白的Window,但是WMS賦予了它一個(gè)animation。這個(gè)Starting Window的處理過(guò)程需要注意幾點(diǎn): ·1. 在A(yíng)MS請求WMS啟動(dòng)Starting Window時(shí),這個(gè)過(guò)程是被置在WMS的消息隊列中,也就是說(shuō)這個(gè)過(guò)程是一個(gè)異步的過(guò)程,并且需要將其置在WMS消息隊列的隊首。 一般情況下,Starting Window是在activity Window之前顯示的,但是由于是異步過(guò)程,因此從理論上來(lái)說(shuō)activity Window較早顯示是有可能的,如果這樣的話(huà),Starting Window將會(huì )被清除而不再顯示。例如在addStartingWindow()@PhoneWindowManager.java方法調用addView之前做一個(gè)sleep操作,結果就可能不顯示Starting Window。 setAppStartingWindow()@WindowManagerService.java
- // The previous app was getting ready to show a
- // starting window, but hasn't yet done so. Steal it!
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG,
- "Moving pending starting from " + ttoken
- + " to " + wtoken);
- wtoken.startingData = ttoken.startingData;
- ttoken.startingData = null;
- ttoken.startingMoved = true;
- Message m = mH.obtainMessage(H.ADD_STARTING, wtoken);
- // Note: we really want to do sendMessageAtFrontOfQueue() because we
- // want to process the message ASAP, before any other queued
- // messages.
- mH.sendMessageAtFrontOfQueue(m);
- return;
復制代碼 2. Starting Window 是設置了Animation的 addStartingWindow()@PhoneWindowManager.java
- final WindowManager.LayoutParams params = win.getAttributes();
- params.token = appToken;
- params.packageName = packageName;
- params.windowAnimations = win.getWindowStyle().getResourceId(
- com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
- params.setTitle("Starting " + packageName);
復制代碼 3. Starting Window 同普通的activity Window一樣,均為一個(gè)PhoneWindow,其中包看著(zhù)DecorView和ViewRoot。 addStartingWindow()@PhoneWindowManager.java
- try {
- Context context = mContext;
- boolean setTheme = false;
- //Log.i(TAG, "addStartingWindow " + packageName + ": nonLocalizedLabel="
- // + nonLocalizedLabel + " theme=" + Integer.toHexString(theme));
- if (theme != 0 || labelRes != 0) {
- try {
- context = context.createPackageContext(packageName, 0);
- if (theme != 0) {
- context.setTheme(theme);
- setTheme = true;
- }
- } catch (PackageManager.NameNotFoundException e) {
- // Ignore
- }
- }
- if (!setTheme) {
- context.setTheme(com.android.internal.R.style.Theme);
- }
- //創(chuàng )建PhoneWindow
- Window win = PolicyManager.makeNewWindow(context);
- if (win.getWindowStyle().getBoolean(
- com.android.internal.R.styleable.Window_windowDisablePreview, false)) {
- return null;
- }
-
- Resources r = context.getResources();
- win.setTitle(r.getText(labelRes, nonLocalizedLabel));
-
- win.setType(
- WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
- // Force the window flags: this is a fake window, so it is not really
- // touchable or focusable by the user. We also add in the ALT_FOCUSABLE_IM
- // flag because we do know that the next window will take input
- // focus, so we want to get the IME window up on top of us right away.
- win.setFlags(
- WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
- WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
- WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
- WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
-
- win.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.MATCH_PARENT);
-
- final WindowManager.LayoutParams params = win.getAttributes();
- params.token = appToken;
- params.packageName = packageName;
- params.windowAnimations = win.getWindowStyle().getResourceId(
- com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
- params.setTitle("Starting " + packageName);
- WindowManagerImpl wm = (WindowManagerImpl)
- context.getSystemService(Context.WINDOW_SERVICE);
- View view = win.getDecorView();
- if (win.isFloating()) {
- // Whoops, there is no way to display an animation/preview
- // of such a thing! After all that work... let's skip it.
- // (Note that we must do this here because it is in
- // getDecorView() where the theme is evaluated... maybe
- // we should peek the floating attribute from the theme
- // earlier.)
- return null;
- }
-
- if (localLOGV) Log.v(
- TAG, "Adding starting window for " + packageName
- + " / " + appToken + ": "
- + (view.getParent() != null ? view : null));
- <span style="white-space: pre;"> </span> //向WindowManager addView
- wm.addView(view, params);
- // Only return the view if it was successfully added to the
- // window manager... which we can tell by it having a parent.
- return view.getParent() != null ? view : null;
- }
復制代碼

2. 啟動(dòng)新進(jìn)程 如果新啟動(dòng)的activity需要運行在新的進(jìn)程中,那么這個(gè)流程就涉及到了一個(gè)新進(jìn)程的啟動(dòng),由于畫(huà)圖的局限性,這個(gè)過(guò)程在上圖中沒(méi)有體現出來(lái)。 所有的ProcessRecord被存儲在mProcessNames變量中,以當前的進(jìn)程的名字為索引。 @ActivityManagerService.java
- final ProcessMap<ProcessRecord> mProcessNames
- = new ProcessMap<ProcessRecord>();
復制代碼 進(jìn)程名字的確定有如下規則: ①如果Activity設置了android:process屬性,則processName為屬性設置的值;
@ComponentInfo.java
- public String processName;
復制代碼② 如果Activity沒(méi)有設置android:process屬性,那么Activity的processName為Application的processName。如果Application設置了process屬性,那么processName為該值;如果沒(méi)有設置,processName為Package的名字,即
@PackageItemInfo.java - public String packageName;
復制代碼整個(gè)進(jìn)程啟動(dòng)的過(guò)程前面有一篇文章介紹過(guò),就不在介紹。 3. Application Transition Application Transition是android在實(shí)現窗口切換過(guò)程中,為了提供更好的用戶(hù)體驗和特定的指示,來(lái)呈現出的過(guò)渡效果。一般情況下,Application Transition是一個(gè)動(dòng)畫(huà)效果。 Application Transition有兩種,一種是啟動(dòng)activity時(shí)的Transition動(dòng)畫(huà),一種是啟動(dòng)一些widget時(shí)的Transition動(dòng)畫(huà)。 Transition類(lèi)型的設置通過(guò)函數prepareAppTransition()@WindowManagerService.java來(lái)進(jìn)行. 設置完Transition類(lèi)型之后,通過(guò)executeAppTransition()@WindowManagerService.java函數來(lái)執行這個(gè)Transition。 prepareAppTransition()-->executeAppTransition()-->performLayoutAndPlaceSurfacesLocked(); 具體的Transition的animation繪制過(guò)程在分析WMS再做分析。 3.1 activity Transition 當啟動(dòng)一個(gè)activity時(shí),系統會(huì )給它的window呈現提供一個(gè)animation,這個(gè)animation可以在frameworks/base/core/res/res/values/styles.xml中進(jìn)行設置 - <!-- Standard animations for a full-screen window or activity. -->
- <style name="Animation.Activity">
- <item name="activityOpenEnterAnimation">@anim/activity_open_enter</item>
- <item name="activityOpenExitAnimation">@anim/activity_open_exit</item>
- <item name="activityCloseEnterAnimation">@anim/activity_close_enter</item>
- <item name="activityCloseExitAnimation">@anim/activity_close_exit</item>
- <item name="taskOpenEnterAnimation">@anim/task_open_enter</item>
- <item name="taskOpenExitAnimation">@anim/task_open_exit</item>
- <item name="taskCloseEnterAnimation">@anim/task_close_enter</item>
- <item name="taskCloseExitAnimation">@anim/task_close_exit</item>
- <item name="taskToFrontEnterAnimation">@anim/task_open_enter</item>
- <item name="taskToFrontExitAnimation">@anim/task_open_exit</item>
- <item name="taskToBackEnterAnimation">@anim/task_close_enter</item>
- <item name="taskToBackExitAnimation">@anim/task_close_exit</item>
- <item name="wallpaperOpenEnterAnimation">@anim/wallpaper_open_enter</item>
- <item name="wallpaperOpenExitAnimation">@anim/wallpaper_open_exit</item>
- <item name="wallpaperCloseEnterAnimation">@anim/wallpaper_close_enter</item>
- <item name="wallpaperCloseExitAnimation">@anim/wallpaper_close_exit</item>
- <item name="wallpaperIntraOpenEnterAnimation">@anim/wallpaper_intra_open_enter</item>
- <item name="wallpaperIntraOpenExitAnimation">@anim/wallpaper_intra_open_exit</item>
- <item name="wallpaperIntraCloseEnterAnimation">@anim/wallpaper_intra_close_enter</item>
- <item name="wallpaperIntraCloseExitAnimation">@anim/wallpaper_intra_close_exit</item>
- </style>
復制代碼activity啟動(dòng)的animation根據當前的activity所在的task狀態(tài)有所不同,從上面的xml中的animation定義中就可以看出,它的分類(lèi): ★ 如果啟動(dòng)的activity運行在原來(lái)的task中,那么使用animation activityOpenEnterAnimation/activityOpenExitAnimation; ★ 如果啟動(dòng)的activity運行在新的task中,那么使用animation taskOpenEnterAnimation/taskOpenExitAnimation; ★ 如果結束的activity結束之后原來(lái)的task還存在,那么使用activityCloseEnterAnimation/activityCloseExitAnimation; ★ 如果結束的activity結束之后原來(lái)的task將不存在,也即次activity為task最后的activity,那么使用taskCloseEnterAnimation/taskCloseExitAnimation; ★ 一些特定的情況下,AMS需要將某個(gè)task move到最前面,例如上一篇文章中的task reparenting過(guò)程,此時(shí)使用taskToFrontEnterAnimation/taskToFrontExitAnimation;
★ 一些特定的情況下,AMS需要將某個(gè)task move到最底端,此時(shí)使用taskToBackEnterAnimation/taskToBackExitAnimation; ★ 如果當前的activity使用的theme中的參數android:windowShowWallpaper為true,此時(shí)的activity應該以當前的壁紙為背景,并且前一個(gè)顯示的activity的背景不是當前的壁紙,此時(shí)使用wallpaperOpenEnterAnimation/wallpaperOpenExitAnimation/wallpaperCloseEnterAnimation/wallpaperCloseExitAnimation, 如下面activity所示: ★ 如果當前的activity使用的theme中的參數android:windowShowWallpaper為true,此時(shí)的activity應該以當前的壁紙為背景,并且前一個(gè)顯示的activity的背景是當前的壁紙,此時(shí)使用wallpaperIntraOpenEnterAnimation/wallpaperIntraOpenExitAnimation/wallpaperIntraCloseEnterAnimation/wallpaperIntraCloseExitAnimation. 下面代碼即是判斷當前應該選擇那些帶有wallpaper的Transition類(lèi)型。 performLayoutAndPlaceSurfacesLockedInner()@WindowManagerService.java - final int NC = mClosingApps.size();
- NN = NC + mOpeningApps.size();
- for (i=0; i<NN; i++) {
- AppWindowToken wtoken;
- int mode;
- if (i < NC) {
- wtoken = mClosingApps.get(i);
- mode = 1;
- } else {
- wtoken = mOpeningApps.get(i-NC);
- mode = 2;
- }
- if (mLowerWallpaperTarget != null) {
- if (mLowerWallpaperTarget.mAppToken == wtoken
- || mUpperWallpaperTarget.mAppToken == wtoken) {
- foundWallpapers |= mode;
- }
- }
- if (wtoken.appFullscreen) {
- WindowState ws = wtoken.findMainWindow();
- if (ws != null) {
- // If this is a compatibility mode
- // window, we will always use its anim.
- if ((ws.mAttrs.flags&FLAG_COMPATIBLE_WINDOW) != 0) {
- animLp = ws.mAttrs;
- animToken = ws.mAppToken;
- bestAnimLayer = Integer.MAX_VALUE;
- } else if (ws.mLayer > bestAnimLayer) {
- animLp = ws.mAttrs;
- animToken = ws.mAppToken;
- bestAnimLayer = ws.mLayer;
- }
- }
- }
- }
- if (foundWallpapers == 3) {
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
- "Wallpaper animation!");
- switch (transit) {
- case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:
- case WindowManagerPolicy.TRANSIT_TASK_OPEN:
- case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT:
- transit = WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN;
- break;
- case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE:
- case WindowManagerPolicy.TRANSIT_TASK_CLOSE:
- case WindowManagerPolicy.TRANSIT_TASK_TO_BACK:
- transit = WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_CLOSE;
- break;
- }
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
- "New transit: " + transit);
- } else if (oldWallpaper != null) {
- // We are transitioning from an activity with
- // a wallpaper to one without.
- transit = WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE;
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
- "New transit away from wallpaper: " + transit);
- } else if (mWallpaperTarget != null) {
- // We are transitioning from an activity without
- // a wallpaper to now showing the wallpaper
- transit = WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN;
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
- "New transit into wallpaper: " + transit);
- }
復制代碼 3.2 widget Transition 每個(gè)widget在啟動(dòng)時(shí)的animation和activity不一樣,并且在frameworks/base/core/res/res/values/styles.xml中可以設置不同widget。 - private boolean applyAnimationLocked(WindowState win,
- int transit, boolean isEntrance) {
- if (win.mLocalAnimating && win.mAnimationIsEntrance == isEntrance) {
- // If we are trying to apply an animation, but already running
- // an animation of the same type, then just leave that one alone.
- return true;
- }
- // Only apply an animation if the display isn't frozen. If it is
- // frozen, there is no reason to animate and it can cause strange
- // artifacts when we unfreeze the display if some different animation
- // is running.
- if (!mDisplayFrozen && mPolicy.isScreenOn()) {
- int anim = mPolicy.selectAnimationLw(win, transit);
- int attr = -1;
- Animation a = null;
- if (anim != 0) {
- a = AnimationUtils.loadAnimation(mContext, anim);
- } else {
- switch (transit) {
- case WindowManagerPolicy.TRANSIT_ENTER:
- attr = com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation;
- break;
- case WindowManagerPolicy.TRANSIT_EXIT:
- attr = com.android.internal.R.styleable.WindowAnimation_windowExitAnimation;
- break;
- case WindowManagerPolicy.TRANSIT_SHOW:
- attr = com.android.internal.R.styleable.WindowAnimation_windowShowAnimation;
- break;
- case WindowManagerPolicy.TRANSIT_HIDE:
- attr = com.android.internal.R.styleable.WindowAnimation_windowHideAnimation;
- break;
- }
- if (attr >= 0) {
- a = loadAnimation(win.mAttrs, attr);
- }
- }
- if (DEBUG_ANIM) Slog.v(TAG, "applyAnimation: win=" + win
- + " anim=" + anim + " attr=0x" + Integer.toHexString(attr)
- + " mAnimation=" + win.mAnimation
- + " isEntrance=" + isEntrance);
- if (a != null) {
- if (DEBUG_ANIM) {
- RuntimeException e = null;
- if (!HIDE_STACK_CRAWLS) {
- e = new RuntimeException();
- e.fillInStackTrace();
- }
- Slog.v(TAG, "Loaded animation " + a + " for " + win, e);
- }
- win.setAnimation(a);
- win.mAnimationIsEntrance = isEntrance;
- }
- } else {
- win.clearAnimation();
- }
- return win.mAnimation != null;
- }
復制代碼 4. Activity啟動(dòng) 文章的前面的內容中分析的一直是AMS對一個(gè)新啟動(dòng)的activity的管理,activity在A(yíng)MS中的形態(tài)是以ActivityRecord的形式來(lái)管理的,下面的時(shí)序圖中則是描繪了應用中一個(gè)activity的創(chuàng )建并啟動(dòng)的過(guò)程。
5. Activity pausing過(guò)程 Activity pausing過(guò)程有3種情況: 1. 第一種情況是從一個(gè)activity啟動(dòng)另一個(gè)activity的同時(shí),也伴隨著(zhù)前一個(gè)activity的pause過(guò)程。?? resumeTopActivityLocked()@ActivityStack.java
- // We need to start pausing the current activity so the top one
- // can be resumed...
- if (mResumedActivity != null) {
- if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing");
- startPausingLocked(userLeaving, false);
- return true;
- }
復制代碼 2. 第二種情況是當PowerManagerService要求AMS休眠或者設備shutDown時(shí); @ActivityStack.java
- void pauseIfSleepingLocked() {
- if (mService.mSleeping || mService.mShuttingDown) {
- if (!mGoingToSleep.isHeld()) {
- mGoingToSleep.acquire();
- if (mLaunchingActivity.isHeld()) {
- mLaunchingActivity.release();
- mService.mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
- }
- }
- // If we are not currently pausing an activity, get the current
- // one to pause. If we are pausing one, we will just let that stuff
- // run and release the wake lock when all done.
- if (mPausingActivity == null) {
- if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause...");
- if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false");
- startPausingLocked(false, true);
- }
- }
- }
復制代碼 3.第三種情況是一個(gè)activity finish過(guò)程中。這個(gè)下面再介紹。 下圖為第一種情況的時(shí)序圖,整個(gè)pausing過(guò)程的是相同的,因此以一種情況的時(shí)序圖來(lái)體現activity的pausing過(guò)程。
6. Activity Stoping 過(guò)程 我們知道,當Activity不可見(jiàn)時(shí)會(huì )執行stoping的過(guò)程,下面我們就來(lái)分析以下一個(gè)activity是怎么來(lái)進(jìn)行stop的。下面給出整個(gè)stop過(guò)程的時(shí)序圖: 在stop中有一個(gè)很重要的概念就是activity idle狀態(tài),不論是activity被新啟動(dòng)的activity完全覆蓋,還是activity被finish,也就是activity的stop過(guò)程以及finsh過(guò)程,均是在最新被resume的activity已經(jīng)resume完成之后才去處理。 我們可以想象一下,每個(gè)應用程序的主線(xiàn)程ActivityThread中,當沒(méi)有任何的消息待處理時(shí),此時(shí)我們可以認為此時(shí)的已被resumed的activity狀態(tài)時(shí)空閑的,沒(méi)有任何的人機交互。因此android設計者將前一個(gè)被完全覆蓋不可見(jiàn)的或者finish的activity的stop或finish操作放在此時(shí)來(lái)處理。這樣做是合情合理,畢竟stop或者finish一個(gè)activity以及顯示新的activity之間的關(guān)系是同步,是必須有先后順序的,為了達到更好的用戶(hù)體驗,理所當然應該是先顯示新的activity,然后采取stop或者finish舊的activity。為了實(shí)現這個(gè)目的,android設計者使用了MessageQueue的這個(gè)IdleHandler機制。 首先,我們看一下MessageQueue的IdleHandler機制。 next ()@MessageQueue.java
- // 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("MessageQueue", "IdleHandler threw exception", t);
- }
- if (!keep) {
- synchronized (this) {
- mIdleHandlers.remove(idler);
- }
- }
- }
復制代碼在A(yíng)ctivityThread線(xiàn)程的Looper中,Looper會(huì )不停的去查找消息隊列中是否有消息需要處理,如果沒(méi)有任何的消息待處理,那么將查看當前的消息隊列是否有IdleHandler注冊,如果有逐個(gè)執行這些IdleHandler。
明白了IdleHandler的機制,回過(guò)頭來(lái)了看ActivityThread的IdleHandler的注冊過(guò)程,代碼如下。 handleResumeActivity()@ActivityThread.java
- r.nextIdle = mNewActivities;
- mNewActivities = r;
- if (localLOGV) Slog.v(
- TAG, "Scheduling idle handler for " + r);
- Looper.myQueue().addIdleHandler(new Idler());
復制代碼 7. Activity finishing過(guò)程 用戶(hù)從application結束當前的activity,如按back鍵; 如同activity不可見(jiàn)時(shí)的處理一樣,activity的finishing過(guò)程同樣是在新的activity被resume之后才去執行,但是存在一種情況,當mHistory棧中存在多個(gè)(多于4個(gè))activity時(shí),假如此時(shí)user以很快的速度去按back鍵,并且在第一個(gè)需resume的activity尚未被resume完成時(shí),已經(jīng)被user觸發(fā)了多次back鍵,此時(shí)應該怎么處理finish過(guò)程呢? 按照上面的邏輯來(lái)看,user不停的以很快的速度去觸發(fā)back鍵,直到回到home activity,這種情況下ActivityThread的Looper一直會(huì )有消息需要處理,根本不可能去處理它的IdleHandler,也就不可能去處理各個(gè)activity的finish過(guò)程,直到回到home activity之后才能有空閑去處理。我們可以想象一下如果按照這個(gè)邏輯去操作的話(huà),會(huì )有什么問(wèn)題? 設想一下,我們累計了多個(gè)activity在A(yíng)ctivityThread的Looper在idle狀態(tài)下處理,那么這個(gè)過(guò)程將是比較長(cháng)的,假如此時(shí)又有user觸發(fā)了啟動(dòng)actibity的操作,那么ActivityThread將會(huì )同時(shí)處理累計的activity的finish過(guò)程,同時(shí)又需要處理activity的啟動(dòng)過(guò)程,那么這么做的結果只能是給用戶(hù)帶來(lái)系統很慢的用戶(hù)體驗。因此上面的finish邏輯需要進(jìn)行一定的矯正與修改。 AMS在累計的activity超過(guò)3個(gè)時(shí),就會(huì )強制調用Idle處理操作。這么做就有效的消耗了累計的activity的finish過(guò)程,就很大程度上減輕了上述所說(shuō)的問(wèn)題。 finishCurrentActivityLocked()@ActivityStack.java
- // First things first: if this activity is currently visible,
- // and the resumed activity is not yet visible, then hold off on
- // finishing until the resumed one becomes visible.
- if (mode == FINISH_AFTER_VISIBLE && r.nowVisible) {
- if (!mStoppingActivities.contains(r)) {
- mStoppingActivities.add(r);
- Slog.d(TAG, "finishCurrentActivityLocked mStoppingActivities size:" + mStoppingActivities.size());
- if (mStoppingActivities.size() > 3) {
- // If we already have a few activities waiting to stop,
- // then give up on things going idle and start clearing
- // them out.
- Message msg = Message.obtain();
- msg.what = IDLE_NOW_MSG;
- mHandler.sendMessage(msg);
- }
- }
- r.state = ActivityState.STOPPING;
- mService.updateOomAdjLocked();
- return r;
- }
復制代碼同樣的問(wèn)題也存在與activity啟動(dòng)過(guò)程中,假如user以很快的速度去不停的啟動(dòng)activity,那么被覆蓋的activity的stop過(guò)程很上述的finish過(guò)程一樣,也會(huì )不停的累計,出現相同的問(wèn)題。解決的思路也是一致的。
completePauseLocked()@ActivityStack.java - mStoppingActivities.add(prev);
- if (mStoppingActivities.size() > 3) {
- // If we already have a few activities waiting to stop,
- // then give up on things going idle and start clearing
- // them out.
- if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle");
- Message msg = Message.obtain();
- msg.what = IDLE_NOW_MSG;
- mHandler.sendMessage(msg);
- }
復制代碼 |