前言
在Android系統(tǒng)中,很多應(yīng)用都需要根據(jù)具體情況來控制狀態(tài)欄和導(dǎo)航欄的顯示和隱藏,又或者將狀態(tài)欄透明,實現(xiàn)諸如沉浸式、全面屏燈效果,而要實現(xiàn)這些效果,都離不開SystemUIVisibility屬性。由于SystemUIVisibilityy屬性主要用來控制系統(tǒng)狀態(tài)欄和導(dǎo)航欄的行為,而狀態(tài)欄和導(dǎo)航欄都屬于SystemUI模塊的StatusBar,所以SystemUIVisibility屬性的消費者肯定包含StatusBar。另外當(dāng)狀態(tài)欄和導(dǎo)航欄發(fā)生變化的時候,窗口的布局一般也會跟著發(fā)生變化,這就意味著窗口管理者PhoneWindowManager肯定也要消費SystemUIVisibility屬性。本篇文章我們就來具體分析一下和這個屬性有關(guān)的代碼。
一、SystemUIVisibility屬性常見常見取值
1、為窗口設(shè)置SystemUIVisibility屬性的方式有兩種,一種是直接在窗口的WindowManager.LayoutParams對象的systemUiVisibility屬性上進(jìn)行設(shè)置,并通過WindowManager.updateViewLayout()方法使其生效。
frameworks/base/core/java/android/view/WindowManager.java
public interface WindowManager extends ViewManager {
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
//隱藏窗口的所有裝飾,比如狀態(tài)欄和導(dǎo)航欄
public static final int FLAG_FULLSCREEN = 0x00000400;
//控制窗口狀態(tài)欄、導(dǎo)航欄的顯示和隱藏
public int systemUiVisibility;
}
}
2、另一種是在一個已經(jīng)顯示在窗口上的控件中調(diào)用setSystemUiVisibility方法,傳入如下屬性。
frameworks/base/core/java/android/view/View.java
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
//隱藏導(dǎo)航欄
public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 0x00000002;
//隱藏狀態(tài)欄
public static final int SYSTEM_UI_FLAG_FULLSCREEN = 0x00000004;
}
這種方式最終影響的其實是窗口的WindowManager.LayoutParams對象的subtreeSystemUiVisibility屬性。
public interface WindowManager extends ViewManager {
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
//控制窗口狀態(tài)欄、導(dǎo)航欄的顯示和隱藏
public int subtreeSystemUiVisibility;
}
}
窗口的狀態(tài)欄導(dǎo)航欄顯示與否,最終其實是受以上兩個屬性共同影響的。接下來我們具體來分析一下View的setSystemUiVisibility方法是如何生效的。
二、View的setSystemUiVisibility方法調(diào)用流程
1、View的setSystemUiVisibility方法如下所示。
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
int mSystemUiVisibility;
protected ViewParent mParent;
public void setSystemUiVisibility(int visibility) {
if (visibility != mSystemUiVisibility) {
mSystemUiVisibility = visibility;//保存SystemUIVisibility屬性
if (mParent != null && mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
mParent.recomputeViewAttributes(this);//通知父控件子控件屬性發(fā)生了變化
}
}
}
}
setSystemUiVisibility方法首先將屬性賦值給mSystemUiVisibility,然后會調(diào)用父控件的recomputeViewAttributes方法,通知父控件子控件屬性發(fā)生了變化。ViewParent是一個接口,在Android中有兩個類實現(xiàn)了這個接口,它們分別是ViewGroup和ViewRootImpl。
2、ViewGroup和ViewRootImpl和recomputeViewAttributes方法相關(guān)的代碼如下所示。
frameworks/base/core/java/android/view/View.java
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
@Override
public void recomputeViewAttributes(View child) {
if (mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
ViewParent parent = mParent;
if (parent != null) parent.recomputeViewAttributes(this);
}
}
}
frameworks/base/core/java/android/view/ViewRootImpl.java
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
AttachedSurfaceControl {
final View.AttachInfo mAttachInfo;
@Override
public void recomputeViewAttributes(View child) {
checkThread();//檢測線程是不是UI線程
if (mView == child) {
mAttachInfo.mRecomputeGlobalAttributes = true;//標(biāo)記需要重新計算本地屬性
if (!mWillDrawSoon) {
scheduleTraversals();//進(jìn)一步調(diào)用scheduleTraversals方法。
}
}
}
}
結(jié)合Android 9.0系統(tǒng)源碼_窗口管理(二)WindowManager對窗口的管理過程,我們知道包括Activity的跟布局DecorView在內(nèi)的任何View,WindowManager在將它添加到窗口上的過程中,最終都會創(chuàng)建一個ViewRootImpl,并將View設(shè)置給ViewRootImpl,這樣根View的父類就變成了ViewRootImpl。這就意味著不管任何子View調(diào)用recomputeViewAttributes方法,最終所觸發(fā)的都是ViewRootImpl的recomputeViewAttributes,而ViewRootImpl會進(jìn)一步調(diào)用scheduleTraversals方法。
3、ViewRootImpl和scheduleTraversals方法相關(guān)的代碼如下所示。
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
AttachedSurfaceControl {
final Choreographer mChoreographer;//編舞者
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();//回調(diào)對象
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//等待系統(tǒng)垂直刷新同步信號,回調(diào)TraversalRunnable對象的run方法
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();//繼續(xù)執(zhí)行doTraversal
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
//執(zhí)行performTraversals
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
}
scheduleTraversals方法會為編舞者對象設(shè)置回調(diào),最終會等待系統(tǒng)垂直刷新同步信號,回調(diào)TraversalRunnable對象的run方法,該方法會調(diào)用doTraversal方法,然后進(jìn)一步調(diào)用performTraversals方法。
4、ViewRootImpl的performTraversals方法代碼邏輯非常多,這里只列出了我們需要關(guān)注的代碼。
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
AttachedSurfaceControl {
final IWindowSession mWindowSession;//和WMS通信的Binder對象,具體為Session對象
public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//窗口屬性
final View.AttachInfo mAttachInfo;//控件信息
private void performTraversals() {
final View host = mView;
if (host == null || !mAdded) {
return;
}
if (mWaitForBlastSyncComplete) {
mRequestedTraverseWhilePaused = true;
return;
}
mIsInTraversal = true;
mWillDrawSoon = true;
boolean windowSizeMayChange = false;
WindowManager.LayoutParams lp = mWindowAttributes;//將當(dāng)前窗口的最新屬性賦值給lp
int desiredWindowWidth;
int desiredWindowHeight;
final int viewVisibility = getHostVisibility();
final boolean viewVisibilityChanged = !mFirst
&& (mViewVisibility != viewVisibility || mNewSurfaceNeeded
|| mAppVisibilityChanged);
mAppVisibilityChanged = false;
final boolean viewUserVisibilityChanged = !mFirst &&
((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE));
WindowManager.LayoutParams params = null;
...代碼省略...
//收集mView的屬性,判斷是否需要更新params
if (collectViewAttributes()) {
params = lp;
}
...代碼省略...
//此方法最終會觸發(fā)WindowManagerService的relayoutWindow方法
relayoutWindow(params, viewVisibility, insetsPending);
...代碼省略...
//測量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...代碼省略...
//布局
performLayout(lp, mWidth, mHeight);
...代碼省略...
//繪制
performDraw();
...代碼省略...
}
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
...代碼省略...
//調(diào)用IWindowSession的relayout方法,該方法最終會觸發(fā)WindowManagerService的relayoutWindow方法
int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
mPendingMergedConfiguration, mSurface);
}
}
performTraversals首先調(diào)用collectViewAttributes方法收集所有子View的屬性,然后調(diào)用relayoutWindow方法,該方法最終會觸發(fā)WindowManagerService的relayoutWindow方法,然后回繼續(xù)調(diào)用觸發(fā)View測量的performMeasure方法,觸發(fā)View布局的performLayout方法和觸發(fā)View繪制的performDraw方法。
三、獲取最新的SystemUIVisibility屬性
1、ViewRootImpl的collectViewAttributes方法是一個很關(guān)鍵的方法,此方法會重新計算最新的SystemUIVisibility屬性。
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
AttachedSurfaceControl {
public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//窗口屬性
final View.AttachInfo mAttachInfo;//控件信息
private boolean collectViewAttributes() {
//判斷是否需要重新計算本地屬性
if (mAttachInfo.mRecomputeGlobalAttributes) {
//Log.i(mTag, "Computing view hierarchy attributes!");
mAttachInfo.mRecomputeGlobalAttributes = false;
boolean oldScreenOn = mAttachInfo.mKeepScreenOn;
mAttachInfo.mKeepScreenOn = false;
//清空已經(jīng)存在的SystemUiVisibility屬性
mAttachInfo.mSystemUiVisibility = 0;
mAttachInfo.mHasSystemUiListeners = false;
//重新獲取窗口視圖mView最新的的SystemUI屬性,賦值給mAttachInfo
mView.dispatchCollectViewAttributes(mAttachInfo, 0);
...代碼暫時省略...
}
return false;
}
}
collectViewAttributes首先會清空當(dāng)前窗口視圖mView已經(jīng)存在的SystemUiVisibility屬性,然后調(diào)用View的dispatchCollectViewAttributes方法重新獲取最新的的SystemUiVisibility屬性。
2、View的dispatchCollectViewAttributes方法如下所示。
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
int mSystemUiVisibility;
void dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility) {
performCollectViewAttributes(attachInfo, visibility);
}
void performCollectViewAttributes(AttachInfo attachInfo, int visibility) {
if ((visibility & VISIBILITY_MASK) == VISIBLE) {
if ((mViewFlags & KEEP_SCREEN_ON) == KEEP_SCREEN_ON) {
attachInfo.mKeepScreenOn = true;
}
//將最新的systemuivisiblity賦予AttachInfo的mSystemUiVisibility 屬性
attachInfo.mSystemUiVisibility |= mSystemUiVisibility;
//設(shè)置最新的SystemUiVisibility監(jiān)聽對象,如果不為空,則將AttachInfo的mHasSystemUiListeners屬性設(shè)置為true。
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnSystemUiVisibilityChangeListener != null) {
attachInfo.mHasSystemUiListeners = true;
}
}
}
}
3、接著看ViewRootImpl的collectViewAttributes方法。
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
AttachedSurfaceControl {
public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//窗口屬性
final View.AttachInfo mAttachInfo;//控件信息
private boolean collectViewAttributes() {
//判斷是否需要重新計算本地屬性
if (mAttachInfo.mRecomputeGlobalAttributes) {
...代碼省略...
//移除被禁用的SystemUiVisibility屬性
mAttachInfo.mSystemUiVisibility &= ~mAttachInfo.mDisabledSystemUiVisibility;
//讓params引用指向mWindowAttributes對象
WindowManager.LayoutParams params = mWindowAttributes;
mAttachInfo.mSystemUiVisibility |= getImpliedSystemUiVisibility(params);
if (mAttachInfo.mKeepScreenOn != oldScreenOn
|| mAttachInfo.mSystemUiVisibility != params.subtreeSystemUiVisibility
|| mAttachInfo.mHasSystemUiListeners != params.hasSystemUiListeners) {
applyKeepScreenOnFlag(params);
//將重新獲取的窗口視圖mView的SystemUiVisibility保存到窗口的LayoutParams屬性中
params.subtreeSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
params.hasSystemUiListeners = mAttachInfo.mHasSystemUiListeners;
//調(diào)用View的dispatchWindowSystemUiVisiblityChanged方法
mView.dispatchWindowSystemUiVisiblityChanged(mAttachInfo.mSystemUiVisibility);
return true;
}
}
return false;
}
}
在重新獲得mView的SystemUiVisibility屬性之后,首先會從該屬性中移除被禁用的SystemUiVisibility屬性,然后讓params引用指向mWindowAttributes對象,并將重新獲取的保存在mAttachInfo對象中的SystemUiVisibility屬性保存到當(dāng)前窗口的LayoutParams屬性中,最后會調(diào)用當(dāng)前View的dispatchWindowSystemUiVisiblityChanged方法。
4、View的dispatchWindowSystemUiVisiblityChanged方法如下所示。
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
@Deprecated
public void dispatchWindowSystemUiVisiblityChanged(int visible) {
onWindowSystemUiVisibilityChanged(visible);//調(diào)用onWindowSystemUiVisibilityChanged方法
}
public void onWindowSystemUiVisibilityChanged(int visible) {
//默認(rèn)為空實現(xiàn)
}
}
該方法會進(jìn)一步調(diào)用onWindowSystemUiVisibilityChanged方法,onWindowSystemUiVisibilityChanged方法默認(rèn)為空實現(xiàn),但是如果當(dāng)前mView為DecorView時則不同,DecorView實現(xiàn)了此方法。
四、DecorView更新狀態(tài)欄和導(dǎo)航欄背景顏色
1、DecorView的onWindowSystemUiVisibilityChanged方法如下所示。
frameworks/base/core/java/com/android/internal/policy/DecorView.java
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
@Override
public void onWindowSystemUiVisibilityChanged(int visible) {
//調(diào)用updateColorViews方法
updateColorViews(null /* insets */, true /* animate */);
}
}
onWindowSystemUiVisibilityChanged方法會調(diào)用一個updateColorViews這個關(guān)鍵方法。
2、updateColorViews會調(diào)用updateColorViewInt方法更新導(dǎo)航欄和狀態(tài)欄的背景顏色。
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
//獲取窗口的SystemUIVisibility屬性
int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
//判斷窗口類型是否是輸入法
final boolean isImeWindow = mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD;
//判斷窗口類型不是浮動窗口和輸入法,則讓SystemUIVisibility屬性生效
if (!mWindow.mIsFloating || isImeWindow) {
//獲取是否禁止窗口動畫的標(biāo)記
boolean disallowAnimate = !isLaidOut();
disallowAnimate |= ((mLastWindowFlags ^ attrs.flags)
& FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
mLastWindowFlags = attrs.flags;
if (insets != null) {
mLastTopInset = getColorViewTopInset(insets.getStableInsetTop(),
insets.getSystemWindowInsetTop());
mLastBottomInset = getColorViewBottomInset(insets.getStableInsetBottom(),
insets.getSystemWindowInsetBottom());
mLastRightInset = getColorViewRightInset(insets.getStableInsetRight(),
insets.getSystemWindowInsetRight());
mLastLeftInset = getColorViewRightInset(insets.getStableInsetLeft(),
insets.getSystemWindowInsetLeft());
// Don't animate if the presence of stable insets has changed, because that
// indicates that the window was either just added and received them for the
// first time, or the window size or position has changed.
boolean hasTopStableInset = insets.getStableInsetTop() != 0;
disallowAnimate |= (hasTopStableInset != mLastHasTopStableInset);
mLastHasTopStableInset = hasTopStableInset;
boolean hasBottomStableInset = insets.getStableInsetBottom() != 0;
disallowAnimate |= (hasBottomStableInset != mLastHasBottomStableInset);
mLastHasBottomStableInset = hasBottomStableInset;
boolean hasRightStableInset = insets.getStableInsetRight() != 0;
disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset);
mLastHasRightStableInset = hasRightStableInset;
boolean hasLeftStableInset = insets.getStableInsetLeft() != 0;
disallowAnimate |= (hasLeftStableInset != mLastHasLeftStableInset);
mLastHasLeftStableInset = hasLeftStableInset;
mLastShouldAlwaysConsumeNavBar = insets.shouldAlwaysConsumeNavBar();
}
boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset);
boolean navBarToLeftEdge = isNavBarToLeftEdge(mLastBottomInset, mLastLeftInset);
int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset, mLastLeftInset);
//更新導(dǎo)航欄顏色,mNavigationColorViewState為導(dǎo)航欄的相關(guān)篩選條件
updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
mWindow.mNavigationBarColor, mWindow.mNavigationBarDividerColor, navBarSize/*導(dǎo)航欄高度*/,
navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge,
0 /* sideInset */, animate && !disallowAnimate, false /* force */);
boolean statusBarNeedsRightInset = navBarToRightEdge
&& mNavigationColorViewState.present;
boolean statusBarNeedsLeftInset = navBarToLeftEdge
&& mNavigationColorViewState.present;
int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset
: statusBarNeedsLeftInset ? mLastLeftInset : 0;
//更新狀態(tài)欄顏色,mStatusColorViewState為狀態(tài)欄的相關(guān)篩選條件
updateColorViewInt(mStatusColorViewState, sysUiVisibility,
calculateStatusBarColor(), 0, mLastTopInset/*狀態(tài)欄高度*/,
false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset,
animate && !disallowAnimate,
mForceWindowDrawsStatusBarBackground);
}
...代碼省略...
}
}
關(guān)于DecorView更新狀態(tài)欄、導(dǎo)航欄背景顏色的具體過程,請參考Android 9.0系統(tǒng)源碼_SystemUI(八)PhoneWindow更新狀態(tài)欄和導(dǎo)航欄背景顏色的流程解析。
五、WindowManagerService的relayoutWindow方法
1、重新回到第二節(jié)第4步ViewRootImpl的performTraversals方法中。
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
AttachedSurfaceControl {
final IWindowSession mWindowSession;//和WMS通信的Binder對象,具體為Session對象
public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//窗口屬性
final View.AttachInfo mAttachInfo;//控件信息
private void performTraversals() {
...代碼省略...
//收集mView的屬性,判斷是否需要更新params
if (collectViewAttributes()) {
params = lp;
}
...代碼省略...
//此方法最終會觸發(fā)WindowManagerService的relayoutWindow方法
relayoutWindow(params, viewVisibility, insetsPending);
...代碼省略...
//測量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...代碼省略...
//布局
performLayout(lp, mWidth, mHeight);
...代碼省略...
//繪制
performDraw();
...代碼省略...
}
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
...代碼省略...
//調(diào)用IWindowSession的relayout方法,該方法最終會觸發(fā)WindowManagerService的relayoutWindow方法
int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
mPendingMergedConfiguration, mSurface);
}
}
在調(diào)用collectViewAttributes獲取最新的systemUIVisibiliy屬性之后,會調(diào)用relayoutWindow方法,該方法進(jìn)一步調(diào)用IWindowSession的relayout方法,IWindowSession的具體實現(xiàn)類為Session。
2、Session的relayout方法如下所示。
frameworks/base/services/core/java/com/android/server/wm/Session.java
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final WindowManagerService mService;
@Override
public int relayout(IWindow window, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
int res = mService.relayoutWindow(this, window, attrs,
requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
outActiveControls, outSurfaceSize);
return res;
}
}
relayout方法會進(jìn)一步調(diào)用WindowManagerService的relayoutWindow方法。
3、WindowManagerService的relayoutWindow方法如下所示。
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs/**窗口屬性**/,
int requestedWidth, int requestedHeight, int viewVisibility/**根View控件是否可見**/, int flags,
long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
Surface outSurface) {
int result = 0;
boolean configChanged;
//是否有狀態(tài)欄的使用權(quán)限
final boolean hasStatusBarPermission =
mContext.checkCallingOrSelfPermission(permission.STATUS_BAR)
== PackageManager.PERMISSION_GRANTED;
//是否有狀態(tài)欄服務(wù)的使用權(quán)限
final boolean hasStatusBarServicePermission =
mContext.checkCallingOrSelfPermission(permission.STATUS_BAR_SERVICE)
== PackageManager.PERMISSION_GRANTED;
long origId = Binder.clearCallingIdentity();
final int displayId;
synchronized(mWindowMap) {
//獲取當(dāng)前要操作的窗口對象
WindowState win = windowForClientLocked(session, client, false);
if (win == null) {
return 0;
}
//獲取窗口所屬的屏幕設(shè)備id
displayId = win.getDisplayId();
//窗口動畫
WindowStateAnimator winAnimator = win.mWinAnimator;
if (viewVisibility != View.GONE) {
win.setRequestedSize(requestedWidth, requestedHeight);
}
win.setFrameNumber(frameNumber);
int attrChanges = 0;
int flagChanges = 0;
if (attrs != null) {
//如果窗口屬性不為空,這里會對窗口的相關(guān)屬性進(jìn)行一次預(yù)處理
mPolicy.adjustWindowParamsLw(win, attrs, hasStatusBarServicePermission);
// if they don't have the permission, mask out the status bar bits
if (seq == win.mSeq) {
int systemUiVisibility = attrs.systemUiVisibility
| attrs.subtreeSystemUiVisibility;
if ((systemUiVisibility & DISABLE_MASK) != 0) {
if (!hasStatusBarPermission) {
systemUiVisibility &= ~DISABLE_MASK;
}
}
win.mSystemUiVisibility = systemUiVisibility;
}
if (win.mAttrs.type != attrs.type) {
throw new IllegalArgumentException(
"Window type can not be changed after the window is added.");
}
// Odd choice but less odd than embedding in copyFrom()
if ((attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY)
!= 0) {
attrs.x = win.mAttrs.x;
attrs.y = win.mAttrs.y;
attrs.width = win.mAttrs.width;
attrs.height = win.mAttrs.height;
}
//檢測窗口標(biāo)記和樣式是否發(fā)生了變化
flagChanges = win.mAttrs.flags ^= attrs.flags;
attrChanges = win.mAttrs.copyFrom(attrs);
if ((attrChanges & (WindowManager.LayoutParams.LAYOUT_CHANGED
| WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED)) != 0) {
win.mLayoutNeeded = true;
}
if (win.mAppToken != null && ((flagChanges & FLAG_SHOW_WHEN_LOCKED) != 0
|| (flagChanges & FLAG_DISMISS_KEYGUARD) != 0)) {
win.mAppToken.checkKeyguardFlagsChanged();
}
if (((attrChanges & LayoutParams.ACCESSIBILITY_TITLE_CHANGED) != 0)
&& (mAccessibilityController != null)
&& (win.getDisplayId() == DEFAULT_DISPLAY)) {
// No move or resize, but the controller checks for title changes as well
mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
if ((flagChanges & PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {
updateNonSystemOverlayWindowsVisibilityIfNeeded(
win, win.mWinAnimator.getShown());
}
}
...代碼省略...
if (focusMayChange) {
//System.out.println("Focus may change: " + win.mAttrs.getTitle());
if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/)) {
imMayMove = false;
}
//System.out.println("Relayout " + win + ": focus=" + mCurrentFocus);
}
...代碼省略...
}
}
relayoutWindow方法會調(diào)用一個關(guān)鍵方法updateFocusedWindowLocked。
4、WindowManagerService的updateFocusedWindowLocked方法如下所示。
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
//窗口管理策略的接口類WindowManagerPolicy(WMP),它用來定義一個窗口策略所要遵循的通用規(guī)范。
final WindowManagerPolicy mPolicy;
//根窗口
RootWindowContainer mRoot;
private WindowManagerService(Context context, InputManagerService inputManager,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
WindowManagerPolicy policy) {
...代碼省略...
mPolicy = policy;
mRoot = new RootWindowContainer(this);
...代碼省略...
}
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
//獲取當(dāng)前最新的焦點窗口
WindowState newFocus = mRoot.computeFocusedWindow();
if (mCurrentFocus != newFocus) {//如果窗口焦點發(fā)生了變化
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
// This check makes sure that we don't already have the focus
// change message pending.
mH.removeMessages(H.REPORT_FOCUS_CHANGE);
mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE);
// TODO(multidisplay): Focused windows on default display only.
final DisplayContent displayContent = getDefaultDisplayContentLocked();
boolean imWindowChanged = false;
if (mInputMethodWindow != null) {//如果輸入法窗口不為空
final WindowState prevTarget = mInputMethodTarget;
final WindowState newTarget =
displayContent.computeImeTarget(true /* updateImeTarget*/);
imWindowChanged = prevTarget != newTarget;
if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
&& mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
final int prevImeAnimLayer = mInputMethodWindow.mWinAnimator.mAnimLayer;
displayContent.assignWindowLayers(false /* setLayoutNeeded */);
imWindowChanged |=
prevImeAnimLayer != mInputMethodWindow.mWinAnimator.mAnimLayer;
}
}
if (imWindowChanged) {//輸入法窗口發(fā)生了變化
mWindowsChanged = true;
displayContent.setLayoutNeeded();
newFocus = mRoot.computeFocusedWindow();
}
if (DEBUG_FOCUS_LIGHT || localLOGV) Slog.v(TAG_WM, "Changing focus from " +
mCurrentFocus + " to " + newFocus + " Callers=" + Debug.getCallers(4));
final WindowState oldFocus = mCurrentFocus;
mCurrentFocus = newFocus;
mLosingFocus.remove(newFocus);
if (mCurrentFocus != null) {
mWinAddedSinceNullFocus.clear();
mWinRemovedSinceNullFocus.clear();
}
//調(diào)用WindowManagerPolicy的focusChangedLw方法
int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
if (imWindowChanged && oldFocus != mInputMethodWindow) {
// Focus of the input method window changed. Perform layout if needed.
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
displayContent.performLayout(true /*initial*/, updateInputWindows);
focusChanged &= ~FINISH_LAYOUT_REDO_LAYOUT;
} else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
// Client will do the layout, but we need to assign layers
// for handleNewWindowLocked() below.
displayContent.assignWindowLayers(false /* setLayoutNeeded */);
}
}
if ((focusChanged & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
// The change in focus caused us to need to do a layout. Okay.
displayContent.setLayoutNeeded();
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
displayContent.performLayout(true /*initial*/, updateInputWindows);
}
}
if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
// If we defer assigning layers, then the caller is responsible for
// doing this part.
mInputMonitor.setInputFocusLw(mCurrentFocus, updateInputWindows);
}
displayContent.adjustForImeIfNeeded();
// We may need to schedule some toast windows to be removed. The toasts for an app that
// does not have input focus are removed within a timeout to prevent apps to redress
// other apps' UI.
displayContent.scheduleToastWindowsTimeoutIfNeededLocked(oldFocus, newFocus);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
return true;
}
return false;
}
}
updateFocusedWindowLocked首先是獲取最新的焦點窗口,之后還會判斷當(dāng)前窗口焦點是否發(fā)生了變化,如果發(fā)生了變化,則會調(diào)用WindowManagerPolicy的focusChangedLw方法。
六、PhoneWindowManager的updateSystemUiVisibilityLw方法
1、WindowManagerPolicy是一個抽象接口。
frameworks/base/services/core/java/com/android/server/policy/WindowManagerPolicy.java
public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
/**
* 一個新的窗口持有了焦點
*/
public int focusChangedLw(WindowState lastFocus, WindowState newFocus);
}
2、結(jié)合Android 9.0系統(tǒng)源碼_窗口管理(一)WindowManagerService的啟動流程這篇文章我們可以知道,SystemServer在創(chuàng)建WindowManagerService對象的時候,將PhoneWindowManager對象實例賦值給了mPolicy。
frameworks/base/service/java/com/android/server/SystemServer.java
public final class SystemServer {
private void startOtherServices() {
...代碼省略...
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore, new PhoneWindowManager());
...代碼省略...
}
}
3、來看下PhoneWindowManager是如何實現(xiàn)focusChangedLw方法的。
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public int focusChangedLw(WindowState lastFocus, WindowState newFocus) {
mFocusedWindow = newFocus;
if ((updateSystemUiVisibilityLw()&SYSTEM_UI_CHANGING_LAYOUT) != 0) {
// If the navigation bar has been hidden or shown, we need to do another
// layout pass to update that window.
return FINISH_LAYOUT_REDO_LAYOUT;
}
return 0;
}
//更新窗口的SystemUiVisibility屬性參數(shù)
private int updateSystemUiVisibilityLw() {
//獲取當(dāng)前的焦點窗口
WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow
: mTopFullscreenOpaqueWindowState;
//如果不存在焦點窗口則直接返回
if (winCandidate == null) {
return 0;
}
if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) {
winCandidate = isStatusBarKeyguard() ? mStatusBar : mTopFullscreenOpaqueWindowState;
if (winCandidate == null) {
return 0;
}
}
final WindowState win = winCandidate;
if ((win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 && mKeyguardOccluded) {
return 0;
}
int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null)
& ~mResettingSystemUiFlags
& ~mForceClearedSystemUiFlags;
if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) {
tmpVisibility &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS);
}
//獲取和SystemUIVisibility相關(guān)的各種窗口參數(shù)
final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */,
mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
final int dockedVisibility = updateLightStatusBarLw(0 /* vis */,
mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
mWindowManagerFuncs.getStackBounds(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds);
mWindowManagerFuncs.getStackBounds(
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds);
final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
final int diff = visibility ^ mLastSystemUiFlags;
final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags;
final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags;
final boolean needsMenu = win.getNeedsMenuLw(mTopFullscreenOpaqueWindowState);
if (diff == 0 && fullscreenDiff == 0 && dockedDiff == 0 && mLastFocusNeedsMenu == needsMenu
&& mFocusedApp == win.getAppToken()
&& mLastNonDockedStackBounds.equals(mNonDockedStackBounds)
&& mLastDockedStackBounds.equals(mDockedStackBounds)) {
return 0;
}
mLastSystemUiFlags = visibility;
mLastFullscreenStackSysUiFlags = fullscreenVisibility;
mLastDockedStackSysUiFlags = dockedVisibility;
mLastFocusNeedsMenu = needsMenu;
mFocusedApp = win.getAppToken();
final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds);
final Rect dockedStackBounds = new Rect(mDockedStackBounds);
mHandler.post(new Runnable() {
@Override
public void run() {
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
if (statusbar != null) {
//最終會觸發(fā)狀態(tài)欄管理服務(wù)StatusBarManagerService的setSystemUiVisibility方法,
//通知狀態(tài)欄和底部欄進(jìn)行樣式調(diào)整
statusbar.setSystemUiVisibility(visibility, fullscreenVisibility,
dockedVisibility, 0xffffffff, fullscreenStackBounds,
dockedStackBounds, win.toString());
statusbar.topAppWindowChanged(needsMenu);
}
}
});
return diff;
}
}
PhoneWindowManager的focusChangedLw方法直接調(diào)用了updateSystemUiVisibilityLw方法,此方法會對窗口的SystemUiVisibility屬性做一些處理,最終調(diào)用狀態(tài)欄管理服務(wù)StatusBarManagerService的setSystemUiVisibility方法,通知狀態(tài)欄和底部欄進(jìn)行樣式調(diào)整。
4、updateSystemUiVisibilityLw方法中有用到對SystemUIVisibility屬性做處理的幾個關(guān)鍵方法:updateLightStatusBarLw、chooseNavigationColorWindowLw、updateLightNavigationBarLw和updateSystemBarsLw,這里一并貼出,有興趣的可以看下。
public class PhoneWindowManager implements WindowManagerPolicy {
private int updateLightStatusBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming) {
final boolean onKeyguard = isStatusBarKeyguard() && !mKeyguardOccluded;
final WindowState statusColorWin = onKeyguard ? mStatusBar : opaqueOrDimming;
if (statusColorWin != null && (statusColorWin == opaque || onKeyguard)) {
// If the top fullscreen-or-dimming window is also the top fullscreen, respect
// its light flag.
vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
vis |= PolicyControl.getSystemUiVisibility(statusColorWin, null)
& View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
} else if (statusColorWin != null && statusColorWin.isDimming()) {
// Otherwise if it's dimming, clear the light flag.
vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
}
return vis;
}
@VisibleForTesting
@Nullable
static WindowState chooseNavigationColorWindowLw(WindowState opaque,
WindowState opaqueOrDimming, WindowState imeWindow,
@NavigationBarPosition int navBarPosition) {
// If the IME window is visible and FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS is set, then IME
// window can be navigation color window.
final boolean imeWindowCanNavColorWindow = imeWindow != null
&& imeWindow.isVisibleLw()
&& navBarPosition == NAV_BAR_BOTTOM
&& (PolicyControl.getWindowFlags(imeWindow, null)
& WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
if (opaque != null && opaqueOrDimming == opaque) {
// If the top fullscreen-or-dimming window is also the top fullscreen, respect it
// unless IME window is also eligible, since currently the IME window is always show
// above the opaque fullscreen app window, regardless of the IME target window.
// TODO(b/31559891): Maybe we need to revisit this condition once b/31559891 is fixed.
return imeWindowCanNavColorWindow ? imeWindow : opaque;
}
if (opaqueOrDimming == null || !opaqueOrDimming.isDimming()) {
// No dimming window is involved. Determine the result only with the IME window.
return imeWindowCanNavColorWindow ? imeWindow : null;
}
if (!imeWindowCanNavColorWindow) {
// No IME window is involved. Determine the result only with opaqueOrDimming.
return opaqueOrDimming;
}
// The IME window and the dimming window are competing. Check if the dimming window can be
// IME target or not.
if (LayoutParams.mayUseInputMethod(PolicyControl.getWindowFlags(opaqueOrDimming, null))) {
// The IME window is above the dimming window.
return imeWindow;
} else {
// The dimming window is above the IME window.
return opaqueOrDimming;
}
}
@VisibleForTesting
static int updateLightNavigationBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming,
WindowState imeWindow, WindowState navColorWin) {
if (navColorWin != null) {
if (navColorWin == imeWindow || navColorWin == opaque) {
// Respect the light flag.
vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
vis |= PolicyControl.getSystemUiVisibility(navColorWin, null)
& View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
} else if (navColorWin == opaqueOrDimming && navColorWin.isDimming()) {
// Clear the light flag for dimming window.
vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
}
}
return vis;
}
private int updateSystemBarsLw(WindowState win, int oldVis, int vis) {
final boolean dockedStackVisible =
mWindowManagerInternal.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final boolean freeformStackVisible =
mWindowManagerInternal.isStackVisible(WINDOWING_MODE_FREEFORM);
final boolean resizing = mWindowManagerInternal.isDockedDividerResizing();
// We need to force system bars when the docked stack is visible, when the freeform stack
// is visible but also when we are resizing for the transitions when docked stack
// visibility changes.
mForceShowSystemBars = dockedStackVisible || freeformStackVisible || resizing;
final boolean forceOpaqueStatusBar = mForceShowSystemBars && !mForceStatusBarFromKeyguard;
// apply translucent bar vis flags
WindowState fullscreenTransWin = isStatusBarKeyguard() && !mKeyguardOccluded
? mStatusBar
: mTopFullscreenOpaqueWindowState;
vis = mStatusBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
vis = mNavigationBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
final int dockedVis = mStatusBarController.applyTranslucentFlagLw(
mTopDockedOpaqueWindowState, 0, 0);
final boolean fullscreenDrawsStatusBarBackground =
drawsStatusBarBackground(vis, mTopFullscreenOpaqueWindowState);
final boolean dockedDrawsStatusBarBackground =
drawsStatusBarBackground(dockedVis, mTopDockedOpaqueWindowState);
// prevent status bar interaction from clearing certain flags
int type = win.getAttrs().type;
boolean statusBarHasFocus = type == TYPE_STATUS_BAR;
if (statusBarHasFocus && !isStatusBarKeyguard()) {
int flags = View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_IMMERSIVE
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
if (mKeyguardOccluded) {
flags |= View.STATUS_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSLUCENT;
}
vis = (vis & ~flags) | (oldVis & flags);
}
if (fullscreenDrawsStatusBarBackground && dockedDrawsStatusBarBackground) {
vis |= View.STATUS_BAR_TRANSPARENT;
vis &= ~View.STATUS_BAR_TRANSLUCENT;
} else if ((!areTranslucentBarsAllowed() && fullscreenTransWin != mStatusBar)
|| forceOpaqueStatusBar) {
vis &= ~(View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT);
}
vis = configureNavBarOpacity(vis, dockedStackVisible, freeformStackVisible, resizing);
// update status bar
boolean immersiveSticky =
(vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
final boolean hideStatusBarWM =
mTopFullscreenOpaqueWindowState != null
&& (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null)
& WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
final boolean hideStatusBarSysui =
(vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
final boolean hideNavBarSysui =
(vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
final boolean transientStatusBarAllowed = mStatusBar != null
&& (statusBarHasFocus || (!mForceShowSystemBars
&& (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky))));
final boolean transientNavBarAllowed = mNavigationBar != null
&& !mForceShowSystemBars && hideNavBarSysui && immersiveSticky;
final long now = SystemClock.uptimeMillis();
final boolean pendingPanic = mPendingPanicGestureUptime != 0
&& now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION;
if (pendingPanic && hideNavBarSysui && !isStatusBarKeyguard() && mKeyguardDrawComplete) {
// The user performed the panic gesture recently, we're about to hide the bars,
// we're no longer on the Keyguard and the screen is ready. We can now request the bars.
mPendingPanicGestureUptime = 0;
mStatusBarController.showTransient();
if (!isNavBarEmpty(vis)) {
mNavigationBarController.showTransient();
}
}
final boolean denyTransientStatus = mStatusBarController.isTransientShowRequested()
&& !transientStatusBarAllowed && hideStatusBarSysui;
final boolean denyTransientNav = mNavigationBarController.isTransientShowRequested()
&& !transientNavBarAllowed;
if (denyTransientStatus || denyTransientNav || mForceShowSystemBars) {
// clear the clearable flags instead
clearClearableFlagsLw();
vis &= ~View.SYSTEM_UI_CLEARABLE_FLAGS;
}
final boolean immersive = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
immersiveSticky = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
final boolean navAllowedHidden = immersive || immersiveSticky;
if (hideNavBarSysui && !navAllowedHidden
&& getWindowLayerLw(win) > getWindowLayerFromTypeLw(TYPE_INPUT_CONSUMER)) {
// We can't hide the navbar from this window otherwise the input consumer would not get
// the input events.
vis = (vis & ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
}
vis = mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis);
// update navigation bar
boolean oldImmersiveMode = isImmersiveMode(oldVis);
boolean newImmersiveMode = isImmersiveMode(vis);
if (win != null && oldImmersiveMode != newImmersiveMode) {
final String pkg = win.getOwningPackage();
mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode,
isUserSetupComplete(), isNavBarEmpty(win.getSystemUiVisibility()));
}
vis = mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis);
final WindowState navColorWin = chooseNavigationColorWindowLw(
mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState,
mWindowManagerFuncs.getInputMethodWindowLw(), mNavigationBarPosition);
vis = updateLightNavigationBarLw(vis, mTopFullscreenOpaqueWindowState,
mTopFullscreenOpaqueOrDimmingWindowState,
mWindowManagerFuncs.getInputMethodWindowLw(), navColorWin);
return vis;
}
}
七、狀態(tài)欄管理服務(wù)StatusBarManagerService發(fā)送SystemUIVisibility屬性
1、在第六節(jié)第2步的最后,PhoneWindowManager先是調(diào)用getStatusBarManagerInternal方法獲取StatusBarManagerInternal的對象實例,然后再調(diào)用該對象的setSystemUiVisibility方法,PhoneWindowManager的getStatusBarManagerInternal方法如下所示。
public class PhoneWindowManager implements WindowManagerPolicy {
//獲取StatusBarManagerInternal對象實例
StatusBarManagerInternal getStatusBarManagerInternal() {
synchronized (mServiceAquireLock) {
if (mStatusBarManagerInternal == null) {
mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class);
}
return mStatusBarManagerInternal;
}
}
}
getStatusBarManagerInternal就是將存儲在LocalServices的StatusBarManagerInternal對象實例取出然后返回。
2、StatusBarManagerInternal是一個抽象接口。
frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
public interface StatusBarManagerInternal {
void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
Rect fullscreenBounds, Rect dockedBounds, String cause);
}
3、系統(tǒng)最早是在狀態(tài)欄管理服務(wù)StatusBarManagerService的構(gòu)造方法中將StatusBarManagerInternal的對象實例存儲到LocalServices中的。
frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
public class StatusBarManagerService extends IStatusBarService.Stub {
public StatusBarManagerService(Context context, WindowManagerService windowManager) {
mContext = context;
mWindowManager = windowManager;
//將mInternalService存儲到LocalServices中。
LocalServices.addService(StatusBarManagerInternal.class, mInternalService);
LocalServices.addService(GlobalActionsProvider.class, mGlobalActionsProvider);
}
private final StatusBarManagerInternal mInternalService = new StatusBarManagerInternal() {
private boolean mNotificationLightOn;
@Override
public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
int mask,
Rect fullscreenBounds, Rect dockedBounds, String cause) {
//進(jìn)一步調(diào)用StatusBarManagerService的setSystemUiVisibility方法。
StatusBarManagerService.this.setSystemUiVisibility(vis, fullscreenStackVis,
dockedStackVis, mask, fullscreenBounds, dockedBounds, cause);
}
}
private void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
Rect fullscreenBounds, Rect dockedBounds, String cause) {
// also allows calls from window manager which is in this process.
enforceStatusBarService();
if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")");
synchronized (mLock) {
//進(jìn)一步調(diào)用updateUiVisibilityLocked方法。
updateUiVisibilityLocked(vis, fullscreenStackVis, dockedStackVis, mask,
fullscreenBounds, dockedBounds);
disableLocked(
mCurrentUserId,
vis & StatusBarManager.DISABLE_MASK,
mSysUiVisToken,
cause, 1);
}
}
}
由以上代碼可知StatusBarManagerInternal的setSystemUiVisibility方法會進(jìn)一步調(diào)用StatusBarManagerService的setSystemUiVisibility方法,StatusBarManagerService的setSystemUiVisibility方法會繼續(xù)調(diào)用updateUiVisibilityLocked。
4、來看下StatusBarManagerService的updateUiVisibilityLocked方法。
public class StatusBarManagerService extends IStatusBarService.Stub {
private volatile IStatusBar mBar;
//設(shè)置監(jiān)聽
@Override
public void registerStatusBar(IStatusBar bar, List<String> iconSlots,
List<StatusBarIcon> iconList, int switches[], List<IBinder> binders,
Rect fullscreenStackBounds, Rect dockedStackBounds) {
enforceStatusBarService();
Slog.i(TAG, "registerStatusBar bar=" + bar);
mBar = bar;//為mBar賦值
...代碼省略...
}
}
private void updateUiVisibilityLocked(final int vis,
final int fullscreenStackVis, final int dockedStackVis, final int mask,
final Rect fullscreenBounds, final Rect dockedBounds) {
if (mSystemUiVisibility != vis
|| mFullscreenStackSysUiVisibility != fullscreenStackVis
|| mDockedStackSysUiVisibility != dockedStackVis
|| !mFullscreenStackBounds.equals(fullscreenBounds)
|| !mDockedStackBounds.equals(dockedBounds)) {
mSystemUiVisibility = vis;
mFullscreenStackSysUiVisibility = fullscreenStackVis;
mDockedStackSysUiVisibility = dockedStackVis;
mFullscreenStackBounds.set(fullscreenBounds);
mDockedStackBounds.set(dockedBounds);
mHandler.post(new Runnable() {
public void run() {
if (mBar != null) {//判斷mBar是否為空
try {
//調(diào)用IStatusBar的setSystemUiVisibility方法,觸發(fā)監(jiān)聽回調(diào)
mBar.setSystemUiVisibility(vis, fullscreenStackVis, dockedStackVis,
mask, fullscreenBounds, dockedBounds);
} catch (RemoteException ex) {
}
}
}
});
}
}
}
updateUiVisibilityLocked方法會調(diào)用類型為IStatusBar的屬性對象mBar的setSystemUiVisibility方法,mBar對象是在registerStatusBar方法中被賦值的,而且IStatusBar是一個aidl。
frameworks/base/core/java/com/android/internal/statusbar/IStatusBar.aidl
oneway interface IStatusBar
{
//通知SystemUIVisibility屬性發(fā)生了變化
void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
in Rect fullscreenBounds, in Rect dockedBounds, String packageName);
}
八、系統(tǒng)狀態(tài)欄StatusBar設(shè)置監(jiān)聽回調(diào),獲取最新的SystemUIVisibility屬性
1、我們在Android 9.0系統(tǒng)源碼_SystemUI(二)系統(tǒng)狀態(tài)欄導(dǎo)航欄的創(chuàng)建和添加這篇文章中有詳細(xì)介紹StatusBar創(chuàng)建狀態(tài)欄和導(dǎo)航欄的過程,這里我們只列出了和獲取SystemUIVisibility屬性相關(guān)的代碼。
frameworks/base/packages/SystemUI/src/com/android/systemui/SystemBars.java
public class StatusBar extends SystemUI implements DemoMode,
DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter {
protected CommandQueue mCommandQueue;
protected IStatusBarService mBarService;
@Override
public void start() {
...代碼省略
mBarService = IStatusBarService.Stub.asInterface(ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mCommandQueue = getComponent(CommandQueue.class);
//mCommandQueue將當(dāng)前StatusBar 對象添加為事件回調(diào)對象
mCommandQueue.addCallbacks(this);
//調(diào)用StatusBarManagerService的registerStatusBar方法,設(shè)置mCommandQueue為setSystemUiVisibility回調(diào)對象
mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
fullscreenStackBounds, dockedStackBounds);
...代碼省略
}
//StatusBarManagerService的setSystemUiVisibility方法最終會回調(diào)到這里。
@Override
public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
//至此StatusBar才終于成功收到了應(yīng)用所設(shè)置的SystemUiVisibility屬性,然后根據(jù)具體的屬性顯示對應(yīng)的樣式
..代碼省略...
}
}
2、StatusBar的start方法首先會為類型為IStatusBarService的屬性對象mBarService賦值,IStatusBarService是一個aidl。
frameworks/base/core/java/com/android/internal/statusbar/IStatusBarService.aidl
interface IStatusBarService
{
// You need the STATUS_BAR_SERVICE permission
void registerStatusBar(IStatusBar callbacks, out List<String> iconSlots,
out List<StatusBarIcon> iconList,
out int[] switches, out List<IBinder> binders, out Rect fullscreenStackBounds,
out Rect dockedStackBounds);
}
通過它我們可以調(diào)用到狀態(tài)欄管理服務(wù)StatusBarManagerService的registerStatusBar方法。StatusBar指定了類型為CommandQueue的屬性對象mCommandQueue為StatusBarManagerService的setSystemUiVisibility的回調(diào)對象,這樣StatusBarManagerService對象首先會回調(diào)CommandQueue的setSystemUiVisibility方法。
3、CommandQueue和setSystemUiVisibility方法相關(guān)的代碼如下所示。
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java文章來源:http://www.zghlxwxcb.cn/news/detail-665089.html
public class CommandQueue extends IStatusBar.Stub {
private static final int MSG_SET_SYSTEMUI_VISIBILITY = 6 << MSG_SHIFT;
private ArrayList<Callbacks> mCallbacks = new ArrayList<>();//回調(diào)對象集合
//回調(diào)對象
public interface Callbacks {
default void setSystemUiVisibility(int vis, int fullscreenStackVis,
int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
}
}
//添加回調(diào)對象到mCallbacks集合中
public void addCallbacks(Callbacks callbacks) {
mCallbacks.add(callbacks);
callbacks.disable(mDisable1, mDisable2, false /* animate */);
}
//StatusBarManagerService對象首先會回調(diào)此方法。
public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
synchronized (mLock) {
// Don't coalesce these, since it might have one time flags set such as
// STATUS_BAR_UNHIDE which might get lost.
SomeArgs args = SomeArgs.obtain();
args.argi1 = vis;
args.argi2 = fullscreenStackVis;
args.argi3 = dockedStackVis;
args.argi4 = mask;
args.arg1 = fullscreenStackBounds;
args.arg2 = dockedStackBounds;
//發(fā)送消息
mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, args).sendToTarget();
}
}
private final class H extends Handler {
private H(Looper l) {
super(l);
}
public void handleMessage(Message msg) {
final int what = msg.what & MSG_MASK;
switch (what) {
...代碼省略...
case MSG_SET_SYSTEMUI_VISIBILITY:
SomeArgs args = (SomeArgs) msg.obj;
//依次調(diào)用集合中對象的setSystemUiVisibility方法,因為StatusBar有調(diào)用addCallbacks方法,
//因此這里會觸發(fā)StatusBar的setSystemUiVisibility方法。
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).setSystemUiVisibility(args.argi1, args.argi2, args.argi3,
args.argi4, (Rect) args.arg1, (Rect) args.arg2);
}
args.recycle();
break;
...代碼省略...
}
}
}
}
CommandQueue的addCallbacks方法會將回調(diào)對象(例如前面的StatusBar)添加到mCallbacks集合中,當(dāng)CommandQueued的setSystemUiVisibility方法被回調(diào)的時候,會通過Handler發(fā)送消息,并在Handler的handleMessage方法的對應(yīng)消息分支中依次調(diào)用集合中回調(diào)對象的setSystemUiVisibility方法,因為StatusBar有調(diào)用addCallbacks方法將自己添加到回調(diào)集合中,因此這里會觸發(fā)StatusBar的setSystemUiVisibility方法,至此StatusBar才終于成功收到了應(yīng)用所設(shè)置的SystemUiVisibility屬性,然后根據(jù)具體的屬性顯示對應(yīng)的樣式。文章來源地址http://www.zghlxwxcb.cn/news/detail-665089.html
到了這里,關(guān)于Android 9系統(tǒng)源碼_SystemUI(十)SystemUIVisibility屬性的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!