赞
踩
在Android系统中,很多应用都需要根据具体情况来控制状态栏和导航栏的显示和隐藏,又或者将状态栏透明,实现诸如沉浸式、全面屏灯效果,而要实现这些效果,都离不开SystemUIVisibility属性。由于SystemUIVisibilityy属性主要用来控制系统状态栏和导航栏的行为,而状态栏和导航栏都属于SystemUI模块的StatusBar,所以SystemUIVisibility属性的消费者肯定包含StatusBar。另外当状态栏和导航栏发生变化的时候,窗口的布局一般也会跟着发生变化,这就意味着窗口管理者PhoneWindowManager肯定也要消费SystemUIVisibility属性。本篇文章我们就来具体分析一下和这个属性有关的代码。
1、为窗口设置SystemUIVisibility属性的方式有两种,一种是直接在窗口的WindowManager.LayoutParams对象的systemUiVisibility属性上进行设置,并通过WindowManager.updateViewLayout()方法使其生效。
frameworks/base/core/java/android/view/WindowManager.java
public interface WindowManager extends ViewManager {
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
//隐藏窗口的所有装饰,比如状态栏和导航栏
public static final int FLAG_FULLSCREEN = 0x00000400;
//控制窗口状态栏、导航栏的显示和隐藏
public int systemUiVisibility;
}
}
2、另一种是在一个已经显示在窗口上的控件中调用setSystemUiVisibility方法,传入如下属性。
frameworks/base/core/java/android/view/View.java
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
//隐藏导航栏
public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 0x00000002;
//隐藏状态栏
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 {
//控制窗口状态栏、导航栏的显示和隐藏
public int subtreeSystemUiVisibility;
}
}
窗口的状态栏导航栏显示与否,最终其实是受以上两个属性共同影响的。接下来我们具体来分析一下View的setSystemUiVisibility方法是如何生效的。
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);//通知父控件子控件属性发生了变化
}
}
}
}
setSystemUiVisibility方法首先将属性赋值给mSystemUiVisibility,然后会调用父控件的recomputeViewAttributes方法,通知父控件子控件属性发生了变化。ViewParent是一个接口,在Android中有两个类实现了这个接口,它们分别是ViewGroup和ViewRootImpl。
2、ViewGroup和ViewRootImpl和recomputeViewAttributes方法相关的代码如下所示。
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;//标记需要重新计算本地属性 if (!mWillDrawSoon) { scheduleTraversals();//进一步调用scheduleTraversals方法。 } } } }
结合Android 9.0系统源码_窗口管理(二)WindowManager对窗口的管理过程,我们知道包括Activity的跟布局DecorView在内的任何View,WindowManager在将它添加到窗口上的过程中,最终都会创建一个ViewRootImpl,并将View设置给ViewRootImpl,这样根View的父类就变成了ViewRootImpl。这就意味着不管任何子View调用recomputeViewAttributes方法,最终所触发的都是ViewRootImpl的recomputeViewAttributes,而ViewRootImpl会进一步调用scheduleTraversals方法。
3、ViewRootImpl和scheduleTraversals方法相关的代码如下所示。
public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks, AttachedSurfaceControl { final Choreographer mChoreographer;//编舞者 final TraversalRunnable mTraversalRunnable = new TraversalRunnable();//回调对象 void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); //等待系统垂直刷新同步信号,回调TraversalRunnable对象的run方法 mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } } final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal();//继续执行doTraversal } } void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } //执行performTraversals performTraversals(); if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } } } }
scheduleTraversals方法会为编舞者对象设置回调,最终会等待系统垂直刷新同步信号,回调TraversalRunnable对象的run方法,该方法会调用doTraversal方法,然后进一步调用performTraversals方法。
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() { 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;//将当前窗口的最新属性赋值给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; } ...代码省略... //此方法最终会触发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 { ...代码省略... //调用IWindowSession的relayout方法,该方法最终会触发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首先调用collectViewAttributes方法收集所有子View的属性,然后调用relayoutWindow方法,该方法最终会触发WindowManagerService的relayoutWindow方法,然后回继续调用触发View测量的performMeasure方法,触发View布局的performLayout方法和触发View绘制的performDraw方法。
1、ViewRootImpl的collectViewAttributes方法是一个很关键的方法,此方法会重新计算最新的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; //清空已经存在的SystemUiVisibility属性 mAttachInfo.mSystemUiVisibility = 0; mAttachInfo.mHasSystemUiListeners = false; //重新获取窗口视图mView最新的的SystemUI属性,赋值给mAttachInfo mView.dispatchCollectViewAttributes(mAttachInfo, 0); ...代码暂时省略... } return false; } }
collectViewAttributes首先会清空当前窗口视图mView已经存在的SystemUiVisibility属性,然后调用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; //设置最新的SystemUiVisibility监听对象,如果不为空,则将AttachInfo的mHasSystemUiListeners属性设置为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; //调用View的dispatchWindowSystemUiVisiblityChanged方法 mView.dispatchWindowSystemUiVisiblityChanged(mAttachInfo.mSystemUiVisibility); return true; } } return false; } }
在重新获得mView的SystemUiVisibility属性之后,首先会从该属性中移除被禁用的SystemUiVisibility属性,然后让params引用指向mWindowAttributes对象,并将重新获取的保存在mAttachInfo对象中的SystemUiVisibility属性保存到当前窗口的LayoutParams属性中,最后会调用当前View的dispatchWindowSystemUiVisiblityChanged方法。
4、View的dispatchWindowSystemUiVisiblityChanged方法如下所示。
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
@Deprecated
public void dispatchWindowSystemUiVisiblityChanged(int visible) {
onWindowSystemUiVisibilityChanged(visible);//调用onWindowSystemUiVisibilityChanged方法
}
public void onWindowSystemUiVisibilityChanged(int visible) {
//默认为空实现
}
}
该方法会进一步调用onWindowSystemUiVisibilityChanged方法,onWindowSystemUiVisibilityChanged方法默认为空实现,但是如果当前mView为DecorView时则不同,DecorView实现了此方法。
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) {
//调用updateColorViews方法
updateColorViews(null /* insets */, true /* animate */);
}
}
onWindowSystemUiVisibilityChanged方法会调用一个updateColorViews这个关键方法。
2、updateColorViews会调用updateColorViewInt方法更新导航栏和状态栏的背景颜色。
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) { //获取是否禁止窗口动画的标记 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); //更新导航栏颜色,mNavigationColorViewState为导航栏的相关筛选条件 updateColorViewInt(mNavigationColorViewState, sysUiVisibility, mWindow.mNavigationBarColor, mWindow.mNavigationBarDividerColor, navBarSize/*导航栏高度*/, 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; //更新状态栏颜色,mStatusColorViewState为状态栏的相关筛选条件 updateColorViewInt(mStatusColorViewState, sysUiVisibility, calculateStatusBarColor(), 0, mLastTopInset/*状态栏高度*/, false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset, animate && !disallowAnimate, mForceWindowDrawsStatusBarBackground); } ...代码省略... } }
关于DecorView更新状态栏、导航栏背景颜色的具体过程,请参考Android 9.0系统源码_SystemUI(八)PhoneWindow更新状态栏和导航栏背景颜色的流程解析。
1、重新回到第二节第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; } ...代码省略... //此方法最终会触发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 { ...代码省略... //调用IWindowSession的relayout方法,该方法最终会触发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); } }
在调用collectViewAttributes获取最新的systemUIVisibiliy属性之后,会调用relayoutWindow方法,该方法进一步调用IWindowSession的relayout方法,IWindowSession的具体实现类为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方法会进一步调用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; //是否有状态栏的使用权限 final boolean hasStatusBarPermission = mContext.checkCallingOrSelfPermission(permission.STATUS_BAR) == PackageManager.PERMISSION_GRANTED; //是否有状态栏服务的使用权限 final boolean hasStatusBarServicePermission = mContext.checkCallingOrSelfPermission(permission.STATUS_BAR_SERVICE) == PackageManager.PERMISSION_GRANTED; long origId = Binder.clearCallingIdentity(); final int displayId; synchronized(mWindowMap) { //获取当前要操作的窗口对象 WindowState win = windowForClientLocked(session, client, false); if (win == null) { return 0; } //获取窗口所属的屏幕设备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) { //如果窗口属性不为空,这里会对窗口的相关属性进行一次预处理 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; } //检测窗口标记和样式是否发生了变化 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方法会调用一个关键方法updateFocusedWindowLocked。
4、WindowManagerService的updateFocusedWindowLocked方法如下所示。
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs { //窗口管理策略的接口类WindowManagerPolicy(WMP),它用来定义一个窗口策略所要遵循的通用规范。 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) { //获取当前最新的焦点窗口 WindowState newFocus = mRoot.computeFocusedWindow(); if (mCurrentFocus != newFocus) {//如果窗口焦点发生了变化 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) {//输入法窗口发生了变化 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(); } //调用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首先是获取最新的焦点窗口,之后还会判断当前窗口焦点是否发生了变化,如果发生了变化,则会调用WindowManagerPolicy的focusChangedLw方法。
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、结合Android 9.0系统源码_窗口管理(一)WindowManagerService的启动流程这篇文章我们可以知道,SystemServer在创建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是如何实现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属性参数 private int updateSystemUiVisibilityLw() { //获取当前的焦点窗口 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相关的各种窗口参数 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) { //最终会触发状态栏管理服务StatusBarManagerService的setSystemUiVisibility方法, //通知状态栏和底部栏进行样式调整 statusbar.setSystemUiVisibility(visibility, fullscreenVisibility, dockedVisibility, 0xffffffff, fullscreenStackBounds, dockedStackBounds, win.toString()); statusbar.topAppWindowChanged(needsMenu); } } }); return diff; } }
PhoneWindowManager的focusChangedLw方法直接调用了updateSystemUiVisibilityLw方法,此方法会对窗口的SystemUiVisibility属性做一些处理,最终调用状态栏管理服务StatusBarManagerService的setSystemUiVisibility方法,通知状态栏和底部栏进行样式调整。
4、updateSystemUiVisibilityLw方法中有用到对SystemUIVisibility属性做处理的几个关键方法: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; } }
1、在第六节第2步的最后,PhoneWindowManager先是调用getStatusBarManagerInternal方法获取StatusBarManagerInternal的对象实例,然后再调用该对象的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、系统最早是在状态栏管理服务StatusBarManagerService的构造方法中将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) { //进一步调用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) { //进一步调用updateUiVisibilityLocked方法。 updateUiVisibilityLocked(vis, fullscreenStackVis, dockedStackVis, mask, fullscreenBounds, dockedBounds); disableLocked( mCurrentUserId, vis & StatusBarManager.DISABLE_MASK, mSysUiVisToken, cause, 1); } } }
由以上代码可知StatusBarManagerInternal的setSystemUiVisibility方法会进一步调用StatusBarManagerService的setSystemUiVisibility方法,StatusBarManagerService的setSystemUiVisibility方法会继续调用updateUiVisibilityLocked。
4、来看下StatusBarManagerService的updateUiVisibilityLocked方法。
public class StatusBarManagerService extends IStatusBarService.Stub { private volatile IStatusBar mBar; //设置监听 @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 { //调用IStatusBar的setSystemUiVisibility方法,触发监听回调 mBar.setSystemUiVisibility(vis, fullscreenStackVis, dockedStackVis, mask, fullscreenBounds, dockedBounds); } catch (RemoteException ex) { } } } }); } } }
updateUiVisibilityLocked方法会调用类型为IStatusBar的属性对象mBar的setSystemUiVisibility方法,mBar对象是在registerStatusBar方法中被赋值的,而且IStatusBar是一个aidl。
frameworks/base/core/java/com/android/internal/statusbar/IStatusBar.aidl
oneway interface IStatusBar
{
//通知SystemUIVisibility属性发生了变化
void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
in Rect fullscreenBounds, in Rect dockedBounds, String packageName);
}
1、我们在Android 9.0系统源码_SystemUI(二)系统状态栏导航栏的创建和添加这篇文章中有详细介绍StatusBar创建状态栏和导航栏的过程,这里我们只列出了和获取SystemUIVisibility属性相关的代码。
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将当前StatusBar 对象添加为事件回调对象 mCommandQueue.addCallbacks(this); //调用StatusBarManagerService的registerStatusBar方法,设置mCommandQueue为setSystemUiVisibility回调对象 mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders, fullscreenStackBounds, dockedStackBounds); ...代码省略 } //StatusBarManagerService的setSystemUiVisibility方法最终会回调到这里。 @Override public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) { //至此StatusBar才终于成功收到了应用所设置的SystemUiVisibility属性,然后根据具体的属性显示对应的样式 ..代码省略... } }
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);
}
通过它我们可以调用到状态栏管理服务StatusBarManagerService的registerStatusBar方法。StatusBar指定了类型为CommandQueue的属性对象mCommandQueue为StatusBarManagerService的setSystemUiVisibility的回调对象,这样StatusBarManagerService对象首先会回调CommandQueue的setSystemUiVisibility方法。
3、CommandQueue和setSystemUiVisibility方法相关的代码如下所示。
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
public class CommandQueue extends IStatusBar.Stub { private static final int MSG_SET_SYSTEMUI_VISIBILITY = 6 << MSG_SHIFT; private ArrayList<Callbacks> mCallbacks = new ArrayList<>();//回调对象集合 //回调对象 public interface Callbacks { default void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) { } } //添加回调对象到mCallbacks集合中 public void addCallbacks(Callbacks callbacks) { mCallbacks.add(callbacks); callbacks.disable(mDisable1, mDisable2, false /* animate */); } //StatusBarManagerService对象首先会回调此方法。 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; //发送消息 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; //依次调用集合中对象的setSystemUiVisibility方法,因为StatusBar有调用addCallbacks方法, //因此这里会触发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方法会将回调对象(例如前面的StatusBar)添加到mCallbacks集合中,当CommandQueued的setSystemUiVisibility方法被回调的时候,会通过Handler发送消息,并在Handler的handleMessage方法的对应消息分支中依次调用集合中回调对象的setSystemUiVisibility方法,因为StatusBar有调用addCallbacks方法将自己添加到回调集合中,因此这里会触发StatusBar的setSystemUiVisibility方法,至此StatusBar才终于成功收到了应用所设置的SystemUiVisibility属性,然后根据具体的属性显示对应的样式。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。