在Android系統中,同一時刻只有一個Activity組件是處於激活狀態的,因此,當ActivityManagerService服務激活了一個新的Activity組件時,它就需要通知WindowManagerService服務將該Activity組件的窗口顯示出來,這會涉及到將焦點和屏幕等資源從前一個激活的Activity組件切換到後一個激活的Activity組件的過程,本文就詳細分析這個過程。
Activity窗口的切換操作是在新激活的Activity組件的啓動過程進行的。具體來說,就是在前一個激活的Activity組件進入到Paused狀態並且新激活的Activity組件進之到Resumed 狀態之後,將前一個激活的Activity組件的窗口設置爲不可見,以及將新激活的Activity組件的窗口設置爲可見。整個切換過程是需要在ActivityManagerService服務和WindowManagerService服務的協作之下進行的,如圖1所示。
圖1 Activity窗口的切換操作示意圖
WindowManagerService服務在執行Activity窗口的切換操作的時候,會給參與切換操作的Activity組件的設置一個動畫,以便可以向用戶展現一個Activity組件切換效果,從而提高用戶體驗。事實上,一個Activity窗口在由不可見狀態切換到可見狀態的過程中,除了會被設置一個Activity組件切換動畫之外,還有被設置一個窗口進入動畫,此外,如果該Activity窗口是附加在另外一個窗口之上的,並且這個被附加的窗口正在顯示一個動畫,那麼這個動畫也同時會被設置給到該Activity窗口的顯示過程中去。本文主要是關注Activity窗口的切換操作,在接下來的一篇文章中分析窗口的動畫框架時,我們再詳細分析上述三種動畫是如何作用在窗口的顯示過程中的。
ActivityManagerService服務在啓動一個Activity組件的過程中,會調用到ActivityStack類的成員函數startActivityLocked。ActivityStack類的成員函數startActivityLocked首先會給正在啓動的Activity組件準備一個切換操作,接着再調用其它的成員函數來通知前一個激活的Activity組件進入到Paused狀態。等到前一個激活的Activity組件進入到Paused狀態之後,ActivityManagerService服務就會檢查用來運行正在啓動的Activity組件的進程啓動起來了沒有。如果這個進程還沒有啓動,那麼ActivityManagerService服務就會將該進程啓動起來,然後再調用ActivityStack類的成員函數realStartActivityLocked來將正在啓動的Activity組件加載起來,並且將它的狀態設置爲Resumed,最後通知WindowManagerService服務執行前面所準備的切換操作。
接下來,我們就從ActivityStack類的成員函數startActivityLocked開始分析Activity窗口的切換過程,如圖2所示。
圖2 Activity窗口的切換過程
ActivityStack的startActivityLocked函數
我們先看看ActivityStack的startActivityLocked函數
- final void startActivityLocked(ActivityRecord r, boolean newTask,
- boolean doResume, boolean keepCurTransition, Bundle options) {
- ......
-
- // Slot the activity into the history stack and proceed
- if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task,
- new RuntimeException("here").fillInStackTrace());
- task.addActivityToTop(r);
- task.setFrontOfTask();
-
- r.putInHistory();
- if (!isHomeStack() || numActivities() > 0) {
- ......
- if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, keepCurTransition);
- mNoAnimActivities.add(r);
- } else {
- mWindowManager.prepareAppTransition(newTask
- ? r.mLaunchTaskBehind
- ? AppTransition.TRANSIT_TASK_OPEN_BEHIND
- : AppTransition.TRANSIT_TASK_OPEN
- : AppTransition.TRANSIT_ACTIVITY_OPEN, keepCurTransition);
- mNoAnimActivities.remove(r);
- }
- mWindowManager.addAppToken(task.mActivities.indexOf(r),
- r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
- (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId,
- r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind);
- ......
-
- if (doResume) {
- mStackSupervisor.resumeTopActivitiesLocked(this, r, options);
- }
- }
當Activity組件的一個Intent對象的成員函數getFlags返回的一個標誌值的Intent.FLAG_ACTIVITY_NO_ANIMATION位可能會不等於0,這意味着正在啓動的Activity組件不需要顯示切換動畫。在這種情況下,ActivityManagerSerivce服務就會通知WindowManagerService服務不需要準備一個Activity組件切換操作,這是通過以WindowManagerPolicy.TRANSIT_NONE爲參數來調用ActivityStack類的成員變量mService所指向的一個ActivityManagerService對象的成員變量mWindowManager所描述的一 個WindowManagerService對象的成員函數prepareAppTransition來實現的。
當系統需要執行一個Activity組件切換操作,即需要WindowManagerService服務在顯示正在啓動的Activity組件的窗口時應用一個切換動畫,那麼這個動畫的類型也是有講究的。具體來說,如果參數r描述的是Activity組件是需要在在一個新的任務中啓動的,即參數newTask的值等於true,那麼切換動畫的類型就指定爲WindowManagerPolicy.TRANSIT_TASK_OPEN,否則的話,切換動畫的類型就指定爲WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN。
無論如何,最終指定的切換動畫的類型都是通過調用ActivityStack類的成員變量mService所指向的一個ActivityManagerService對象的成員變量mWindowManager所描述的一個WindowManagerService對象的成員函數prepareAppTransition來通知WindowManagerService服務的。
ActivityStack類的成員函數startActivityLocked通知WindowManagerService服務準備好一個Activity組件切換操作之後,如果參數doResume的值等於true,那麼它就會繼續調用另外一個成員函數resumeTopActivityLocked來繼續執行啓動參數r所描述的一個Activity組件的操作。
接下來,我們就首先分析WindowManagerService類的成員函數prepareAppTransition的實現,以便可以瞭解WindowManagerService服務是如何準備一個Activity組件切換操作的,然後再回過頭來分析ActivityStack類的成員函數resumeTopActivityLocked是如何繼續執行啓動參數r所描述的一個Activity組件的操作的。
WMS的prepareAppTransition函數
下面我們就來分析下WMS的prepareAppTransition函數:
- public void prepareAppTransition(int transit, boolean alwaysKeepCurrent) {
- ......
-
- synchronized(mWindowMap) {
- if (!mAppTransition.isTransitionSet() || mAppTransition.isTransitionNone()) {
- mAppTransition.setAppTransition(transit);
- } else if (!alwaysKeepCurrent) {
- if (transit == AppTransition.TRANSIT_TASK_OPEN
- && mAppTransition.isTransitionEqual(
- AppTransition.TRANSIT_TASK_CLOSE)) {
- // Opening a new task always supersedes a close for the anim.
- mAppTransition.setAppTransition(transit);
- } else if (transit == AppTransition.TRANSIT_ACTIVITY_OPEN
- && mAppTransition.isTransitionEqual(
- AppTransition.TRANSIT_ACTIVITY_CLOSE)) {
- // Opening a new activity always supersedes a close for the anim.
- mAppTransition.setAppTransition(transit);
- }
- }
- if (okToDisplay() && mAppTransition.prepare()) {
- mSkipAppTransitionAnimation = false;
- }
- if (mAppTransition.isTransitionSet()) {
- mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
- mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUT, 5000);
- }
- }
- }
WindowManagerService類的成員變量mAppTransition描述的就是WindowManagerService服務接下來要執行一個Activity組件切換操作的類型,也就是要執行的一個Activity組件切換動畫的類型。WindowManagerService類的成員函數prepareAppTransition按照以下規則來設置WindowManagerService服務接下來要執行的Activity組件切換動畫的類型:
1. 當 WindowManagerService類的成員變量mAppTransition的值等於WindowManagerPolicy.TRANSIT_UNSET或者WindowManagerPolicy.TRANSIT_NONE的時候,就說明WindowManagerService服務接下來沒有Activity組件切換動畫等待執行的,這時候參數transit所描述的Activity組件切換動畫就可以作爲WindowManagerService服務接下來要執行的Activity組件切換動畫。
2. 當WindowManagerService類的成員變量mAppTransition的值等於WindowManagerPolicy.TRANSIT_TASK_CLOSE,那麼就說明WindowManagerService服務接下來要執行一個關閉Activity組件任務的切換動畫等待執行的。在這種情況下,如果參數transit所描述的是一個打開Activity組件任務的切換動畫,即它的值等於WindowManagerPolicy.TRANSIT_TASK_OPEN,那麼就需要將WindowManagerService服務接下來要執行的Activity組件切換動畫爲打開Activity組件任務類型的。這是因爲打開Activity組件任務的切換動畫的優先級高於關閉Activity組件任務的切換動畫。
3. 當WindowManagerService類的成員變量mAppTransition的值等於WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE,那麼就說明WindowManagerService服務接下來要執行一個關閉Activity組件的切換動畫等待執行的。在這種情況下,如果參數transit所描述的是一個打開Activity組件的切換動畫,即它的值等於WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN,那麼就需要將WindowManagerService服務接下來要執行的Activity組件切換動畫爲打開Activity組件類型的。這是因爲打開Activity組件的切換動畫的優先級高於關閉Activity組件的切換動畫。
mSkipAppTransitionAnimation表示WindowManagerService服務是否需要不執行Activity組件的切換動畫?
最後,WindowManagerService類的成員函數prepareAppTransition還會調用成員變量mH所描述的一個H對象的成員函數sendMessageDelayed來向WindowManagerService服務所運行在的線程發送一個類型爲APP_TRANSITION_TIMEOUT的消息。這個消息將在5秒後被執行,是用來強制前面所設置的Activity組件切換動畫要在5秒之內執行完成的,否則的話,WindowManagerService服務就會認爲該切換動畫執行超時了。
- case APP_TRANSITION_TIMEOUT: {
- synchronized (mWindowMap) {
- if (mAppTransition.isTransitionSet() || !mOpeningApps.isEmpty()
- || !mClosingApps.isEmpty()) {
- mAppTransition.setTimeout();//設置timeout狀態
- performLayoutAndPlaceSurfacesLocked();//時間到了強制刷新
- }
- }
- break;
- }
setTimeout只是設置了一個狀態
- void setTimeout() {
- mAppTransitionState = APP_STATE_TIMEOUT;
- }
而setAppTransition是設置Activity下一個切換類型
- void setAppTransition(int transit) {
- mNextAppTransition = transit;
- }
ActivityStackSupervisor的realStartActivityLocked函數
這一步執行完成之後,WindowManagerService服務接下來要執行的Activity組件切換操作或者說切換動畫就準備完成了。注意,這時候只是準備好Activity組件切換動畫,但是這個切換動畫還不能執行,要等到前一個激活的Activity組件進入到Paused狀態並且接下來正在啓動的Activity組件進入到Resumed狀態之後才能執行。
回到前面的Step 1中,即ActivityStack類的成員函數startActivityLocked,接下來它就會調用另外一個成員函數resumeTopActivityLocked來繼續啓動指定的Activity組件。ActivityStack類的成員函數resumeTopActivityLocked以及接下來要調用的其它成員函數就是執行以下兩個操作:
1. 通知當前處於激活狀態的Activity組件所運行在的應用程序進程,它所運行的一個Activity組件要由Resumed狀態進入到Paused狀態了。
2. 檢查用來運行當前正在啓動的Activity組件的應用程序進程是否已經啓動起來了。如果已經啓動起來,那麼就會直接通知該應用程序進程將正在啓動的Activity組件加載起來,否則的話,就會先將該應用程序進程啓動起來,然後再通知它將正在啓動的Activity組件加載起來。
在第2步中,通知相應的應用程序進程將正在啓動的Activity組件加載起來是通過調用ActivityStackSupervisor類的成員函數realStartActivityLocked來實現的,接下來我們就繼續分析這個成員函數的實現,以便可以繼續瞭解Activity組件的切換操作的執行過程。
- final boolean realStartActivityLocked(ActivityRecord r,
- ProcessRecord app, boolean andResume, boolean checkConfig)
- throws RemoteException {
-
- if (andResume) {
- r.startFreezingScreenLocked(app, 0);
- mWindowManager.setAppVisibility(r.appToken, true);
-
- // schedule launch ticks to collect information about slow apps.
- r.startLaunchTickingLocked();
- }
-
- try {
- ......
- app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
- System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
- new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage,
- task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
- newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
-
- ......
- } catch (RemoteException e) {
- .....
- }
-
- ......
- if (andResume) {
- stack.minimalResumeActivityLocked(r);
- } else {
- r.state = STOPPED;
- r.stopped = true;
- }
- ......
-
- return true;
- }
上面這個函數主要有如下步驟:
1. 通知WindowManagerService服務將參數r所描述的Activity組件的可見性設置爲true,這是通過調用ActivityStack類的成員變量mService所指向的一個ActivityManagerService對象的成員變量mWindowManager所描述的一個WindowManagerService對象的成員函數setAppVisibility來實現的。
2. 通知參數r所描述的Activity組件所運行在的應用程序進程將它加載起來,這是通過調用參數app所指向的一個ProcessRecord對象的成員變量thread所描述的一個類型爲ApplictionThread的Binder代理對象的成員函數scheduleLaunchActivity來實現的。
3. 當參數andResume的值等於true的時候,就表示在執行第2步時,參數r所描述的Activity組件所運行在的應用程序進程已經將它的狀態設置爲Resumed了,即已經調用過它的成員函數onResume了。在這種情況,ActivityManagerService服務也需要將該Activity組件的狀態設置爲Resumed了,即將r所指向的一個ActivityRecord對象的成員變量state的值設置爲ActivityState.RESUMED,並且將ActivityStack類的成員變量mResumedActivity的值設置爲r,以便表示當前激活的Activity組件爲參數r所描述的Activity組件。最後,ActivityManagerService服務還需要調用ActivityStack類的成員函數minimalResumeActivityLocked函數。而minimalResumeActivityLocked主要調用了completeResumeLocked函數。completeResumeLocked函數來通知WindowManagerService服務執行在前面Step 2所準備好的Activity組件切換操作。
接下來,我們首先分析WindowManagerService類的成員函數setAppVisibility的實現,以便可以瞭解WindowManagerService服務是如何設置一個Activity組件的可見性的,接着再分析ActivityStack類的成員函數completeResumeLocked的實現,以便可以瞭解ActivityManagerService服務是如何通知WindowManagerService服務執行前面所準備好的一個Activity組件切換操作的。
WMS的setAppVisibility函數
- public void setAppVisibility(IBinder token, boolean visible) {
- ......
- AppWindowToken wtoken;
-
- synchronized(mWindowMap) {
- wtoken = findAppWindowToken(token);//找到APPWindowToken
- if (wtoken == null) {
- Slog.w(TAG, "Attempted to set visibility of non-existing app token: " + token);
- return;
- }
-
- mOpeningApps.remove(wtoken);
- mClosingApps.remove(wtoken);
- wtoken.waitingToShow = false;
- wtoken.hiddenRequested = !visible;
-
- // If we are preparing an app transition, then delay changing
- // the visibility of this token until we execute that transition.
- if (okToDisplay() && mAppTransition.isTransitionSet()) {
- if (!wtoken.mAppAnimator.usingTransferredAnimation &&
- (!wtoken.startingDisplayed || mSkipAppTransitionAnimation)) {
- wtoken.mAppAnimator.setDummyAnimation();
- }
- wtoken.inPendingTransaction = true;
- if (visible) {
- mOpeningApps.add(wtoken);//把要打開的應用的APPWindowToken放入mOpeningApps中
- wtoken.startingMoved = false;
- wtoken.mEnteringAnimation = true;
-
- if (wtoken.hidden) {
- wtoken.allDrawn = false;
- wtoken.deferClearAllDrawn = false;
- wtoken.waitingToShow = true;
-
- if (wtoken.clientHidden) {
- wtoken.clientHidden = false;
- wtoken.sendAppVisibilityToClients();
- }
- }
- } else {
- mClosingApps.add(wtoken);
- wtoken.mEnteringAnimation = false;
- }
- if (mAppTransition.getAppTransition() == AppTransition.TRANSIT_TASK_OPEN_BEHIND) {
- // We're launchingBehind, add the launching activity to mOpeningApps.
- final WindowState win =
- findFocusedWindowLocked(getDefaultDisplayContentLocked());
- if (win != null) {
- final AppWindowToken focusedToken = win.mAppToken;
- if (focusedToken != null) {
- focusedToken.hidden = true;
- mOpeningApps.add(focusedToken);
- }
- }
- }
- return;
- }
- .....
- setTokenVisibilityLocked(wtoken, null, visible, AppTransition.TRANSIT_UNSET,//設置
- true, wtoken.voiceInteraction);
- wtoken.updateReportedVisibilityLocked();
- .....
- }
- }
每一個Activity組件在WindowManagerService服務內部都對應有一個AppWindowToken對象,用來描述該Activity組件的窗口在WindowManagerService服務中的狀態。因此,WindowManagerService類的成員函數setAppVisibility就先通過調用成員函數findAppWindowToken來找到與參數token所描述的一個Activity組件所對應的一個AppWindowToken對象wtoken,以便接下來可以設置它的狀態。
注意,WindowManagerService類的成員函數setAppVisibility在修改參數token所描述的一個Activity組件的可見性的時候,需要考慮WindowManagerService服務接下來是否需要執行一個Activity組件操作,即WindowManagerService類的成員變量mNextAppTransition的值是否等於WindowManagerPolicy.TRANSIT_UNSET。如果是TRANSIT_UNSET的話,只要簡單地執行以下兩個操作即可。
1. 調用WindowManagerService類的成員函數setTokenVisibilityLocked來將參數token所描述的Activity組件的可見性設置爲參數visible所描述的值;(這個函數還會設置動畫,當有改變的時候還會調用performLayoutAndPlaceSurfacesLocked刷新系統)
2. 調用AppWindowToken對象wtoken的成員函數updateReportedVisibilityLocked來向ActivityManagerService服務報告參數token所描述的Activity組件的可見性。
如果不是TRANSIT_UNSET,也是就是已經設置了Transit,那麼參數token所描述的Activity組件就是正在等待執行切換操作的Activity組件,這時候修改的可見性就會複雜一些:
我們假設WindowManagerService服務接下來需要執行一個Activity組件操作,即WindowManagerService類的成員變量mNextAppTransition的值等於WindowManagerPolicy.TRANSIT_UNSET,這時候還需要滿足okToDisplay函數即:
1. 屏幕當前不是處於凍結狀態,即WindowManagerService類的成員變量mDisplayFrozen的值等於false;
2. 屏幕當前是點亮的,即WindowManagerService類的成員變量mPolicy所指向的一個PhoneWindowManager對象的成員函數isScreenOn的返回值等於true;
那麼WindowManagerService類的成員函數setAppVisibility接下來纔會開始修改參數token所描述的一個Activity組件的可見性,即:
1. 將AppWindowToken對象wtoken的成員變量hiddenRequested的值設置爲參數visible的相反值。也就是說,如果參數token所描述的Activity組件是可見的,那麼就將AppWindowToken對象wtoken的成員變量hiddenRequested的值設置爲false,否則的話,就設置爲true。
2. 調用AppWindowToken對象wtoken的成員函數setDummyAnimation來給參數token所描述的Activity組件設置一個啞動畫。注意,要等到執行參數token所描述的Activity組件的切換操作時,WindowManagerService服務纔會給該Activity組件設置一個合適的切換動畫。
3. 分別將AppWindowToken對象wtoken從WindowManagerService類的成員變量mOpeningApps和mClosingApps所描述的兩個ArrayList中刪除。注意,WindowManagerService類的成員變量mOpeningApps和mClosingApps保存的分別是系統當前正在打開和關閉的Activity組件,後面會根據參數visible的值來決定參數token所描述的Activity組件是正在打開的還是正在關閉的,以便可以將它放在WindowManagerService類的成員變量mOpeningApps或者mClosingApps中。
4. 將AppWindowToken對象wtoken的成員變量waitingToShow和waitingToHide的值都初始化爲false,表示參數token所描述的Activity組件既不是正在等待顯示的,也不是正在等待隱藏的,這兩個成員變量的值同樣是要根據參數visible的值來設置的。
5. 將AppWindowToken對象wtoken的成員變量inPendingTransaction的值設置爲true,表示參數token所描述的Activity組件正在等待切換。
接下來的操作取決於參數visible的值是true還是false。
假設參數visible的值等於true,那麼就表示要將參數token所描述的Activity組件設置可見,這時候WindowManagerService類的成員函數setAppVisibility會繼續執行以下操作:
1. 將AppWindowToken對象wtoken添加WindowManagerService類的成員變量mOpeningApps所描述的一個ArrayList中,表示參數token所描述的Activity組件是正在打開的。
2. 將AppWindowToken對象wtoken的成員變量startingDisplayed和startingMoved的值都設置爲false,表示參數token所描述的Activity組件的啓動窗口還沒有顯示出來,以及也沒有被轉移給其它的Activity組件。
3. 如果AppWindowToken對象wtoken的成員變量hidden的值等於true,那麼就意味着參數token所描述的Activity組件當前是不可見的。由於在這種情況下,參數token所描述的Activity組件正在等待打開,因此,該Activity組件的窗口一定是還沒有繪製出來,並且正在等待繪製以及顯示出來,這時候就需要將AppWindowToken對象wtoken的成員變量allDrawn和waitingToShow的值分別設置爲false和true。
4. 如果AppWindowToken對象wtoken的成員變量hidden的值等於true,並且另外一個成員變量clientHidden的值也等於true,那麼就說明在應用程序進程這一側看來,參數token所描述的Activity組件是不可見的,這時候就需要讓該應用程序進程認爲參數token所描述的Activity組件是可見的,以便該應用程序進程可以將參數token所描述的Activity組件的窗口繪製出來,這樣WindowManagerService服務接下來纔可以將該Activity組件的窗口顯示出來。通知應用程序進程將參數token所描述的Activity組件設置爲true是通過調用AppWindowToken對象wtoken的成員函數sendAppVisibilityToClients來實現的,同時在通知之前,也會將AppWindowToken對象wtoken的成員變量clientHidden設置爲false。
假設參數visible的值等於false,那麼就表示要將參數token所描述的Activity組件設置不可見,這時候WindowManagerService類的成員函數setAppVisibility會繼續執行以下操作:
1. 將AppWindowToken對象wtoken添加WindowManagerService類的成員變量mClosingApps所描述的一個ArrayList中,表示參數token所描述的Activity組件是正在關閉的。
2. 如果AppWindowToken對象wtoken的成員變量hidden的值等於false,那麼就意味着參數token所描述的Activity組件當前是可見的。由於在這種情況下,參數token所描述的Activity組件正在等待關閉,因此,該Activity組件的窗口接下來的狀態應該等待隱藏不見的,這時候就需要將AppWindowToken對象wtoken的成員變量waitingToHide的值設置爲true。
這一步執行完成之後,參數token所描述的Activity組件的可見性就設置好了,回到前面的Step 3中,即ActivityStack類的成員函數realStartActivityLocked中,接下來就會通知參數token所描述的Activity組件所運行在的應用程序進程將它加載起來,並且最後調用ActivityStack類的成員函數completeResumeLocked來通知WindowManagerService服務執行在前面Step 2所準備好的Activity組件切換操作。
ActivityStack的completeResumeLocked函數
- private void completeResumeLocked(ActivityRecord next) {
- ......
- mStackSupervisor.scheduleIdleTimeoutLocked(next);
-
- mStackSupervisor.reportResumedActivityLocked(next);
-
- ......
- }
ActivityStack類的成員函數completeResumeLocked主要是執行以下三個操作:
1. 調用ActivityStackSupervisor的scheduleIdleTimeoutLocked函數向AMS所運行在的線程發送一個類型爲IDLE_TIMEOUT_MSG的消息,這個消息將在IDLE_TIMEOUT毫秒後處理。這個類型爲IDLE_TIMEOUT_MSG實際是用來監控WindowManagerService服務能否在IDLE_TIMEOUT毫秒之內,完成參數next所描述的Activity組件的切換操作,並且將它的窗口顯示出來。如果能夠到的話,WindowManagerService服務就會通知ActivityManagerService服務,然後ActivityManagerService服務就會執行一些清理工作,例如,將那些已經處於Stopped狀態的Activity組件清理掉。如果不能夠做到的話,那麼ActivityStack類的成員函數completeResumeLocked也需要保證在IDLE_TIMEOUT毫秒之後,ActivityManagerService服務能夠執行上述的清理工作。
2. 調用ActivityStackSupervisor的eportResumedActivityLocked函數。
我們來看下ActivityStackSupervisor的eportResumedActivityLocked函數。
- boolean reportResumedActivityLocked(ActivityRecord r) {
- final ActivityStack stack = r.task.stack;
- if (isFrontStack(stack)) {
- mService.updateUsageStats(r, true);
- }
- if (allResumedActivitiesComplete()) {
- ensureActivitiesVisibleLocked(null, 0);
- mWindowManager.executeAppTransition();
- return true;
- }
- return false;
- }
這個函數當Activity完成onResume之後,會調用ensureActivitiesVisibleLocked函數(檢查哪些Activity組件是需要設置爲可見的,哪些Activity組件是需要設置爲不可見的)和WMS的executeAppTransition函數(通知WindowManagerService服務執行在前面Step 2所準備好的Activity組件切換操作)。
這裏我們再看下WMS的executeAppTransition函數
WMS的executeAppTransition函數
- public void executeAppTransition() {
- ......
-
- synchronized(mWindowMap) {
- if (mAppTransition.isTransitionSet()) {
- mAppTransition.setReady();
- final long origId = Binder.clearCallingIdentity();
- try {
- performLayoutAndPlaceSurfacesLocked();
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
- }
executeAppTransition函數邏輯很簡單,先看mAppTransition(下一個切換操作)是否被設置過,然後調用setReady設置狀態爲準備狀態。接着就調用performLayoutAndPlaceSurfacesLocked刷新系統。
WMS的handleAppTransitionReadyLocked函數
在WMS的performLayoutAndPlaceSurfacesLocked關於Activity切換的代碼主要如下:
- if (mAppTransition.isReady()) {
- defaultDisplay.pendingLayoutChanges |= handleAppTransitionReadyLocked(defaultWindows);
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAppTransitionReadyLocked",
- defaultDisplay.pendingLayoutChanges);
- }
這裏mAPPTransition的isReady函數就是在executeAppTransition函數中將下一個Activity的操作狀態設置成準備狀態的。 我們主要看下handleAppTransitionReadyLocked函數,這個函數比較長我們分段分析:
- public int handleAppTransitionReadyLocked(WindowList windows) {
- int changes = 0;
- int i;
- int appsCount = mOpeningApps.size();
- boolean goodToGo = true;
- if (!mAppTransition.isTimeout()) {
- for (i = 0; i < appsCount && goodToGo; i++) {
- AppWindowToken wtoken = mOpeningApps.valueAt(i);
- if (!wtoken.allDrawn && !wtoken.startingDisplayed
- && !wtoken.startingMoved) {
- goodToGo = false;
- }
- }
系統當前所有正在打開的Activity組件都保存在WindowManagerService類的成員變量mOpeningApps所描述的一個ArrayList中,因此,這段代碼就通過遍歷這個ArrayList來檢查每一個正在打開的Activity組件的UI是否已經繪製完成,即檢查對應的AppWindowToken對象的成員變量allDraw的值是否不等於true。如果不等於true的話,就說明還沒有繪製完成。此外,還要求這些正在打開的Activity組件的啓動窗口已經顯示結束,或者已經轉移給其它的Activity組件,即要求查對應的AppWindowToken對象的成員變量startingDisplayed和startingMoved的值均等於true。只要其中的一個正在打開的Activity組件不能滿足上述條件,那麼變量goodToGo的值就會等於false,表示這時候還不能執行Activity組件操作。
下面很大篇幅是壁紙相關的我們就不分析了。
我們繼續分析剩下的,主要就是mOpeningApps和mClosingApps中要打開的app和關閉的app的動畫設置。
- appsCount = mOpeningApps.size();//要啓動的app的APPWindowToken
- for (i = 0; i < appsCount; i++) {
- AppWindowToken wtoken = mOpeningApps.valueAt(i);
- final AppWindowAnimator appAnimator = wtoken.mAppAnimator;
-
- if (!appAnimator.usingTransferredAnimation) {
- appAnimator.clearThumbnail();
- appAnimator.animation = null;
- }
- wtoken.inPendingTransaction = false;
- if (!setTokenVisibilityLocked(
- wtoken, animLp, true, transit, false, voiceInteraction)){//設置動畫
- mNoAnimationNotifyOnTransitionFinished.add(wtoken.token);
- }
- wtoken.updateReportedVisibilityLocked();
- wtoken.waitingToShow = false;
-
- appAnimator.mAllAppWinAnimators.clear();
- final int windowsCount = wtoken.allAppWindows.size();
- for (int j = 0; j < windowsCount; j++) {
- appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator);
- }
- mAnimator.mAnimating |= appAnimator.showAllWindowsLocked();//這個函數會設置VSync回調,並且設置動畫
- mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
- ......
- }
- appsCount = mClosingApps.size();//要關閉的app的APPWindowToken
- for (i = 0; i < appsCount; i++) {
- AppWindowToken wtoken = mClosingApps.valueAt(i);
- final AppWindowAnimator appAnimator = wtoken.mAppAnimator;
-
- appAnimator.clearThumbnail();
- appAnimator.animation = null;
- wtoken.inPendingTransaction = false;
- setTokenVisibilityLocked(wtoken, animLp, false, transit, false, voiceInteraction);//設置動畫
- wtoken.updateReportedVisibilityLocked();
- wtoken.allDrawn = true;
- wtoken.deferClearAllDrawn = false;
-
- if (wtoken.startingWindow != null && !wtoken.startingWindow.mExiting) {
- scheduleRemoveStartingWindowLocked(wtoken);
- }
- mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
-
- ......
- }
-
- ......
-
- mOpeningApps.clear();
- mClosingApps.clear();
-
- // This has changed the visibility of windows, so perform
- // a new layout to get them all up-to-date.
- changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT
- | WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
- ......
- }
-
- return changes;
- }
這段代碼將給所有參與了切換操作的Activity組件設置一個切換動畫,而這個動畫就來自前面所獲得的變量animLp所指向的一個WindowManager.LayoutParams對象。由於參與了切換操作的Activity組件可以劃分爲兩類,即一類是正在打開的,一類是正在打閉的,因此,我們就分別討論這兩種類型的Activity組件的切換動畫的設置過程。
1)對於正在打開的Activity組件,它們的切換動畫的設置過程如下所示:
1. 找到對應的AppWindowToken對象;
2. 將對應的AppWindowToken對象的成員變量reportedVisible的值設置爲false,表示還沒有向ActivityManagerService服務報告過正在打開的Activity組件的可見性;
3. 將對應的AppWindowToken對象的成員變量inPendingTransaction的值設置爲false,表示正在打開的Activity組件不是處於等待執行切換操作的狀態了;
4. 將對應的AppWindowToken對象的成員變量animation的值設置爲null,因爲接下來要重新這個成員變量的值來描述正在打開的Activity組件的切換動畫;
5. 調用WindowManagerService類的成員函數setTokenVisibilityLocked將正在打開的Activity組件的可見性設置爲true,並且給正在打開的Activity組件設置一個切換動畫,這個切換動畫會保存在對應的AppWindowToken對象的成員變量animation中;
6. 調用對應的AppWindowToken對象的成員函數updateReportedVisibilityLocked向ActivityManagerService服務報告正在打開的Activity組件的可見性;
7. 將對應的AppWindowToken對象的成員變量waitingToShow的值設置爲false,表示正在打開的Activity組件的窗口不是處於等待顯示的狀態了;
8. 調用對應的AppWindowToken對象的成員函數showAllWindowsLocked通知SurfaceFlinger服務將正在打開的Activity組件的窗口設置爲可見的。
2)對於正在關閉的Activity組件,它們的切換動畫的設置過程如下所示:
1. 找到對應的AppWindowToken對象;
2. 將對應的AppWindowToken對象的成員變量inPendingTransaction的值設置爲false,表示正在關閉的Activity組件不是處於等待執行切換操作的狀態了;
3. 將對應的AppWindowToken對象的成員變量animation的值設置爲null,因爲接下來要重新這個成員變量的值來描述正在關閉的Activity組件的切換動畫;
4. 調用WindowManagerService類的成員函數setTokenVisibilityLocked將正在關閉的Activity組件的可見性設置爲true,並且給正在關閉的Activity組件設置一個切換動畫,這個切換動畫會保存在對應的AppWindowToken對象的成員變量animation中;
5. 調用對應的AppWindowToken對象的成員函數updateReportedVisibilityLocked向ActivityManagerService服務報告正在關閉的Activity組件的可見性;
6. 將對應的AppWindowToken對象的成員變量waitingToHide的值設置爲false,表示正在關閉的Activity組件的窗口不是處於等待隱藏的狀態了;
7. 將對應的AppWindowToken對象的成員變量allDrawn的值設置爲true,這樣就可以使得前面所設置的切換動畫得以執行。
給所有參與了切換操作的Activity組件都設置了一個切換動畫之後,接下來就可以將WindowManagerService類的成員變量mOpeningApps和mClosingApps所描述的兩個ArrayList清空了。
最後我們再來看下最後返回的changes,
changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT | WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
由於前面的操作已經導致那些正在打開的Activity組件的窗口由不可見變爲可見,即相當於是導致窗口堆棧發生了變化,這時候就需要重新計算各個窗口的大小,以便讓各個窗口對自己的UI元素進行重新佈局,這是通過將變量changes的值的PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT位設置爲1來實現的。
從前面的分析可以知道,一旦變量changes的值不等於0,WindowManagerService類的成員函數performLayoutAndPlaceSurfacesLockedInner開始的那個do…while循環就會重複執行,也就是會重複執行以下三個操作:
1. 計算各個窗口的大小,以便讓各個窗口可以對自己的UI元素進行佈局。
2. 計算各個窗口接下來要執行的動畫。
3. 執行各個窗口的動畫。
至此,我們就分析完成了Activity組件切換操作了。Activity組件的切換操作執行完成之後,參與了切換操作的Activity組件的窗口就會獲得一個切換動畫。一個窗口的切換動畫,與其本身所設置的進入動畫,以及其父窗口所設置的動畫,一起形成了窗口的顯示過程動畫。
AppWindowAnimator的showAllWindowsLocked函數
下面我們再來看看AppWindowAnimator的showAllWindowsLocked函數
- boolean showAllWindowsLocked() {
- boolean isAnimating = false;
- final int NW = mAllAppWinAnimators.size();
- for (int i=0; i<NW; i++) {
- WindowStateAnimator winAnimator = mAllAppWinAnimators.get(i);
- winAnimator.performShowLocked();
- isAnimating |= winAnimator.isAnimating();
- }
- return isAnimating;
- }
這個函數就遍歷這個AppWindowWinAnimators下所有的WindowStateAnimator,然後調用WindowStateAnimator的performShowLocked函數,這個函數就是顯示動畫的第一步,設置VSync信號,已經設置動畫。當有VSync信號過來時,就會開始播放動畫了。