当前位置:   article > 正文

Android 12系统源码_SystemUI(八)SystemUIVisibility属性

systemuivisibility

前言

在Android系统中,很多应用都需要根据具体情况来控制状态栏和导航栏的显示和隐藏,又或者将状态栏透明,实现诸如沉浸式、全面屏灯效果,而要实现这些效果,都离不开SystemUIVisibility属性。由于SystemUIVisibilityy属性主要用来控制系统状态栏和导航栏的行为,而状态栏和导航栏都属于SystemUI模块的StatusBar,所以SystemUIVisibility属性的消费者肯定包含StatusBar。另外当状态栏和导航栏发生变化的时候,窗口的布局一般也会跟着发生变化,这就意味着窗口管理者PhoneWindowManager肯定也要消费SystemUIVisibility属性。本篇文章我们就来具体分析一下和这个属性有关的代码。

一、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;
    }
}    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这种方式最终影响的其实是窗口的WindowManager.LayoutParams对象的subtreeSystemUiVisibility属性。

public interface WindowManager extends ViewManager {
    public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
		//控制窗口状态栏、导航栏的显示和隐藏
        public int subtreeSystemUiVisibility;
    }
}    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

窗口的状态栏导航栏显示与否,最终其实是受以上两个属性共同影响的。接下来我们具体来分析一下View的setSystemUiVisibility方法是如何生效的。

二、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);//通知父控件子控件属性发生了变化
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

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);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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方法。
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

结合Android 12系统源码_窗口管理(二)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;
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

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, params,
                (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
                insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
                mTempControls, mSurfaceSize);        
     }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

performTraversals首先调用collectViewAttributes方法收集所有子View的属性,然后调用relayoutWindow方法,该方法最终会触发WindowManagerService的relayoutWindow方法,然后回继续调用触发View测量的performMeasure方法,触发View布局的performLayout方法和触发View绘制的performDraw方法。

三、获取最新的SystemUIVisibility属性

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;
    }
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

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;
            }
        }
    }

}    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

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);
            mCompatibleVisibilityInfo.globalVisibility =
                    (mCompatibleVisibilityInfo.globalVisibility & ~View.SYSTEM_UI_FLAG_LOW_PROFILE)
                            | (mAttachInfo.mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
            dispatchDispatchSystemUiVisibilityChanged(mCompatibleVisibilityInfo);
            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;
    }
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

在重新获得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) {
    	//默认为空实现
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

该方法会进一步调用onWindowSystemUiVisibilityChanged方法,onWindowSystemUiVisibilityChanged方法默认为空实现,但是如果当前mView为DecorView时则不同,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 */);
        updateDecorCaptionStatus(getResources().getConfiguration());
        if (mStatusGuard != null && mStatusGuard.getVisibility() == VISIBLE) {
            updateStatusGuardColor();
        }
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

onWindowSystemUiVisibilityChanged方法会调用一个updateColorViews这个关键方法。
2、updateColorViews会调用updateColorViewInt方法更新导航栏和状态栏的背景颜色。

    public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
    
    WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
    		...代码省略...
            //更新导航栏颜色,mNavigationColorViewState为导航栏的相关筛选条件
            Log.d("SystemBar", "updateColorViews mNavigationColorViewState="+mNavigationColorViewState);
            updateColorViewInt(mNavigationColorViewState, calculateNavigationBarColor(appearance),
                    mWindow.mNavigationBarDividerColor, navBarSize,
                    navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge,
                    0 /* sideInset */, animate && !disallowAnimate,
                    mForceWindowDrawsBarBackgrounds, controller);
            boolean oldDrawLegacy = mDrawLegacyNavigationBarBackground;
            mDrawLegacyNavigationBarBackground = mNavigationColorViewState.visible
                    && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0;
            if (oldDrawLegacy != mDrawLegacyNavigationBarBackground) {
                if (viewRoot != null) {
                    viewRoot.requestInvalidateRootRenderNode();
                }
            }
    		...代码省略...
            boolean statusBarNeedsRightInset = navBarToRightEdge
                    && mNavigationColorViewState.present;
            boolean statusBarNeedsLeftInset = navBarToLeftEdge
                    && mNavigationColorViewState.present;
            int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset
                    : statusBarNeedsLeftInset ? mLastLeftInset : 0;
            int statusBarColor = calculateStatusBarColor(appearance);
            //更新状态栏颜色,mStatusColorViewState为状态栏的相关筛选条件
            Log.d("SystemBar", "updateColorViews mStatusColorViewState="+mStatusColorViewState);
            updateColorViewInt(mStatusColorViewState, statusBarColor, 0,
                    mLastTopInset, false /* matchVertical */, statusBarNeedsLeftInset,
                    statusBarSideInset, animate && !disallowAnimate,
                    mForceWindowDrawsBarBackgrounds, controller);
    		...代码省略...
    }
        
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

关于DecorView更新状态栏、导航栏背景颜色的过程,后续篇章会具体分析。

五、WindowManagerService的relayoutWindow方法

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, params,
                (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
                insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
                mTempControls, mSurfaceSize);        
     }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

在调用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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

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, LayoutParams attrs//窗口属性
                              , int requestedWidth, int requestedHeight, int viewVisibility//根View控件是否可见
                              , int flags, long frameNumber, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
                              SurfaceControl outSurfaceControl, InsetsState outInsetsState,
                              InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
            ...代码省略...
           final WindowState win = windowForClientLocked(session, client, false);
            if (win == null) {
                return 0;
            }
            final DisplayContent displayContent = win.getDisplayContent();
            final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
            ...代码省略...
            //是否应该重新布局
            final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
                    (win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
                            || win.mActivityRecord.isClientVisible());
           ...代码省略...
           if (shouldRelayout) {
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");
                //标记
                result = win.relayoutVisibleWindow(result);
                if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                    //焦点可能变化了
                    focusMayChange = true;
                }
                if (win.mAttrs.type == TYPE_INPUT_METHOD
                        && displayContent.mInputMethodWindow == null) {
                    displayContent.setInputMethodWindowLocked(win);
                    imMayMove = true;
                }
                win.adjustStartingWindowFlags();
                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            }  
            ...代码省略... 
            if (focusMayChange) {
                //relayoutWindow add by syl
                Log.d(TAG, "SystemBar: WindowManagerService_relayoutWindow");
                if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {
                    imMayMove = false;
                }
            }   
            ...代码省略...           
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

relayoutWindow方法会调用一个关键方法updateFocusedWindowLocked。

4、WindowManagerService的updateFocusedWindowLocked方法如下所示。

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
   
   RootWindowContainer mRoot;

    //更新窗口焦点的变化
    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
        Log.d("SystemBar", "WindowManagerService: updateFocusedWindowLocked");
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
        boolean changed = mRoot.updateFocusedWindowLocked(mode, updateInputWindows);
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        return changed;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

此方法会进一步调用RootWindowContainer的updateFocusedWindowLocked方法。

5、RootWindowContainer的updateFocusedWindowLocked方法如下所示。
/frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java

class RootWindowContainer extends WindowContainer<DisplayContent>
        implements DisplayManager.DisplayListener {
    // 当窗口焦点发生变化的时候,更新窗口焦点的变化
    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
        mTopFocusedAppByProcess.clear();
        boolean changed = false;
        int topFocusedDisplayId = INVALID_DISPLAY;
        for (int i = mChildren.size() - 1; i >= 0; --i) {
        	//循环遍历DisplayContent的updateFocusedWindowLocked方法
            final DisplayContent dc = mChildren.get(i);
            changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows, topFocusedDisplayId);
            final WindowState newFocus = dc.mCurrentFocus;
            if (newFocus != null) {
                final int pidOfNewFocus = newFocus.mSession.mPid;
                if (mTopFocusedAppByProcess.get(pidOfNewFocus) == null) {
                    mTopFocusedAppByProcess.put(pidOfNewFocus, newFocus.mActivityRecord);
                }
                if (topFocusedDisplayId == INVALID_DISPLAY) {
                    topFocusedDisplayId = dc.getDisplayId();
                }
            } else if (topFocusedDisplayId == INVALID_DISPLAY && dc.mFocusedApp != null) {
                // The top-most display that has a focused app should still be the top focused
                // display even when the app window is not ready yet (process not attached or
                // window not added yet).
                topFocusedDisplayId = dc.getDisplayId();
            }
        }
        if (topFocusedDisplayId == INVALID_DISPLAY) {
            topFocusedDisplayId = DEFAULT_DISPLAY;
        }
        if (mTopFocusedDisplayId != topFocusedDisplayId) {
            mTopFocusedDisplayId = topFocusedDisplayId;
            mWmService.mInputManager.setFocusedDisplay(topFocusedDisplayId);
            mWmService.mPolicy.setTopFocusedDisplay(topFocusedDisplayId);
            mWmService.mAccessibilityController.setFocusedDisplay(topFocusedDisplayId);
            ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "New topFocusedDisplayId=%d", topFocusedDisplayId);
        }
        return changed;
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

6、DisplayContent 的updateFocusedWindowLocked方法如下所示。

class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.DisplayContentInfo {

    //当窗口焦点发生变化的时候,更新窗口焦点的变化 
    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows,
            int topFocusedDisplayId) {
        WindowState newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
        if (mCurrentFocus == newFocus) {
            return false;
        }
        boolean imWindowChanged = false;
        final WindowState imWindow = mInputMethodWindow;
        if (imWindow != null) {
            final WindowState prevTarget = mImeLayeringTarget;
            final WindowState newTarget = computeImeTarget(true /* updateImeTarget*/);
            imWindowChanged = prevTarget != newTarget;

            if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
                    && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
                assignWindowLayers(false /* setLayoutNeeded */);
            }

            if (imWindowChanged) {
                mWmService.mWindowsChanged = true;
                setLayoutNeeded();
                newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
            }
        }

        ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "Changing focus from %s to %s displayId=%d Callers=%s",
                mCurrentFocus, newFocus, getDisplayId(), Debug.getCallers(4));
        final WindowState oldFocus = mCurrentFocus;
        mCurrentFocus = newFocus;

        if (newFocus != null) {
            mWinAddedSinceNullFocus.clear();
            mWinRemovedSinceNullFocus.clear();

            if (newFocus.canReceiveKeys()) {
                // Displaying a window implicitly causes dispatching to be unpaused.
                // This is to protect against bugs if someone pauses dispatching but
                // forgets to resume.
                newFocus.mToken.paused = false;
            }
        }
		//触发DisplayPolicy的focusChangedLw方法。
        getDisplayPolicy().focusChangedLw(oldFocus, newFocus);
		...代码省略...
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

7、DisplayPolicy的focusChangedLw方法如下所示。

public class DisplayPolicy {
    /**
     * A new window has been focused.
     */
    public void focusChangedLw(WindowState lastFocus, WindowState newFocus) {
      	//焦点发生了变化
        mFocusedWindow = newFocus;
        mLastFocusedWindow = lastFocus;
        if (mDisplayContent.isDefaultDisplay) {
            mService.mPolicy.onDefaultDisplayFocusChangedLw(newFocus);
        }
        //更新SystemBar的属性
        updateSystemBarAttributes();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

DisplayPolicy的focusChangedLw方法最终会调用updateSystemBarAttributes方法来刷新系统栏属性。

8、DisplayPolicy的updateSystemBarAttributes的方法如下所示。

public class DisplayPolicy {

    //更新SystemBar的属性
    void updateSystemBarAttributes() {
        WindowState winCandidate = mFocusedWindow;//焦点窗口
        if (winCandidate == null && mTopFullscreenOpaqueWindowState != null
                && (mTopFullscreenOpaqueWindowState.mAttrs.flags
                & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0) {
            //只有焦点窗口才能控制系统栏
            winCandidate = mTopFullscreenOpaqueWindowState;
        }
        // If there is no window focused, there will be nobody to handle the events
        // anyway, so just hang on in whatever state we're in until things settle down.
        if (winCandidate == null) {
            return;
        }

        // The immersive mode confirmation should never affect the system bar visibility, otherwise
        // it will unhide the navigation bar and hide itself.
        if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) {

            // The immersive mode confirmation took the focus from mLastFocusedWindow which was
            // controlling the system ui visibility. So if mLastFocusedWindow can still receive
            // keys, we let it keep controlling the visibility.
            final boolean lastFocusCanReceiveKeys =
                    (mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys());
            winCandidate = isKeyguardShowing() && !isKeyguardOccluded() ? mNotificationShade
                    : lastFocusCanReceiveKeys ? mLastFocusedWindow
                            : mTopFullscreenOpaqueWindowState;
            if (winCandidate == null) {
                return;
            }
        }
        final WindowState win = winCandidate;
        mSystemUiControllingWindow = win;

        final int displayId = getDisplayId();//获取屏幕设备id
        final int disableFlags = win.getDisableFlags();//获取窗口禁止的属性标记
        //结合参数disableFlags重新更新系统栏窗口参数
        final int opaqueAppearance = updateSystemBarsLw(win, disableFlags);

 		final WindowState navColorWin = chooseNavigationColorWindowLw(mNavBarColorWindowCandidate,
        mDisplayContent.mInputMethodWindow, mNavigationBarPosition);
        final boolean isNavbarColorManagedByIme =
                navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow;
        final int appearance = updateLightNavigationBarLw(win.mAttrs.insetsFlags.appearance,
                navColorWin) | opaqueAppearance;
        final int behavior = win.mAttrs.insetsFlags.behavior;
        final String focusedApp = win.mAttrs.packageName;
        final boolean isFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR)
                || !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
        final AppearanceRegion[] appearanceRegions =
                new AppearanceRegion[mStatusBarColorWindows.size()];
        for (int i = mStatusBarColorWindows.size() - 1; i >= 0; i--) {
            final WindowState windowState = mStatusBarColorWindows.get(i);
            appearanceRegions[i] = new AppearanceRegion(
                    getStatusBarAppearance(windowState, windowState),
                    new Rect(windowState.getFrame()));
        }
        if (mLastDisableFlags != disableFlags) {
            mLastDisableFlags = disableFlags;
            final String cause = win.toString();
            callStatusBarSafely(statusBar -> statusBar.setDisableFlags(displayId, disableFlags,
                    cause));
        }
        if (mLastAppearance == appearance
                && mLastBehavior == behavior
                && mRequestedVisibilities.equals(win.getRequestedVisibilities())
                && Objects.equals(mFocusedApp, focusedApp)
                && mLastFocusIsFullscreen == isFullscreen
                && Arrays.equals(mLastStatusBarAppearanceRegions, appearanceRegions)) {
            return;
        }
        if (mDisplayContent.isDefaultDisplay && mLastFocusIsFullscreen != isFullscreen
                && ((mLastAppearance ^ appearance) & APPEARANCE_LOW_PROFILE_BARS) != 0) {
            mService.mInputManager.setSystemUiLightsOut(
                    isFullscreen || (appearance & APPEARANCE_LOW_PROFILE_BARS) != 0);
        }
        final InsetsVisibilities requestedVisibilities =
                new InsetsVisibilities(win.getRequestedVisibilities());
        mLastAppearance = appearance;
        mLastBehavior = behavior;
        mRequestedVisibilities = requestedVisibilities;
        mFocusedApp = focusedApp;
        mLastFocusIsFullscreen = isFullscreen;
        mLastStatusBarAppearanceRegions = appearanceRegions;
        //调用状态栏管理服务StatusBarManagerService的onSystemBarAttributesChanged
        callStatusBarSafely(statusBar -> statusBar.onSystemBarAttributesChanged(displayId,
                appearance, appearanceRegions, isNavbarColorManagedByIme, behavior,
                requestedVisibilities, focusedApp));
    }
 }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93

updateSystemBarAttributes方法首先获取当前的焦点窗口mFocusedWindow,将该窗口赋值给winCandidate,并判断该窗口是否可以操控系统栏,如果不允许会直接返回;如果允许,则会结合winCandidate的属性调用updateSystemBarsLw方法,来更新系统栏参数,之后还会调用状态栏管理服务StatusBarManagerService的onSystemBarAttributesChanged,而此回调方法最终会回传给系统状态栏和导航栏,使得新的SystemUIVisibility生效。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/327155?site
推荐阅读
相关标签
  

闽ICP备14008679号