Android 9.0系统源码_窗口管理(二)WindowManager对窗口的管理过程

前言

上一篇我们具体分析了窗口管理者WindowManagerService的启动流程,对于WindowManagerService有了一个初步的认识。在此基础上,我本打算应该进一步分析WindowManagerService是如何管理系统中的各种窗口的,然而由于Android系统的架构设计,在分析WindowManagerService之前,我们必须先对WindowManager有一个基本的认识,才能更好的理解WindowManagerService的对窗口的管理过程。
系统对Window的操作
如上图所示,系统主要是通过WindowManager和WindowManagerService对窗口进行操作管理的,WindowManager更上层一些,WindowManagerService更底层一些,WindowManager对窗口的各种处理最终都是通过调用WindowMnagerService实现的。不同类型的窗口,WindowManager的添加过程可能会有所不同,但是WindowManagerService处理的部分,基本上是一样的。

一、窗口类型

在分析WindowManager对窗口的管理之前,我们需要先来认识一下Android系统中的窗口类型,因为不同的窗口类型,WindowManager的添加过程会有所不同。

Window的类型有很多种,比如应用程序窗口、系统错误窗口、输入法窗口、PopupWindow、Toast、Dialog等。总的来说Window分为三大类型,分别是Application Window(应用程序窗口)、Sub Window(子窗口)、System Window(系统窗口),每个大类型中又分很多小类型,它们都定义在WindowManager的静态内部类LayoutParams中,下面简单介绍一下Window的三大类型。

1、应用程序窗口
Activity就是一个典型的应用程序窗口,应用程序窗口包含的类型如下所示:

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 FIRST_APPLICATION_WINDOW = 1;
        public static final int TYPE_BASE_APPLICATION = 1;//应用程序窗口的基础值,其他窗口的值都要大于这个值
        public static final int TYPE_APPLICATION = 2;//普通的应用程序窗口类型
        public static final int TYPE_APPLICATION_STARTING = 3;//应用程序启动窗口类型,用户系统在应用程序窗口启动前显示的窗口
        public static final int TYPE_DRAWN_APPLICATION = 4;
        public static final int LAST_APPLICATION_WINDOW = 99;//应用程序窗口的结束值
}
}

应用程序窗口的Type值的范围为1~99。

2、子窗口
子窗口不能单独存在,需要依附于其他窗口才行,PopupWindow就属于子窗口。子窗口的类型定义如下所示:

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 FIRST_SUB_WINDOW = 1000;//子窗口类型初始值
        public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
        public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
        public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
        public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
        public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW + 4;
        public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
        public static final int LAST_SUB_WINDOW = 1999;//子窗口类型结束值
}
}

子窗口的Type值的范围为1000~1999。
3、系统窗口
Toast、输入法窗口、系统音量条窗口、系统错误窗口都属于系统窗口。系统窗口的类型定义如下所示:

public interface WindowManager extends ViewManager {
    
    
    public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
    
    
        public static final int FIRST_SYSTEM_WINDOW     = 2000; //系统窗口类型初始值
        public static final int TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW; //系统状态栏窗口
        public static final int TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1; //搜索条窗口
        @Deprecated //use TYPE_APPLICATION_OVERLAY instead
        public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2; //通话窗口
        @Deprecated //use TYPE_APPLICATION_OVERLAY instead
        public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3; //系统Alert窗口
        public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4; //锁屏窗口
        @Deprecated//use TYPE_APPLICATION_OVERLAY instead
        public static final int TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5; //Toast窗口
        @Deprecated //use TYPE_APPLICATION_OVERLAY instead
        public static final int TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6;
        @Deprecated //use TYPE_APPLICATION_OVERLAY instead
        public static final int TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7;
        public static final int TYPE_SYSTEM_DIALOG      = FIRST_SYSTEM_WINDOW+8;//系统弹窗
        public static final int TYPE_KEYGUARD_DIALOG    = FIRST_SYSTEM_WINDOW+9;//锁屏弹窗
        @Deprecated//use TYPE_APPLICATION_OVERLAY instead
        public static final int TYPE_SYSTEM_ERROR       = FIRST_SYSTEM_WINDOW+10;
        public static final int TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11;
        public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;
        public static final int TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13;
        public static final int TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14;
        public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15;
        public static final int TYPE_DRAG               = FIRST_SYSTEM_WINDOW+16;
        public static final int TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17;
        public static final int TYPE_POINTER = FIRST_SYSTEM_WINDOW+18;
   public static final int TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19;//系统导航栏
        public static final int TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20;//音量
        public static final int TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21;
        public static final int TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22;
        public static final int TYPE_DREAM = FIRST_SYSTEM_WINDOW+23;
        public static final int TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24;
        public static final int TYPE_DISPLAY_OVERLAY = FIRST_SYSTEM_WINDOW+26;
        public static final int TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27;
        public static final int TYPE_PRIVATE_PRESENTATION = FIRST_SYSTEM_WINDOW+30;
        public static final int TYPE_VOICE_INTERACTION = FIRST_SYSTEM_WINDOW+31;
        public static final int TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32;
        public static final int TYPE_VOICE_INTERACTION_STARTING = FIRST_SYSTEM_WINDOW+33;
        public static final int TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34;
        public static final int TYPE_QS_DIALOG = FIRST_SYSTEM_WINDOW+35;
        public static final int TYPE_PRESENTATION = FIRST_SYSTEM_WINDOW + 37;
        public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38;//很多废弃的系统弹窗类型都可以使用这个进行替换
        public static final int LAST_SYSTEM_WINDOW      = 2999; //系统窗口类型的结束值
}
}

系统窗口的类型值有接近40个,系统窗口的Type值范围为2000~2999。

4、窗口的显示次序
当一个进程向系统申请一个窗口的时候,系统会为窗口确定显示次序。为了方便窗口显示次序的管理,手机屏幕可以虚拟地用X、Y、Z轴来表示,其中Z轴垂直于屏幕,从屏幕内指向屏幕外,这样窗口的显示次序其实就是窗口在Z轴上的次序,这个次序称为Z-Oder。Type值是Z-Order排序的依据,我们知道应用程序窗口的Type值范围为1-99,子窗口为1000-1999,系统窗口为2000-2999,在一般情况下,Type值越大则Z~Order排序越靠前,窗口越靠近用户。
不过窗口显示次序的逻辑并不仅仅依靠窗口的Type,情况是比较多的;最常见的情况,当多个窗口的Type都是Type_APPLICATION,这时系统还需要结合具体情况来计算最终的Z-Oder。

二、系统窗口StatusBar的添加过程

前面我们介绍过Window的三种类型,像是应用开发最常见的Activity,它所对应的Window是应用程序类型。由于分析Activity对应的Window的添加过程还需要先分析它们所对应的Window的创建过程,这里我们简单点,先以系统状态栏窗口StatusBar为例,跟随源码梳理下一下WindowMnager是如何添加状态栏窗口的。

1、StatusBar是SystemUI的重要组成部分,具体指的就是系统状态栏,我们在Android 9.0系统源码_SystemUI(二)系统状态栏导航栏的创建和添加具体分析过它的相关源码。StatusBar主要是调用addStatusBarWindow方法实现状态栏窗口的添加的。

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java

public class StatusBar extends SystemUI implements DemoMode,
        DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
        OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
        ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter {
    
    
  
   protected StatusBarWindowView mStatusBarWindow;
   protected StatusBarWindowManager mStatusBarWindowManager;

    private void addStatusBarWindow() {
    
    
    	//构建视图
        makeStatusBarView();
        ...代码省略...
        //调用StatusBarWindowManager的add方法将系统状态栏的视图添加到窗口上
        mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
    }
    
    protected void makeStatusBarView() {
    
    
        ...代码省略...
        //创建系统状态栏的视图
        inflateStatusBarWindow(context); 
        ...代码省略...
	}
	
    protected void inflateStatusBarWindow(Context context) {
    
    
    	//将视图赋值给mStatusBarWindow
        mStatusBarWindow = (StatusBarWindowView) View.inflate(context, R.layout.super_status_bar, null);
    }    

 }

addStatusBarWindow方法首先调用makeStatusBarView构建状态栏视图,并且会将视图赋值给mStatusBarWindow,然后会调用StatusBarWindowManager的add方法,将状态栏视图和状态栏高度作为参数传进去。

2、StatusBarWindowManager类和add方法相关的代码如下所示。

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java

public class StatusBarWindowManager implements RemoteInputController.Callback, Dumpable {
    
    
   
    private final WindowManager mWindowManager;
    
    public StatusBarWindowManager(Context context) {
    
    
        mContext = context;
        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        ...代码省略...
    }

    public void add(View statusBarView, int barHeight) {
    
    
        mLp = new WindowManager.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                barHeight,
                WindowManager.LayoutParams.TYPE_STATUS_BAR,//窗口类型为状态栏
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                        | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
                PixelFormat.TRANSLUCENT);
        mLp.token = new Binder();
        mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
        mLp.gravity = Gravity.TOP;
        mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
        mLp.setTitle("StatusBar");//对用的Window的名称为StatusBar
        mLp.packageName = mContext.getPackageName();
        mStatusBarView = statusBarView;
        mBarHeight = barHeight;
        mWindowManager.addView(mStatusBarView, mLp);//将状态栏视图添加到WindowManager中
        mLpChanged = new WindowManager.LayoutParams();
        mLpChanged.copyFrom(mLp);
    }
    
}    

StatusBarWindowManager的add方法首先创建LayoutParams,
并配置了包括Width、Height、Type、Flag、Gravity、SoftInputMode等StatusBar视图的属性。特别是设置了窗口类型为TYPE_STATUS_BAR,明确了StatusBar视图的窗口类型是状态栏。然后调用WindowManager的addView方法将状态栏视图添加到WindowManager中。

三、WindowManager的addView方法

1、关于WindowManager的addView方法,具体是在WindowManagerImpl中实现的。

frameworks/base/core/java/android/view/WindowManagerImpl.java

public final class WindowManagerImpl implements WindowManager {
    
    

    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    
    
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
    
}

2、WindowManagerImpl的addView又进一步调用WindowManagerGlobal的addView方法。

frameworks/base/core/java/android/view/WindowManagerGlobal.java

public final class WindowManagerGlobal {
    
    

    private final ArrayList<View> mViews = new ArrayList<View>();//当前存在的View列表
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();//当前存在的ViewRootImpl列表
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();//当前存在的布局参数列表
 public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
    
    
      	...代码省略...
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
    
    
            parentWindow.adjustLayoutParamsForSubWindow(wparams);//如果当前窗口要作为子窗口显示,需要父窗口根据WindowManager.LayoutParams类型的wparams参数对子窗口进行相应调整
        } else {
    
    
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
    
    
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
    
    
            ...代码省略...
            root = new ViewRootImpl(view.getContext(), display);//创建ViewRootImpl实例对象
            view.setLayoutParams(wparams);
            mViews.add(view);//将要添加的view保存到View列表中
            mRoots.add(root);//将新创建的root保存到ViewRootImpl列表中
            mParams.add(wparams);//将窗口的参数保存到布局参数列表中
            try {
    
    
                root.setView(view, wparams, panelParentView);//将窗口和窗口参数设置到ViewRootImpl中
            } catch (RuntimeException e) {
    
    
                if (index >= 0) {
    
    
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }
}

1)WindowManagerGlobal中维护了和Window操作相关的3个关键列表,在窗口的添加、更新和删除过程中都会涉及这3个列表,他们分别是View列表,ViewRootImpl列表,布局参数列表。
2)addView方法首先会对传入的参数view、params、display进行检查,另外如果parentWindow不为空,还需要父窗口根据WindowManager.LayoutParams类型的wparams参数对view进行相应调整。
3)一切准备就绪会创建ViewRootImpl对象实例,在将view、root、mparams保存到对应的数据列表中后,便会调用ViewRootImpl的setView方法将窗口和窗口参数设置到ViewRootImpl中。
4)ViewRootImpl肩负了很多职责,主要有以下几点:

  • View树的根并管理View树
  • 触发View的测量、布局和绘制
  • 输入事件的中转站
  • 管理Surface
  • 负责与WindowManagerService进行进程间通信

3、 了解了ViewRootImpl的职责以后,继续来着看ViewRootImpl的setView的方法。

frameworks/base/core/java/android/view/ViewRootImpl.java

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    
    

  public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    
    
        synchronized (this) {
    
    
              ...代码省略...
                try {
    
    
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } 
              ...代码省略...
    }
}

setView方法中有很多逻辑代码,这里只截取了最关键的一部分,调用了mWindowSession的addToDisplay方法,mWindowSession是IWindowSession类型的,它是一个Binder对象,用于进行进程间通信,IWindowSession是Client端的代理,它的Server端的实现为Session,此前的代码逻辑都是运行在本地进程的,而Session的addToDisplay方法则运行在WindowManagerService所在的进程(SystemServer)中。
ViewRootImpl与WindowManagerService通信
从上图可以看出,本地进程的ViewRootImpl要想和WindowManagerService进行通信需要经过Session,那么Session为何包含在WindowManagerService中呢?

4、继续看Session的addToDisplay方法。

frameworks/base/services/core/java/com/android/server/wm/Session.java

class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
    
    

    final WindowManagerService mService;

    public Session(WindowManagerService service, IWindowSessionCallback callback,
            IInputMethodClient client, IInputContext inputContext) {
    
    
        mService = service;
        ...代码省略...
 	}
 	
    @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, InputChannel outInputChannel) {
    
    
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }
}    

1)addToDisplay方法会进一步调用WindowManagerService的addWindow方法,并将自身作为参数传了进去,每个应用程序进程都会对应一个Session,WindowManagerService会用ArrayList来保存这些Session,这就是为什么WindowManagerService包含Session的原因。
2)之后的工作就全都交给了WindowManagerService处理,WindowManagerService会为这个添加的窗口分配Surface,并确定窗口显示次序,可见负责显示界面的是画布Surface,而不是窗口本身。WindowManagerService会将它所管理的Surface交由SurfaceFlinger处理,SurfaceFlinger会将这些Surface混合并绘制到屏幕上。

四、总结

系统状态栏窗口的添加过程

猜你喜欢

转载自blog.csdn.net/abc6368765/article/details/129781572