<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                原文出處——>[Android窗口管理服務WindowManagerService切換Activity窗口(App Transition)的過程分析](http://blog.csdn.net/luoshengyang/article/details/8596449) 在Android系統中,同一時刻只有一個Activity組件是處于激活狀態的,因此,當ActivityManagerService服務激活了一個新的Activity組件時,它就需要通知WindowManagerService服務將該Activity組件的窗口顯示出來,這會涉及到將焦點和屏幕等資源從前一個激活的Activity組件切換到后一個激活的Activity組件的過程,本文就詳細分析這個過程。 Activity窗口的切換操作是在新激活的Activity組件的啟動過程進行的。具體來說,就是在前一個激活的Activity組件進入到Paused狀態并且新激活的Activity組件進之到Resumed 狀態之后,將前一個激活的Activity組件的窗口設置為不可見,以及將新激活的Activity組件的窗口設置為可見。整個切換過程是需要在ActivityManagerService服務和WindowManagerService服務的協作之下進行的,如圖1所示。 :-: ![](https://box.kancloud.cn/c1b69b9f44ab87810e08dfbe3b5a2e69_523x282.jpg) 圖1 Activity窗口的切換操作示意圖 WindowManagerService服務在執行Activity窗口的切換操作的時候,會給參與切換操作的Activity組件的設置一個動畫,以便可以向用戶展現一個Activity組件切換效果,從而提高用戶體驗。事實上,一個Activity窗口在由不可見狀態切換到可見狀態的過程中,除了會被設置一個Activity組件切換動畫之外,還有被設置一個窗口進入動畫,此外,如果該Activity窗口是附加在另外一個窗口之上的,并且這個被附加的窗口正在顯示一個動畫,那么這個動畫也同時會被設置給到該Activity窗口的顯示過程中去。本文主要是關注Activity窗口的切換操作,在接下來的一篇文章中分析窗口的動畫框架時,我們再詳細分析上述三種動畫是如何作用在窗口的顯示過程中的。 從前面Android應用程序啟動過程源代碼分析一文可以知道,ActivityManagerService服務在啟動一個Activity組件的過程中,會調用到ActivityStack類的成員函數startActivityLocked。ActivityStack類的成員函數startActivityLocked首先會給正在啟動的Activity組件準備一個切換操作,接著再調用其它的成員函數來通知前一個激活的Activity組件進入到Paused狀態。等到前一個激活的Activity組件進入到Paused狀態之后,ActivityManagerService服務就會檢查用來運行正在啟動的Activity組件的進程啟動起來了沒有。如果這個進程還沒有啟動,那么ActivityManagerService服務就會將該進程啟動起來,然后再調用ActivityStack類的成員函數realStartActivityLocked來將正在啟動的Activity組件加載起來,并且將它的狀態設置為Resumed,最后通知WindowManagerService服務執行前面所準備的切換操作。 接下來,我們就從ActivityStack類的成員函數startActivityLocked開始分析Activity窗口的切換過程,如圖2所示。 :-: ![](https://box.kancloud.cn/24990995bda26fa4a5ce5a003be4e534_715x839.jpg) 圖2 Activity窗口的切換過程 這個過程可以分為9個步驟,接下來我們就詳細分析每一個步驟。 **Step 1. ActivityStack.startActivityLocked** ~~~ public class ActivityStack { ...... private final void startActivityLocked(ActivityRecord r, boolean newTask, boolean doResume) { final int NH = mHistory.size(); int addPos = -1; if (!newTask) { // If starting in an existing task, find where that is... ...... } // Place a new activity at top of stack, so it is next to interact // with the user. if (addPos < 0) { addPos = NH; } ...... // Slot the activity into the history stack and proceed mHistory.add(addPos, r); ...... if (NH > 0) { ...... if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); ...... } else if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) { mService.mWindowManager.prepareAppTransition( WindowManagerPolicy.TRANSIT_TASK_OPEN); ...... } else { mService.mWindowManager.prepareAppTransition(newTask ? WindowManagerPolicy.TRANSIT_TASK_OPEN : WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN); ...... } ...... } ...... if (doResume) { resumeTopActivityLocked(null); } } ...... } ~~~ 這個函數定義在文件frameworks/base/services/java/com/android/server/am/ActivityStack.java中。 參數r描述的是正在啟動的Activity組件,ActivityStack類的成員函數startActivityLocked首先找到它在Activity組件堆棧中的位置addPos,即找到它在ActivityStack類的成員變量mHistory所描述的一個ArrayList中的位置,然后再將正在啟動的Activity組件保存在該位置中。 變量NH描述的是將參數r描述的是正在啟動的Activity組件保存在Activity組件堆棧前系統已經啟動了的Activity組件的個數。只有在這個變量的值大于0的情況下,系統才需要執行一個Activity組件切換操作。也就是說,如果參數r描述的是正在啟動的Activity組件是系統中第一個啟動的Activity組件,那么就不需要執行一個Activity組件切換操作了。 注意,即使參數r描述的是正在啟動的Activity組件不是系統中第一個啟動的Activity組件,那么系統也可能不需要執行一個Activity組件切換操作,因為用來啟動參數r所描述的一個Activity組件的一個Intent對象的成員函數getFlags返回的一個標志值的Intent.FLAG_ACTIVITY_NO_ANIMATION位可能會不等于0,這意味著正在啟動的Activity組件不需要顯示切換動畫。在這種情況下,ActivityManagerSerivce服務就會通知WindowManagerService服務不需要準備一個Activity組件切換操作,這是通過以WindowManagerPolicy.TRANSIT_NONE為參數來調用ActivityStack類的成員變量mService所指向的一個ActivityManagerService對象的成員變量mWindowManager所描述的一個WindowManagerService對象的成員函數prepareAppTransition來實現的。 另一方面,如果參數r描述的是正在啟動的Activity組件不是系統中第一個啟動的Activity組件,并且系統需要執行一個Activity組件切換操作,即需要WindowManagerService服務在顯示正在啟動的Activity組件的窗口時應用一個切換動畫,那么這個動畫的類型也是有講究的。具體來說,如果參數r描述的是Activity組件是需要在在一個新的任務中啟動的,即參數newTask的值等于true,那么切換動畫的類型就指定為WindowManagerPolicy.TRANSIT_TASK_OPEN,否則的話,切換動畫的類型就指定為WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN。 此外,如果用來啟動參數r所描述的一個Activity組件的一個Intent對象的成員函數getFlags返回的一個標志值的Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET位不等于0,那么也會將切換動畫的類型設置為WindowManagerPolicy.TRANSIT_TASK_OPEN。Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET這個標志位等于1意味著當參數r所描述的一個Activity組件所在的任務下次作為前臺任務來運行時,參數r所描述的Activity組件以及它上面的并且屬于同一個任務的其它Activity組件都會被結束掉,以使得位于參數r所描述的Activity組件的前面一個Activity組件可以顯示出來。例如,如果我們正在使用一個Email Activity組件來查看Email,這時候又需要啟動另外一個Pictrue Activity來查看該Email附件的一張圖片。這時候Email Activity和Pictrue Activity就是在同一個任務中的。突然間,我們因為其它原歷,需要按下Home鍵回到Launcher中去完成其它任務。完成其它任務之后,再點擊Launcher上面的Email Activity圖標來想重新瀏覽之前正在查看的Email,但是又不想看到的是該Email附件的那張圖片。在這種情況下,之前在啟動Pictrue Activity來查看Email的附件圖片時,就可以將用來啟動Pictrue Activity的一個Intent對象的標志值的Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET位設置為1。 無論如何,最終指定的切換動畫的類型都是通過調用ActivityStack類的成員變量mService所指向的一個ActivityManagerService對象的成員變量mWindowManager所描述的一個WindowManagerService對象的成員函數prepareAppTransition來通知WindowManagerService服務的。 ActivityStack類的成員函數startActivityLocked通知WindowManagerService服務準備好一個Activity組件切換操作之后,如果參數doResume的值等于true,那么它就會繼續調用另外一個成員函數resumeTopActivityLocked來繼續執行啟動參數r所描述的一個Activity組件的操作。 接下來,我們就首先分析WindowManagerService類的成員函數prepareAppTransition的實現,以便可以了解WindowManagerService服務是如何準備一個Activity組件切換操作的,然后再回過頭來分析ActivityStack類的成員函數resumeTopActivityLocked是如何繼續執行啟動參數r所描述的一個Activity組件的操作的。 **Step 2. WindowManagerService.prepareAppTransition** ~~~ public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... // State management of app transitions. When we are preparing for a // transition, mNextAppTransition will be the kind of transition to // perform or TRANSIT_NONE if we are not waiting. If we are waiting, // mOpeningApps and mClosingApps are the lists of tokens that will be // made visible or hidden at the next transition. int mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET; ...... boolean mAppTransitionReady = false; ...... boolean mAppTransitionTimeout = false; boolean mStartingIconInTransition = false; boolean mSkipAppTransitionAnimation = false; public void prepareAppTransition(int transit) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "prepareAppTransition()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { ...... if (!mDisplayFrozen && mPolicy.isScreenOn()) { if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET || mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) { mNextAppTransition = transit; } else if (transit == WindowManagerPolicy.TRANSIT_TASK_OPEN && mNextAppTransition == WindowManagerPolicy.TRANSIT_TASK_CLOSE) { // Opening a new task always supersedes a close for the anim. mNextAppTransition = transit; } else if (transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN && mNextAppTransition == WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE) { // Opening a new activity always supersedes a close for the anim. mNextAppTransition = transit; } mAppTransitionReady = false; mAppTransitionTimeout = false; mStartingIconInTransition = false; mSkipAppTransitionAnimation = false; mH.removeMessages(H.APP_TRANSITION_TIMEOUT); mH.sendMessageDelayed(mH.obtainMessage(H.APP_TRANSITION_TIMEOUT), 5000); } } } ...... } ~~~ 這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。 調用WindowManagerService類的成員函數prepareAppTransition來通知WindowManagerService服務準備一個Activity組件切換操作是需要具有android.Manifest.permission.MANAGE_APP_TOKENS,否則的話,WindowManagerService類的成員函數prepareAppTransition就會拋出一個類型為SecurityException的異常。 WindowManagerService類的成員變量mNextAppTransition描述的就是WindowManagerService服務接下來要執行一個Activity組件切換操作的類型,也就是要執行的一個Activity組件切換動畫的類型。WindowManagerService類的成員函數prepareAppTransition按照以下規則來設置WindowManagerService服務接下來要執行的Activity組件切換動畫的類型: 1. 當 WindowManagerService類的成員變量mNextAppTransition的值等于WindowManagerPolicy.TRANSIT_UNSET或者WindowManagerPolicy.TRANSIT_NONE的時候,就說明WindowManagerService服務接下來沒有Activity組件切換動畫等待執行的,這時候參數transit所描述的Activity組件切換動畫就可以作為WindowManagerService服務接下來要執行的Activity組件切換動畫。 2. 當WindowManagerService類的成員變量mNextAppTransition的值等于WindowManagerPolicy.TRANSIT_TASK_CLOSE,那么就說明WindowManagerService服務接下來要執行一個關閉Activity組件任務的切換動畫等待執行的。在這種情況下,如果參數transit所描述的是一個打開Activity組件任務的切換動畫,即它的值等于WindowManagerPolicy.TRANSIT_TASK_OPEN,那么就需要將WindowManagerService服務接下來要執行的Activity組件切換動畫為打開Activity組件任務類型的。這是因為打開Activity組件任務的切換動畫的優先級高于關閉Activity組件任務的切換動畫。 3. 當WindowManagerService類的成員變量mNextAppTransition的值等于WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE,那么就說明WindowManagerService服務接下來要執行一個關閉Activity組件的切換動畫等待執行的。在這種情況下,如果參數transit所描述的是一個打開Activity組件的切換動畫,即它的值等于WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN,那么就需要將WindowManagerService服務接下來要執行的Activity組件切換動畫為打開Activity組件類型的。這是因為打開Activity組件的切換動畫的優先級高于關閉Activity組件的切換動畫。 設置好WindowManagerService服務接下來要執行的Activity組件切換動畫的類型之后,WindowManagerService類的成員函數prepareAppTransition還會將其余四個成員變量mAppTransitionReady、mAppTransitionTimeout、mStartingIconInTransition和mSkipAppTransitionAnimation的值設置為false,其中: 1. mAppTransitionReady表示WindowManagerService服務可以開始執行一個Activity組件的切換動畫了沒有? 2. mAppTransitionTimeout表示WindowManagerService服務正在執行的Activity組件切換動畫是否已經超時? 3. mStartingIconInTransition表示WindowManagerService服務開始顯示正在啟動的Activity組件的啟動窗口了沒有? 4. mSkipAppTransitionAnimation表示WindowManagerService服務是否需要不執行Activity組件的切換動畫? 最后,WindowManagerService類的成員函數prepareAppTransition還會調用成員變量mH所描述的一個H對象的成員函數sendMessageDelayed來向WindowManagerService服務所運行在的線程發送一個類型為APP_TRANSITION_TIMEOUT的消息。這個消息將在5秒后被執行,是用來強制前面所設置的Activity組件切換動畫要在5秒之內執行完成的,否則的話,WindowManagerService服務就會認為該切換動畫執行超時了。 這一步執行完成之后,WindowManagerService服務接下來要執行的Activity組件切換操作或者說切換動畫就準備完成了。注意,這時候只是準備好Activity組件切換動畫,但是這個切換動畫還不能執行,要等到前一個激活的Activity組件進入到Paused狀態并且接下來正在啟動的Activity組件進入到Resumed狀態之后才能執行。 回到前面的Step 1中,即ActivityStack類的成員函數startActivityLocked,接下來它就會調用另外一個成員函數resumeTopActivityLocked來繼續啟動指定的Activity組件。從前面Android應用程序啟動過程源代碼分析一文可以知道,ActivityStack類的成員函數resumeTopActivityLocked以及接下來要調用的其它成員函數就是執行以下兩個操作: 1. 通知當前處于激活狀態的Activity組件所運行在的應用程序進程,它所運行的一個Activity組件要由Resumed狀態進入到Paused狀態了。 2. 檢查用來運行當前正在啟動的Activity組件的應用程序進程是否已經啟動起來了。如果已經啟動起來,那么就會直接通知該應用程序進程將正在啟動的Activity組件加載起來,否則的話,就會先將該應用程序進程啟動起來,然后再通知它將正在啟動的Activity組件加載起來。 在第2步中,通知相應的應用程序進程將正在啟動的Activity組件加載起來是通過調用ActivityStack類的成員函數realStartActivityLocked來實現的,接下來我們就繼續分析這個成員函數的實現,以便可以繼續了解Activity組件的切換操作的執行過程。 **Step 3. ActivityStack.realStartActivityLocked** ~~~ public class ActivityStack { ...... final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app, boolean andResume, boolean checkConfig) throws RemoteException { ...... mService.mWindowManager.setAppVisibility(r, true); ...... try { ...... app.thread.scheduleLaunchActivity(new Intent(r.intent), r, System.identityHashCode(r), r.info, r.icicle, results, newIntents, !andResume, mService.isNextTransitionForward()); ...... } catch (RemoteException e) { ...... } ...... if (andResume) { // As part of the process of launching, ActivityThread also performs // a resume. r.state = ActivityState.RESUMED; ...... mResumedActivity = r; ...... completeResumeLocked(r); ...... } ...... return true; } ...... } ~~~ 這個函數定義在文件frameworks/base/services/java/com/android/server/am/ActivityStack.java中。 ActivityStack類的成員函數realStartActivityLocked主要執行以下三個操作: 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類的成員函數completeResumeLocked來通知WindowManagerService服務執行在前面Step 2所準備好的Activity組件切換操作。 接下來,我們首先分析WindowManagerService類的成員函數setAppVisibility的實現,以便可以了解WindowManagerService服務是如何設置一個Activity組件的可見性的,接著再分析ActivityStack類的成員函數completeResumeLocked的實現,以便可以了解ActivityManagerService服務是如何通知WindowManagerService服務執行前面所準備好的一個Activity組件切換操作的。 **Step 4. WindowManagerService.setAppVisibility** ~~~ public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... public void setAppVisibility(IBinder token, boolean visible) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppVisibility()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } AppWindowToken wtoken; synchronized(mWindowMap) { wtoken = findAppWindowToken(token); ...... // If we are preparing an app transition, then delay changing // the visibility of this token until we execute that transition. if (!mDisplayFrozen && mPolicy.isScreenOn() && mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { // Already in requested state, don't do anything more. if (wtoken.hiddenRequested != visible) { return; } wtoken.hiddenRequested = !visible; ...... wtoken.setDummyAnimation(); mOpeningApps.remove(wtoken); mClosingApps.remove(wtoken); wtoken.waitingToShow = wtoken.waitingToHide = false; wtoken.inPendingTransaction = true; if (visible) { mOpeningApps.add(wtoken); wtoken.startingDisplayed = false; wtoken.startingMoved = false; // If the token is currently hidden (should be the // common case), then we need to set up to wait for // its windows to be ready. if (wtoken.hidden) { wtoken.allDrawn = false; wtoken.waitingToShow = true; if (wtoken.clientHidden) { // In the case where we are making an app visible // but holding off for a transition, we still need // to tell the client to make its windows visible so // they get drawn. Otherwise, we will wait on // performing the transition until all windows have // been drawn, they never will be, and we are sad. wtoken.clientHidden = false; wtoken.sendAppVisibilityToClients(); } } } else { mClosingApps.add(wtoken); // If the token is currently visible (should be the // common case), then set up to wait for it to be hidden. if (!wtoken.hidden) { wtoken.waitingToHide = true; } } return; } ...... setTokenVisibilityLocked(wtoken, null, visible, WindowManagerPolicy.TRANSIT_UNSET, true); wtoken.updateReportedVisibilityLocked(); ...... } } ...... } ~~~ 這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。 調用WindowManagerService類的成員函數setAppVisibility來設置Activity組件的可見性需要具有android.Manifest.permission.MANAGE_APP_TOKENS權限,否則的話,WindowManagerService類的成員函數setAppVisibility就會拋出一個類型為SecurityException的異常。 從前面Android窗口管理服務WindowManagerService對窗口的組織方式分析一文可以知道,每一個Activity組件在WindowManagerService服務內部都對應有一個AppWindowToken對象,用來描述該Activity組件的窗口在WindowManagerService服務中的狀態。因此,WindowManagerService類的成員函數setAppVisibility就先通過調用成員函數findAppWindowToken來找到與參數token所描述的一個Activity組件所對應的一個AppWindowToken對象wtoken,以便接下來可以設置它的狀態。 注意,WindowManagerService類的成員函數setAppVisibility在修改參數token所描述的一個Activity組件的可見性的時候,需要考慮WindowManagerService服務接下來是否需要執行一個Activity組件操作,即WindowManagerService類的成員變量mNextAppTransition的值是否等于WindowManagerPolicy.TRANSIT_UNSET。如果等于的話,那么參數token所描述的Activity組件就是正在等待執行切換操作的Activity組件,這時候修改的可見性就會復雜一些,否則的話,只要簡單地執行以下兩個操作即可: 1. 調用WindowManagerService類的成員函數setTokenVisibilityLocked來將參數token所描述的Activity組件的可見性設置為參數visible所描述的值; 2. 調用AppWindowToken對象wtoken的成員函數updateReportedVisibilityLocked來向ActivityManagerService服務報告參數token所描述的Activity組件的可見性。 我們假設WindowManagerService服務接下來需要執行一個Activity組件操作,即WindowManagerService類的成員變量mNextAppTransition的值等于WindowManagerPolicy.TRANSIT_UNSET,這時候還需要滿足三個額外的條件,即: 1. 屏幕當前不是處于凍結狀態,即WindowManagerService類的成員變量mDisplayFrozen的值等于false; 2. 屏幕當前是點亮的,即WindowManagerService類的成員變量mPolicy所指向的一個PhoneWindowManager對象的成員函數isScreenOn的返回值等于true; 3. 參數token所描述的一個Activity組件的可見性已經不等于所要設置的可見性,即前面所找到的AppWindowToken對象wtoken的成員變量hiddenRequested的值不等于參數visible的值。 那么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的實現。 **Step 5. ActivityStack.completeResumeLocked** ~~~ public class ActivityStack { ...... private final void completeResumeLocked(ActivityRecord next) { ...... // schedule an idle timeout in case the app doesn't do it for us. Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG); msg.obj = next; mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT); ...... if (mMainStack) { mService.setFocusedActivityLocked(next); } ...... ensureActivitiesVisibleLocked(null, 0); mService.mWindowManager.executeAppTransition(); ...... } ...... } ~~~ 這個函數定義在文件frameworks/base/services/java/com/android/server/am/ActivityStack.java中。 ActivityStack類的成員函數completeResumeLocked主要是執行以下三個操作: 1. 向ActivityManagerService服務所運行在的線程發送一個類型為IDLE_TIMEOUT_MSG的消息,這個消息將在IDLE_TIMEOUT毫秒后處理。這個類型為IDLE_TIMEOUT_MSG實際是用來監控WindowManagerService服務能否在IDLE_TIMEOUT毫秒之內,完成參數next所描述的Activity組件的切換操作,并且將它的窗口顯示出來。如果能夠到的話,WindowManagerService服務就會通知ActivityManagerService服務,然后ActivityManagerService服務就會執行一些清理工作,例如,將那些已經處于Stopped狀態的Activity組件清理掉。如果不能夠做到的話,那么ActivityStack類的成員函數completeResumeLocked也需要保證在IDLE_TIMEOUT毫秒之后,ActivityManagerService服務能夠執行上述的清理工作。 2. 如果當前正在處理的ActivityStack對象描述的是系統當前所使用的Activity組件堆棧,即ActivityStack類的成員變量mMainStack的值等于true,那么就會調用成員變量mService所指向的一個ActivityManagerService對象的成員函數setFocusedActivityLocked來將參數next所描述的Activity組件設置為系統當前獲得焦點的Activity組件。 3. 調用ActivityStack類的成員函數ensureActivitiesVisibleLocked來從上下到檢查哪些Activity組件是需要設置為可見的,哪些Activity組件是需要設置為不可見的。 4. 調用成員變量mService所指向的一個ActivityManagerService對象的成員變量mWindowManager所描述的一個WindowManagerService對象的成員函數executeAppTransition來通知WindowManagerService服務執行在前面Step 2所準備好的Activity組件切換操作。 我們主要關注第3點和第4點的操作,因此,接下來我們先分析ActivityStack類的成員函數ensureActivitiesVisibleLocked的實現,然后再分析WindowManagerService類的成員函數executeAppTransition的實現。 **Step 6. ActivityStack.ensureActivitiesVisibleLocked** ~~~ public class ActivityStack { ...... final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges) { ActivityRecord r = topRunningActivityLocked(null); if (r != null) { ensureActivitiesVisibleLocked(r, starting, null, configChanges); } } ...... } ~~~ 這個函數定義在文件frameworks/base/services/java/com/android/server/am/ActivityStack.java。 ActivityStack類有兩個重載版本的成員函數ensureActivitiesVisibleLocked,其中,兩個參數版本的成員函數是通過調用四個版本的成員函數來實現的,因此,接下來我們主要分析ActivityStack類四個參數版本的成員函數ensureActivitiesVisibleLocked的實現。 ActivityStack類四個參數版本的成員函數ensureActivitiesVisibleLocked主要就是從上下到檢查哪些Activity組件是需要設置為可見的,哪些Activity組件是需要設置為不可見的,我們分段來閱讀: ~~~ public class ActivityStack { ...... final void ensureActivitiesVisibleLocked(ActivityRecord top, ActivityRecord starting, String onlyThisProcess, int configChanges) { ...... // If the top activity is not fullscreen, then we need to // make sure any activities under it are now visible. final int count = mHistory.size(); int i = count-1; while (mHistory.get(i) != top) { i--; } ~~~ 參數top描述的是位于Activity組件堆棧頂端的Activity組件,而參數starting描述的是ActivityManagerService服務當前正在啟動的Activity組件。另外,參數onlyThisProcess描述的是是否只對運行在進程名稱等于它的Activity組件進行處理,如果它的值等于null,那么就表示對所有的Activity組件都進行處理。 這段代碼首先找到參數top所描述的一個Activity組件在Activity組件堆棧的位置i,以便接下來可以從這個位置開始向下檢查哪些Activity組件是需要設置為可見的,哪些Activity組件是需要設置為不可見的。 我們繼續往下閱讀代碼: ~~~ ActivityRecord r; boolean behindFullscreen = false; for (; i>=0; i--) { r = (ActivityRecord)mHistory.get(i); ...... if (r.finishing) { continue; } ...... if (r.app == null || r.app.thread == null) { if (onlyThisProcess == null || onlyThisProcess.equals(r.processName)) { // This activity needs to be visible, but isn't even // running... get it started, but don't resume it // at this point. ...... if (!r.visible) { ...... mService.mWindowManager.setAppVisibility(r, true); } if (r != starting) { startSpecificActivityLocked(r, false, false); } } } else if (r.visible) { // If this activity is already visible, then there is nothing // else to do here. ...... } else if (onlyThisProcess == null) { // This activity is not currently visible, but is running. // Tell it to become visible. r.visible = true; if (r.state != ActivityState.RESUMED && r != starting) { // If this activity is paused, tell it // to now show its window. ...... try { mService.mWindowManager.setAppVisibility(r, true); r.app.thread.scheduleWindowVisibility(r, true); ...... } catch (Exception e) { ...... } } } ...... if (r.fullscreen) { // At this point, nothing else needs to be shown ...... behindFullscreen = true; i--; break; } } ~~~ 這段碼的功能是從上到下找到第一個全屏顯示的Activity組件,并且將該Activity組件以及位于該Activity組件上面的其它Activity組件的可見性設置為true。對這些Activity組件的可見性設置邏輯如下所示: 1. 如果一個Activity組件處于正在結束的狀態,即用來描述該Activity組件的一個ActivityRecord對象r的成員變量finishing的值等于true,那么跳過對該Activity組件的處理。 2. 如果一個Activity組件所運行在的進程還沒有啟動起來,即用來描述該Activity組件的一個ActivityRecord對象r的成員變量app的值等于null,或者該成員變量app的值不等于null,但是它所指向的一個ProcessRecord對象的成員變量thread的值等于null,并且該Activity組件是需要處理的,即參數onlyThisProcess的值等于null,或者它的值不等于null,但是等于該Activity組件所運行在的進程的名稱,那么就會做兩個檢查。第一個檢查是看該Activity組件是否已經處于可見狀態,即ActivityRecord對象r的成員變量visible的值是否等于true。如果不等于的話,那么就會通知WindowManagerService服務將該Activity組件的可見性設置為true。第二個檢查是看該Activity組件是否就是參數sarting所描述的Activity組件。如果不是的話,那么就需要調用ActivityStack類的成員函數startSpecificActivityLocked來將該Activity組件所運行的進程啟動起來。 3. 如果一個Activity組件已經是可見的,即用來描述該Activity組件的一個ActivityRecord對象r的成員變量visible的值等于true,那么就什么也不用做。 4. 如果一個Activity組件是不可見的,即用來描述該Activity組件的一個ActivityRecord對象r的成員變量visible的值等于true,并且該Activity組件是需要處理的,即參數onlyThisProcess的值等于null,那么就需要做兩件事情。第一件事情就將ActivityRecord對象r的成員變量visible的值設置為true,以便表示該Activity組件是可見的。第二件事情是檢查該Activity組件是否是不處于Resumed狀態,并且它不是參數starting所描述的Activity組件。如果檢查通過的話,那么就會先通知WindowManagerService服務將該Activity組件的可見性設置為true,然后再向該Activity組件所運在的進程發送一個通知,該Activity組件可見變為可見了。注意,參數starting所描述的Activity組件在前面的Step 3中已經被設置為可見的了,所以這里不需要重復將它設置為可見。 這段代碼是如何知道一個Activity組件是不是全屏顯示的呢?這是通過檢查一個對應的ActivityRecord對象的成員變量fullscreen來判斷的。如果這個成員變量的值等于true,那么就說明對應的Activity組件是全屏顯示的。 我們繼續往下閱讀最后一段代碼: ~~~ // Now for any activities that aren't visible to the user, make // sure they no longer are keeping the screen frozen. while (i >= 0) { r = (ActivityRecord)mHistory.get(i); ...... if (!r.finishing) { if (behindFullscreen) { if (r.visible) { ...... r.visible = false; try { mService.mWindowManager.setAppVisibility(r, false); if ((r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED) && r.app != null && r.app.thread != null) { ...... r.app.thread.scheduleWindowVisibility(r, false); } } catch (Exception e) { ...... } } ...... } else if (r.fullscreen) { ...... behindFullscreen = true; } } i--; } } ...... } ~~~ 這段代碼是將在前面找到的第一個全屏顯示的Activity組件的下面的所有Activity組件的可見性設置為false。注意,只有那些當前不是處于正在結束狀態的、并且是處于可見狀態的Activity組件才需要處理,即那些對應的ActivityRecord對象的成員變量finishing和visible的值分別等于false和true的值Activity組件才進行處理。處理的邏輯如下所示: 1. 將對應的ActivityRecord對象的成員變量visible的值設置為false。 2. 通知WindowManagerService服務將該Activity組件的可見性設置為false。 3. 向相應的進程發送一個通知,該Activity組件可見變為不可見了。 這一步執行完成之后,回到前面的Step 5中,即ActivityStack類的成員函數completeResumeLocked中,接下來它就會繼續調用WindowManagerService類的成員函數executeAppTransition來執行在前面Step 2所準備好的Activity組件切換操作。 **Step 7. WindowManagerService.executeAppTransition** ~~~ public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... public void executeAppTransition() { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "executeAppTransition()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } synchronized(mWindowMap) { ...... if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { mAppTransitionReady = true; final long origId = Binder.clearCallingIdentity(); performLayoutAndPlaceSurfacesLocked(); Binder.restoreCallingIdentity(origId); } } } ...... } ~~~ 這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。 調用WindowManagerService類的成員函數executeAppTransition來執行之前準備好的Activity組件切換操作需要具有android.Manifest.permission.MANAGE_APP_TOKENS權限,否則的話,WindowManagerService類的成員函數setAppVisibility就會拋出一個類型為SecurityException的異常。 注意,只有當之前已經準備好了一個Activity組件切換操作的情況下,WindowManagerService類的成員函數executeAppTransition才可以調用另外一個成員函數performLayoutAndPlaceSurfacesLocked來執行該Activity組件切換操作。從前面的Step 2可以知道,如果WindowManagerService類的成員變量mNextAppTransition的值不等于WindowManagerPolicy.TRANSIT_UNSET,那么就說明之前已經準備好了一個Activity組件切換操作。 在調用WindowManagerService類的成員函數performLayoutAndPlaceSurfacesLocked來執行一個Activity組件切換操作之前,WindowManagerService類的成員函數executeAppTransition還會將成員變量mAppTransitionReady的值設置為true,以便可以表示WindowManagerService服務已經就準備就緒執行一個Activity組件切換操作了。 接下來,我們就繼續分析WindowManagerService類的成員函數performLayoutAndPlaceSurfacesLocked的實現,以便可以了解WindowManagerService服務是如何執行一個Activity組件切換操作的。 **Step 8. WindowManagerService.performLayoutAndPlaceSurfacesLocked** 這一步可以參考前面Android窗口管理服務WindowManagerService計算Activity窗口大小的過程分析一文的Step 5,它主要就是做三件事情: 1. 檢查是否有窗口資源等待回收。如果有的話,那么就調用WindowManagerService類的成員函數removeWindowInnerLocked來將它們從系統中移除,以便可以回收它們所占用的資源。 2. 調用WindowManagerService類的成員函數performLayoutAndPlaceSurfacesLockedInner來刷新系統UI,其中就包含了執行在前面Step 2中所準備的Activity組件切換操作。 3. 在第2步的執行過程中,如果又有一些窗口要被移除,那么又會繼續調用WindowManagerService類的成員函數removeWindowInnerLocked來將它們從系統中移除。移除完成之后,又會再次遞歸調用WindowManagerService類的成員函數performLayoutAndPlaceSurfacesLocked來刷新系統UI。 我們主要關注第2步,即WindowManagerService類的成員函數performLayoutAndPlaceSurfacesLockedInner的實現,以便可以了解Activity組件的切換過程。 **Step 9. WindowManagerService.performLayoutAndPlaceSurfacesLockedInner** ~~~ public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... private final void performLayoutAndPlaceSurfacesLockedInner( boolean recoveringMemory) { ...... Surface.openTransaction(); ...... try { ...... int repeats = 0; int changes = 0; do { repeats++; if (repeats > 6) { ...... break; } // 1. 計算各個窗口的大小,以便讓各個窗口可以對自己的UI元素進行布局 // FIRST LOOP: Perform a layout, if needed. if (repeats < 4) { changes = performLayoutLockedInner(); if (changes != 0) { continue; } } else { Slog.w(TAG, "Layout repeat skipped after too many iterations"); changes = 0; } // 2. 計算各個窗口接下來要執行的動畫 // Update animations of all applications, including those // associated with exiting/removed apps ...... // 3. 執行各個窗口的動畫 // SECOND LOOP: Execute animations and update visibility of windows. ...... // 4. 檢查當前是否需要執行Activity組件切換操作 // If we are ready to perform an app transition, check through // all of the app tokens to be shown and see if they are ready // to go. if (mAppTransitionReady) { ...... } ...... } while (changes != 0); // 5. 更新各個窗口的繪圖表面 // THIRD LOOP: Update the surfaces of all windows. ...... } catch (RuntimeException e) { ...... } ...... Surface.closeTransaction(); ...... // 6. 銷毀那些需要銷毀的繪圖表面 // Destroy the surface of any windows that are no longer visible. ...... // 7. 刪除那些正在退出的類型為WindowToken的窗口令牌 // Time to remove any exiting tokens? ...... // 8. 刪除那些正在退出的類型為AppWindowToken的窗口令牌 // Time to remove any exiting applications? ...... // 9. 檢查是否需要再一次刷新系統UI if (needRelayout) { requestAnimationLocked(0); } else if (animating) { requestAnimationLocked(currentTime+(1000/60)-SystemClock.uptimeMillis()); } ...... } ...... } ~~~ 這個函數定義在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。 WindowManagerService類的成員函數performLayoutAndPlaceSurfacesLockedInner是整個WindowManagerService服務的核心,它的主要任務就是刷新系統的UI,執行的操作包括: 1. 計算各個窗口的大小,以便讓各個窗口可以對自己的UI元素進行布局。這一步主要是通過調用WindowManagerService類的成員函數performLayoutLockedInner來實現的,具體可以參考前面Android窗口管理服務WindowManagerService計算Activity窗口大小的過程分析一文。 2. 計算各個窗口接下來要執行的動畫。 3. 執行各個窗口的動畫。 4. 檢查當前是否需要執行Activity組件切換操作,即檢查WindowManagerService類的成員變量mAppTransitionReady的值是否等于true。如果等于的話,那么接下來就會執行這個Activity組件切換操作,實際上就是給正在切換的Activity組件應用一個切換動畫。 5. 更新各個窗口的繪圖表面。 6. 銷毀那些需要銷毀的繪圖表面。 7. 刪除那些正在退出的類型為WindowToken的窗口令牌。 8. 刪除那些正在退出的類型為AppWindowToken的窗口令牌。 9. 檢查是否需要再一次刷新系統UI。前面的操作可能會導致窗口堆棧或者窗口布局發生變化,例如,前面第6步銷毀的繪圖表面可能是當前正在顯示的輸入法窗口或者壁紙窗口的繪圖表面,或者前面第3步表明窗口的動畫還沒有結束,這時候都會要求WindowManagerService服務馬上再次刷新系統UI,以便可以反映出窗口堆棧或者窗口布局的變化,例如,可以持續地將窗口的動畫顯示完畢。如果需要再一次刷新系統UI,那么有可能是需要馬上執行的,也有可能是過一會再執行的。如果是窗口堆棧或者窗口布局發生了變化的情況,那么變量needRelayout的值就會等于true,這時候就會以0為參數來調用WindowManagerService類的成員函數requestAnimationLocked,以便可以馬上再次刷新系統UI。如果是窗口的動畫還沒有結束,那么變量animating的值就會等于true,這時候就會以一個非0參數來調用WindowManagerService類的成員函數requestAnimationLocked,請求過一段時間后再刷新系統UI。這個非0參數的值大約就等于1000/60,其中,分子表示1000毫秒,也就是說,在1/60秒后再刷新系統UI,意思就是以60幀每秒(fps)的速度來顯示窗口的動畫。 注意,在上述9個操作中,第1步到第4步是放在一個do...while循環中執行的,這是因為第2步到第4步的操作會導致導致窗口堆棧或者窗口布局發生變化,例如,有些窗口本來是不可見的,現在變成可見了,這時候就會要求重新計算各個窗口的大小,以及讓各個窗口重新布局自己的UI元素,即重新執行第1步的操作。然而,這些操作不能無休止地執行,否則的話,系統的UI就會死在那里不動了,因此,這個do...while循環最多只能執行7次。此外,在這最多7次的循環中,只有前4次循環才會重新各個窗口的大小。這些都是為了盡早地結束上述的do...while的,以便系統的UI可以盡快地刷新出來。 另外一個還有一個地方是需要注意的,即第1步到第5步的操作是放在一個事務中執行,即在Surface.openTransaction和Surface.closeTransaction之間執行,這意味著當Surface.closeTransaction執行完成之后,WindowManagerService服務才會通知SurfaceFlinger服務將系統的UI渲染到幀緩沖區(FB)中去,也就是說,在Surface.closeTransaction執行之后,我們才會看到系統的新UI。實際上,前面第1步到第5步的操作都只是修改了各個窗口的繪制表面的狀態,這些修改都只是反映在圖形緩沖區中,而不是反映在幀緩沖區中的。 在本文中,我們主要關注第4步的操作,即與Activity組件切換相關的操作,我們分段來閱讀這部分代碼: ~~~ // If we are ready to perform an app transition, check through // all of the app tokens to be shown and see if they are ready // to go. if (mAppTransitionReady) { int NN = mOpeningApps.size(); boolean goodToGo = true; ...... if (!mDisplayFrozen && !mAppTransitionTimeout) { // If the display isn't frozen, wait to do anything until // all of the apps are ready. Otherwise just go because // we'll unfreeze the display when everyone is ready. for (i=0; i<NN && goodToGo; i++) { AppWindowToken wtoken = mOpeningApps.get(i); ...... if (!wtoken.allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) { goodToGo = false; } } } ~~~ 從前面的Step 7可以知道,只有當WindowManagerService類的成員變量mAppTransitionReady的值等于true的時候,才說明WindowManagerService服務可以執行一個Activity組件切換操作。不過在執行這個切換操作之前,要求所有正在打開的Activity組件的UI都已經繪制完畢。因為如果這些正在打開的Activity組件的UI還沒有繪制完成的話,就無法給它們應用一個切換動畫。 從前面的Step 4可以知道,系統當前所有正在打開的Activity組件都保存在WindowManagerService類的成員變量mOpeningApps所描述的一個ArrayList中,因此,這段代碼就通過遍歷這個ArrayList來檢查每一個正在打開的Activity組件的UI是否已經繪制完成,即檢查對應的AppWindowToken對象的成員變量allDraw的值是否不等于true。如果不等于true的話,就說明還沒有繪制完成。此外,還要求這些正在打開的Activity組件的啟動窗口已經顯示結束,或者已經轉移給其它的Activity組件,即要求查對應的AppWindowToken對象的成員變量startingDisplayed和startingMoved的值均等于true。只要其中的一個正在打開的Activity組件不能滿足上述條件,那么變量goodToGo的值就會等于false,表示這時候還不能執行Activity組件操作。 注意,上述檢查是在屏幕不是處于凍結狀態或者前面準備好的Activity組件切換操作尚示超時的情況下進行,即在WindowManagerService類的成員變量mDisplayFrozen和mAppTransitionTimeout的值均等于false的情況下進行的,這意味著: 1. 如果屏幕處于凍結狀態,就要馬上執行前面準備好的Activity組件切換操作,因為等到屏幕解凍的時候,上述的三個條件都會得到滿足。 2. 如果者前面準備好的Activity組件切換操作已經超時,那么就說明不需要再等待了,而應該立即執行該Activity組件切換操作。 我們假設上述檢查都能通過,即最終得到的變量goodToGo的值等于true,我們繼續往下閱讀代碼: ~~~ if (goodToGo) { ...... int transit = mNextAppTransition; if (mSkipAppTransitionAnimation) { transit = WindowManagerPolicy.TRANSIT_UNSET; } mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET; mAppTransitionReady = false; mAppTransitionRunning = true; mAppTransitionTimeout = false; mStartingIconInTransition = false; mSkipAppTransitionAnimation = false; mH.removeMessages(H.APP_TRANSITION_TIMEOUT); ~~~ 由于接下來就要執行Activity組件切換操作了,因此,這段代碼就先修改與WindowManagerService服務的Activity組件切換操作相關的狀態。 首先是將WindowManagerService類的成員變量mNextAppTransition的值保存在變量transit中,并且將該成員變量的值重置為WindowManagerPolicy.TRANSIT_UNSET,以便WindowManagerService服務接下來可以準備一個新的Activity組件切換操作。注意,如果WindowManagerService類的成員變量mSkipAppTransitionAnimation的值等于true,那么就意味著要跳過此次Activity組件切換操作,即將前面得到的變量transit的值設置為WindowManagerPolicy.TRANSIT_UNSET。一般來說,如果一個Activity組件還在顯示啟動窗口的過程中,又有另外一個Activity組件被啟動,并且這個Activity組件也要求顯示啟動窗口,那么當前正在顯示啟動窗口的Activity組件就會跳過之前為它所準備的切換操作,這是為了讓后面那個啟動的Activity組件盡快地顯示出來。 接著還會分別設置WindowManagerService類的以下五個成員變量的值,其中: 1. mAppTransitionReady的值被重置為false,表示WindowManagerService服務接下來可以接受執行下一個Activity組件切換操作; 2. mAppTransitionRunning的值被設置為true,表示WindowManagerService服務目前正處于顯示Activity組件切換動畫的過程中; 3. mAppTransitionTimeout的值被設置為false,這是由于前面準備好的Activity組件切換操作已經得到執行了,所以就不存在超時問題了,同時后面還會通過調用WindowManagerService類的成同變量mH所指向一個H對象的成員函數removeMessages來刪除之前發送到WindowManagerService服務所運行在的線程的消息隊列中的一個類型為APP_TRANSITION_TIMEOUT的消息; 4. mStartingIconInTransition的值被重置為false,因為此時那些正在打開的Activity組件的啟動窗口都已經結束顯示; 5. mSkipAppTransitionAnimation的值被重置為false,因為前面已經對跳過切換動畫的情況進行過處理了。 我們繼續向下閱讀代碼: ~~~ // If there are applications waiting to come to the // top of the stack, now is the time to move their windows. // (Note that we don't do apps going to the bottom // here -- we want to keep their windows in the old // Z-order until the animation completes.) if (mToTopApps.size() > 0) { NN = mAppTokens.size(); for (i=0; i<NN; i++) { AppWindowToken wtoken = mAppTokens.get(i); if (wtoken.sendingToTop) { wtoken.sendingToTop = false; moveAppWindowsLocked(wtoken, NN, false); } } mToTopApps.clear(); } ~~~ 這段代碼對那些被請求移動至窗口堆棧頂端的Activity組件進行處理。這些被請求移動至窗口堆棧頂端的Activity組件會被保存在WindowManagerService類的成員變量mToTopApps所描述的一個ArrayList中。同時,用來描述這些被請求移動至窗口堆棧頂端的Activity組件的AppWindowToken對象的成員變量sendingToTop的值也會等于true。對于這些Activity組件,是通過調用WindowManagerService類的成員函數moveAppWindowsLocked來將它們移動到窗口堆棧頂端的。注意,在移動之前,還會將對應的AppWindowToken對象的成員變量sendingToTop的值設置為false。 除了可以請求WindowManagerService服務將指定的Activity組件移動到窗口堆棧頂端之外,還可以請求WindowManagerService服務將指定的Activity組件移動到窗口堆棧底端。這些被請求移動至窗口堆棧底端的Activity組件是保存在WindowManagerService類的成員變量mToBottomApps所描述的一個ArrayList中。不過,這些Activity組件要等到切換動畫顯示完成之后,才會被移動至窗口堆棧底端。 將那些被請求移動至窗口堆棧頂端的Activity組件都移動到窗口堆棧頂端之后,就可以將WindowManagerService類的成員變量mToTopApps所描述的一個ArrayList清空了。 我們繼續往下閱讀代碼: ~~~ WindowState oldWallpaper = mWallpaperTarget; adjustWallpaperWindowsLocked(); ...... // The top-most window will supply the layout params, // and we will determine it below. LayoutParams animLp = null; AppWindowToken animToken = null; int bestAnimLayer = -1; ...... int foundWallpapers = 0; // Do a first pass through the tokens for two // things: // (1) Determine if both the closing and opening // app token sets are wallpaper targets, in which // case special animations are needed // (since the wallpaper needs to stay static // behind them). // (2) Find the layout params of the top-most // application window in the tokens, which is // what will control the animation theme. 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; } } } } ~~~ 這段代碼用來檢查那些參與切換操作的Activity組件的窗口是否與壁紙窗口有關。如果有關的話,接下來要執行的切換動畫就需要修改為與壁紙相關的類型。 要檢查那些參與切換操作的Activity組件的窗口是否與壁紙窗口有關,首先需要確保壁紙窗口目前是位于那些需要顯示壁紙的窗口的下面的,這是通過調用WindowManagerService類的成員函數adjustWallpaperWindowsLocked來實現的。在調整壁紙窗口在窗口堆棧的位置之前,會首先將壁紙窗口的當前目標窗口保存在變量oldWallpaper中,這是因為接下來要執行的切換動畫的類型與壁紙窗口當前是否具有目標窗口有關。這一點我們后面再分析。 參與切換操作的Activity組件可以劃分為兩類,一類是需要打開的,一類是需要關閉的,它們分別保存在WindowManagerService類的成員變量mOpeningApps和mClosingApps所描述的ArrayList中。因此,通過檢查保存在這兩個ArrayList中的Activity組件的窗口是否是需要顯示壁紙的,就可以知道那些參與切換操作的Activity組件的窗口是否與壁紙窗口有關。 從前面Android窗口管理服務WindowManagerService對壁紙窗口(Wallpaper Window)的管理分析一文可以知道,在調整壁紙窗口在窗口堆棧的位置的時候,如果剛好碰到系統在執行兩個Activity組件的切換操作,并且這兩個Activity組件都需要顯示壁紙,那么Z軸位置較低的窗口就會保存在WindowManagerService類的成員變量mLowerWallpaperTarget中,而Z軸位置較高的窗口就會保存在WindowManagerService類的成員變量mUpperWallpaperTarget中。因此,這段代碼就可以通過一個for循環來檢查保存在WindowManagerService類的成員變量mOpeningApps和mClosingApps中那些AppWindowToken對象,如果它們剛好被WindowManagerService類的成員變量mLowerWallpaperTarget或者mUpperWallpaperTarget所描述的WindowState對象的成員變量mAppToken所引用,那么就說明那些參與切換操作的Activity組件的窗口是與壁紙窗口有關的。 最終得到的變量foundWallpapers的值就反映了那些參與切換操作的Activity組件的窗口是否與壁紙窗口有關,其中: 1. 變量foundWallpapers的值等于0表示與壁紙窗口無關; 2. 變量foundWallpapers的值等于1表示只有那些需要關閉的Activity組件與壁紙窗口無關; 3. 變量foundWallpapers的值等于2表示只有那些需要打開的Activity組件與壁紙窗口無關; 4. 變量foundWallpapers的值等于2表示需要打開和關閉的Activity組件均與壁紙窗口無關。 這段代碼的for循環除了用來檢查那些參與切換操作的Activity組件的窗口是否與壁紙窗口有關之外,還有另外一個重要的任務,那就是找到用來創建Activity組件切換動畫的參數。用來創建Activity組件切換動畫的參數是保存在一個類型為WindowManager.LayoutParams的對象中的,而這個類型為WindowManager.LayoutParams的對象是需要來自那些參與切換操作的Activity組件的窗口的。 我們知道,每一個窗口都具有一個WindowManager.LayoutParams對象,用來描述它的屬性,因此,這段代碼的for循環就是要從參與切換操作的Activity組件的窗口的WindowManager.LayoutParams對象中挑選出一個來創建切換動畫,然后再將這個切換動畫應用到每一個參與切換操作的Activity組件的窗口中去。這個被挑選中的窗口應用具有以下屬性: 1. 它所屬的Activity組件是全屏顯示的,即用來描述該Activity組件的AppWindowToken對象的成員變量appFullscreen的值等于true。 2. 它是它所屬的Activity組件的主窗口。一個Activity組件的主窗口可以通過調用對應的AppWindowToken對象的成員函數findMainWindow來獲得。 3. 它是所有候選窗口中Z軸位置最高的,即用來描述它的一個WindowState對象的成員變量mLayer的值是最大的,或者它是一個兼容窗口,即用來描述它的一個WindowState對象的成員變量mAttrs所指向的一個WindowManager.LayoutParams對象的成員變量flags的值的FLAG_COMPATIBLE_WINDOW位等于1。 一旦找到了這樣的窗口,這段代碼中的for循環就會將用來描述它的屬性的一個WindowManager.LayoutParams對象保存在變量animLp中,并且會將用來描述它所屬的Activity組件的一個AppWindowToken對象保存在變量animToken中。 我們繼續往下閱讀代碼: ~~~ if (foundWallpapers == 3) { ...... 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; } ...... } else if (oldWallpaper != null) { // We are transitioning from an activity with // a wallpaper to one without. transit = WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE; ...... } else if (mWallpaperTarget != null) { // We are transitioning from an activity without // a wallpaper to now showing the wallpaper transit = WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN; ...... } if ((transit&WindowManagerPolicy.TRANSIT_ENTER_MASK) != 0) { mLastEnterAnimToken = animToken; mLastEnterAnimParams = animLp; } else if (mLastEnterAnimParams != null) { animLp = mLastEnterAnimParams; mLastEnterAnimToken = null; mLastEnterAnimParams = null; } // If all closing windows are obscured, then there is // no need to do an animation. This is the case, for // example, when this transition is being done behind // the lock screen. if (!mPolicy.allowAppAnimationsLw()) { animLp = null; } ~~~ 這段代碼用來最終確定接下來要設置的切換動畫的類型。 如果變量foundWallpapers的值等于3,那么就說明正在打開和正在關閉的Activity組件的窗口均與壁紙窗口有關,這時候就會按照以下規則來修改之前所設置的切換動畫的類型: 1. 如果之前所設置的切換動畫是與打開操作相關的,即變量transit的值等于WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN、WindowManagerPolicy.TRANSIT_TASK_OPEN或者WindowManagerPolicy.TRANSIT_TASK_TO_FRONT,那么就會將實際的切換動畫的類型設置為WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN。這種類型的切換動畫是與打開壁紙窗口相關的。 2. 如果之前所設置的切換動畫是與關閉操作相關的,即變量transit的值等于WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE、WindowManagerPolicy.TRANSIT_TASK_CLOSE或者WindowManagerPolicy.TRANSIT_TASK_TO_BACK,那么就會將實際的切換動畫的類型設置為WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_CLOSE。這種類型的切換動畫是與關閉壁紙窗口相關的。 如果變量foundWallpapers的值不等于3,但是變量oldWallpaper的值不等于null,那么就說明當前正在執行的Activity組件切換操作是從一個需要顯示壁紙的Activity組件切換到一個不需要顯示壁紙的Activity組件上去,這時候就會將實際的切換動畫的類型設置為WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE。這種類型的切換動畫是與關閉壁紙窗口相關的。 如果變量foundWallpapers的值不等于3,并且變量oldWallpaper的值等于null,但是WindowManagerService類的成員變量mWallpaperTarget的值不等于null,那么就說明當前正在執行的Activity組件切換操作是從一個不需要顯示壁紙的Activity組件切換到一個需要顯示壁紙的Activity組件上去,這時候就會將實際的切換動畫的類型設置為WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN。這種類型的切換動畫是與打開壁紙窗口相關的。 最終得到的切換動畫的類型就保存在變量transit中。從上面的邏輯可以知道,最終得到的切換動畫的類型可能是與關閉操作相關的,但是我們需要的是一個與打開操作相關的切換動畫。因此,如果最終得到的切換動畫的類型是與關閉操作相關的,那么我們就使用上一次使用的Activity組件切換動畫。用來創建上一次使用的Activity組件切換動畫的WindowManager.LayoutParams對象和AppWindowToken分別保存在WindowManagerService類的成員變量mLastEnterAnimParams和mLastEnterAnimToken中。 通過檢查變量transit的值的TRANSIT_ENTER_MASK位是否不等于0,就可以知道最終得到的切換動畫的類型是否是與打開操作相關的。如果是與打開操作相關的,那么就會將前面獲得的變量animToken和animLp的值保存在WindowManagerService類的成員變量mLastEnterAnimToken和mLastEnterAnimParams中,以便以后可以重復使用。如果是與關閉操作相關的,那么就會將WindowManagerService類的成員變量mLastEnterAnimParams的值保存在變量animLp中,以及將WindowManagerService類的成員變量mLastEnterAnimToken和mLastEnterAnimParams重置為null,以便表示上一次使用的Activity組件切換動畫不是與打開操作相關的。 此時,如果我們所獲得的變量animLp的值不等于null,那么它所指向的一個WindowManager.LayoutParams對象就可以用來創建一個Activity組件切換動畫,但是如果窗口管理策略類表明此時不需要顯示Activity組件切換動畫,即WindowManagerService類的成員變量mPolicy所指向的一個PhoneWindowManager對象的成員函數allowAppAnimationsLw的返回值等于false,那么前面得的變量animLp的值就會被重置為null。當正在執行的Activity組件切換操作是發生在鎖屏窗口的后面時,就會出現這種情況。 我們繼續往下閱讀代碼: ~~~ NN = mOpeningApps.size(); for (i=0; i<NN; i++) { AppWindowToken wtoken = mOpeningApps.get(i); ...... wtoken.reportedVisible = false; wtoken.inPendingTransaction = false; wtoken.animation = null; setTokenVisibilityLocked(wtoken, animLp, true, transit, false); wtoken.updateReportedVisibilityLocked(); wtoken.waitingToShow = false; wtoken.showAllWindowsLocked(); } NN = mClosingApps.size(); for (i=0; i<NN; i++) { AppWindowToken wtoken = mClosingApps.get(i); ...... wtoken.inPendingTransaction = false; wtoken.animation = null; setTokenVisibilityLocked(wtoken, animLp, false, transit, false); wtoken.updateReportedVisibilityLocked(); wtoken.waitingToHide = false; // Force the allDrawn flag, because we want to start // this guy's animations regardless of whether it's // gotten drawn. wtoken.allDrawn = true; } ...... mOpeningApps.clear(); mClosingApps.clear(); ~~~ 這段代碼將給所有參與了切換操作的Activity組件設置一個切換動畫,而這個動畫就來自前面所獲得的變量animLp所指向的一個WindowManager.LayoutParams對象。由于參與了切換操作的Activity組件可以劃分為兩類,即一類是正在打開的,一類是正在打閉的,因此,我們就分別討論這兩種類型的Activity組件的切換動畫的設置過程。 對于正在打開的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組件的窗口設置為可見的。 對于正在關閉的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清空了。 我們繼續向下閱讀最后一段代碼: ~~~ // This has changed the visibility of windows, so perform // a new layout to get them all up-to-date. changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT; ...... } ~~~ 由于前面的操作已經導致那些正在打開的Activity組件的窗口由不可見變為可見,即相當于是導致窗口堆棧發生了變化,這時候就需要重新計算各個窗口的大小,以便讓各個窗口對自己的UI元素進行重新布局,這是通過將變量changes的值的PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT位設置為1來實現的。 從前面的分析可以知道,一旦變量changes的值不等于0,WindowManagerService類的成員函數performLayoutAndPlaceSurfacesLockedInner開始的那個do...while循環就會重復執行,也就是會重復執行以下三個操作: 1. 計算各個窗口的大小,以便讓各個窗口可以對自己的UI元素進行布局。 2. 計算各個窗口接下來要執行的動畫。 3. 執行各個窗口的動畫。 至此,我們就分析完成了Activity組件切換操作了。Activity組件的切換操作執行完成之后,參與了切換操作的Activity組件的窗口就會獲得一個切換動畫。一個窗口的切換動畫,與其本身所設置的進入動畫,以及其父窗口所設置的動畫,一起形成了窗口的顯示過程動畫。在接下來的一篇文章中,我們就繼續分析窗口的顯示過程動畫是如何執行的,敬請關注!
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看