IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 7.理解WindowManager -> 正文阅读

[移动开发]7.理解WindowManager

理解WindowManager

这篇文章只是对刘望舒大佬书籍的摘抄,适合复习用,没看过的请先看大佬的原书
下面是大佬博客的链接Android进阶三部曲 第二部《Android进阶解密》

一、Window、WindowManager和WMS

Window是一个抽象类,具体的实现类是PhoneWindow,它对View进行管理。

WindowManager是一个接口类,继承自接口ViewManager,它是用来管理Window的,它的实现类为WindowManagerImpl。

对Window(View)进行添加、更新和删除操作可以使用WindowManager,WindowManager会将具体的工作交给WMS来处理,WindowManager和WMS通过Binder来进行跨进程通信,WMS作为系统服务有很多API不会暴露给WindowManager,与ActivityManager和AMS类似

WMS会在后面学习,它的主要功能是管理Window。

Window、WindowManager和WMS的关系如下图

请添加图片描述

二、WindowManager的关联类

WindowManager继承自ViewManager,ViewManager中定义了3个方法,分别用来添加、更新和删除View

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

public interface ViewManager
{
  public void addView(View view, ViewGroup.LayoutParams params);
  public void updateViewLayout(View view, ViewGroup.LayoutParams params);
  public void removeView(View view);
}

WindowManager也继承了这些方法,而这些方法传入的参数都是View类型,说明Window是以View的形式存在的。

WindowManager在继承ViewManager的同时,又加入很多功能,包括Window的类型和层级相关的常量、内部类以及一些方法,其中有两个方法是根据Window的特性加入的,如下所示:

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

public Display getDefaultDisplay();

public void removeViewImmediate(View view);

getDefaultDisplay方法能够得到WindowManager所管理的屏幕(Display)。removeViewImmediate方法规定在这个方法返回前要立即执行View.onDetachedFromWindow(),来完成传入的View相关的销毁工作。

Window是一个抽象类,它的具体实现是PhoneWindow。

在Activity启动过程中会调用ActivityThread 的performLaunchActivity方法 ,performLaunchActivity方法又会调用Activity的attach 方法,PhoneWindow 就是在Activity的attach 方法中创建过的,

/frameworks/base/core/java/android/app/Activity.java

final void attach(Context context, ActivityThread aThread,
                  Instrumentation instr, IBinder token, int ident,
                  Application application, Intent intent, ActivityInfo info,
                  CharSequence title, Activity parent, String id,
                  NonConfigurationInstances lastNonConfigurationInstances,
                  Configuration config, String referrer, IVoiceInteractor voiceInteractor,
                  Window window, ActivityConfigCallback activityConfigCallback) {
  ...
  //1.创建PhoneWindow实例
  mWindow = new PhoneWindow(this, window, activityConfigCallback);
  ...
  //2.调用PhoneWindow的setWindowManager方法,这个方法在其父类Window中实现
  mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
    mToken, mComponent.flattenToString(),
    (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
  ...
}

这里显示创建了PhoneWindow然后调用PhoneWindow的setWindowManager方法,而这个方法在其父类Window中实现

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

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
                             boolean hardwareAccelerated) {
  mAppToken = appToken;
  mAppName = appName;
  mHardwareAccelerated = hardwareAccelerated
    || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
  if (wm == null) {
    //1.如果WindowManager为null,就调用Context的getSystemService(Context.WINDOW_SERVICE)获取
    wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
  }
  //2.创建WindowManagerImpl
  mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

首先是注释1,如果传入的WindowManager为null,就调用Context的getSystemService(Context.WINDOW_SERVICE)获取,Context.WINDOW_SERVICE的值为"window",getSystemService具体在ContextImpl中实现

/frameworks/base/core/java/android/app/ContextImpl.java

public Object getSystemService(String name) {
  return SystemServiceRegistry.getSystemService(this, name);
}

里面调用了SystemServiceRegistry的getSystemService方法

frameworks/base/core/java/android/app/SystemServiceRegistry.java

public static Object getSystemService(ContextImpl ctx, String name) {
  //SYSTEM_SERVICE_FETCHERS中获取一个ServiceFetcher
  ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
  //调用ServiceFetcher的getService方法
  return fetcher != null ? fetcher.getService(ctx) : null;
}

SYSTEM_SERVICE_FETCHERS是一个HashMap类型的数据,它用来存储服务名称对应ServiceFetcher,这个map的是在类的static方法块中初始化的

final class SystemServiceRegistry {
  ...
  private SystemServiceRegistry() { }
  static {
    ...
    registerService(Context.WINDOW_SERVICE, WindowManager.class,
                    //这里创建了一个CachedServiceFetcher
                    new CachedServiceFetcher<WindowManager>() {
                      @Override
                      public WindowManager createService(ContextImpl ctx) {
                        return new WindowManagerImpl(ctx);
                      }});
    ...
  }
  ...
  private static <T> void registerService(String serviceName, Class<T> serviceClass,
                                          ServiceFetcher<T> serviceFetcher) {
    SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
    SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
  }
  ...
}

在SystemServiceRegistry的静态代码块中,会调用多个registerService方法,registerService会发会将serviceFetcher保存进去

注册Context.WINDOW_SERVICE时,创建了一个CachedServiceFetcher,这个类会在有调用一次createService方法进行创建服务对象,然后将服务对象缓存起来,

很明显上面是创建一个WindowManagerImpl,所以Context的getSystemService(Context.WINDOW_SERVICE)获取获取了一个WindowManagerImpl实例。

我们再回到Window的setWindowManager方法,在注释一中得到了WindowManagerImpl实例,在注释2中调用WindowManagerImpl的createLocalWindowManager方法

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

public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
  return new WindowManagerImpl(mContext, parentWindow);
}

createLocalWindowManager方法同样也创建了WindowManagerImpl,不同的是这次创建的WindowManagerImpl时将创建它的Window作为参数传入进来了,这样WindowManagerImpl就吃有了Window的引用,可以对Window进行操作

例如在Window中添加View,会调用WindowManagerImpl的addView方法

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
  applyDefaultToken(params);
  //1.调用WindowManagerGlobal的addView方法
  mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

在注释1处调用了WindowManagerGlobal的addView方法,其中最后一个参数mParentWindow就是上面创建WindowManagerImpl时传入的Window。

可以看出WindowManagerImpl虽然是WindowManager的实现类,但是没有实现什么功能,而是将功能实现委托给了WindowManagerGlobal。这里用到的是桥接模式

我们再看一下WindowManagerImpl中如何定义WindowManagerGlobal

public final class WindowManagerImpl implements WindowManager {
  //1.获取单例对象WindowManagerGlobal
  private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
  private final Context mContext;
  //2.表示这个WindowManagerImpl的父Window
  private final Window mParentWindow;
  ...
  private WindowManagerImpl(Context context, Window parentWindow) {
    mContext = context;
    //3.给mParentWindow赋值
    mParentWindow = parentWindow;
  }
  ...
}

在注释1中可以看出WindowManagerGlobal是一个单例,说明在一个进程中只有一个WindowManagerGlobal的实例

在注释2和注释3中的代码表示这个WindowManagerImpl实例会作为那个Window的子Window。这说明在一个进程中WindowManagerImpl可能会存在多个实例。

最终,WindowManager的关联类如下图所示
请添加图片描述

从图中可以看出,PhoneWindow继承自Window,Window通过serWindowManager方法与WindowManager发生关联。WindowManager继承自接口ViewManager,WindowManagerImpl是WindowManager接口的实现类,但是具体功能都会委托给WindowManagerGlobal来实现。

三、Window的属性

Window的属性是用来给WMS管理Window用的,他们被定义在WindowManager的内部类LayoutParams中。

Window的属性中与应用开发最密切解除的有3种,它们分别是Type(Window的类型)、Flag(Window的标志)和SoftInputMode(软键盘相关模式)

Window的类型和显示次序

总的来说Window分为三大类型,分别是Application Window(应用程序窗口)、SubWindow(子窗口)、System Window(系统窗口),每个大类型中又包含了很多类型

  1. 应用程序窗口

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

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

    //1.应用程序窗口类型初始值
    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就属于子窗口。,子窗口的类型定义如下

    //子窗口类型初始值
    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;
    
  3. 系统窗口

    Toast、输入法窗口、系统音量条窗口、系统错误窗口都输入系统窗口。系统窗口的类型定义如下

    //系统窗口类型初始值
    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;
    //通话窗口
    public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;
    //系统ALERT窗口
    public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;
    //锁屏窗口
    public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;
    //TOAST窗口
    public static final int TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5;
    ...
    //系统窗口类型结束值
    public static final int LAST_SYSTEM_WINDOW      = 2999;
    

    系统窗口的type值范围为2000~2999

  4. 窗口显示次序

    当一个进程向WMS申请一个窗口时,WMS会为窗口确定显示次序。为了方便窗口显示次序的管理,系统屏幕可以虚拟的用X、Y、Z轴来表示,其中Z轴垂直与屏幕,从屏幕内指向屏幕外,确定窗口显示次序也就是确定窗口在Z轴上的次序,这个次序称为Z-Order。

    Type值是Z-Order排序的依据,在一般情况下,Type值越大则Z-Oder排序越靠前,就越靠近用户。当然窗口显示次序的路基不仅仅如此,还有其他逻辑在。

Window的标志

Window的标志也就是Flag,用于控制Window的显示,同样定义在WindowManager的LayoutParams中,一种有20多个,下表是一些常用的

Flag描述
FLAG_ALLOW_LOCK_WHILE_SCREEN_ON只要窗口可见,就允许在开启状态的屏幕上锁屏
FLAG_NOT_FOCUSABLE窗口不能获得输入焦点,设置该标志时FLAG_NOT_TOUCH_MODAL也会被设置
FALG_NOT_TOUCHABLE窗口不接受任何触摸事件
FALG_NOT_TOUCH_MODAL将该窗口区域外的触摸事件传递给其他的Window,而自己只会处理窗口区域内的触摸事件
FALG_KEPP_SCREEN_ON只要窗口可见,屏幕就会一直亮着
FALG_LAYOUT_NO_LIMITS允许窗口超出屏幕之外
FLAG_FULLSCREEN隐藏所有的屏幕装饰窗口,比如在游戏、播放器中的全屏显示
FALG_SHOW_WHEN_LOCKED窗口可以在锁屏的窗口之上显示
FLAG_IGNORE_CHEEK_PRESSES当用户的脸贴紧屏幕时,不会响应此事件
FLAG_TURN_SCREEN_ON窗口显示时将屏幕点亮

设置Window的Flag有3中方法,第一种是通过Window的addFlags方法

Window window=getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

第二种是通过Window的setFlags方法

Window window=getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);

其实Window的addFlags方法内部会调用setFlags方法,因此这两种方法区别不大。

第三种方法是给LayoutParams设置Flag,并通过WindowManager的addView方法进行添加,

WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN;
WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
TextView textView = new TextView(this);
windowManager.addView(textView, layoutParams);

软件盘的相关模式

默认情况下,软键盘窗口会盖住输入框下方的按钮。我们可以通过修改SoftInputMode,下面是几种常见的WindowManager的静态内部类LayoutParams中定义的软键盘相关模式

SoftInputMode描述
SOFT_INPUT_STATE_UNSPECIFIED没有指定状态,系统会选择一个合适的状态或依赖于主题的设置
SOFT_INPUT_STATE_UNCHANGED不改变软键盘状态
SOFT_INPUT_STATE_HIDDEN当用户进入该窗口时,软键盘默认隐藏
SOFT_INPUT_STATE_ALWAYS_HIDDEN当窗口获取焦点时,软键盘总是被隐藏
SOFT_INPUT_STATE_ADJUST_RESIZE当软键盘弹出时,窗口会调整大小
SOFT_INPUT_STATE_ADJUST_PAN当软键盘弹出时,窗口不会调整大小,要确保输入焦点是可见的

可以看出它们与AndroidManifest中Activity的属性android:windowSoftInputMode是对应的。所以我们除了在AndroidManifest中为Activity设置android:windowSoftInputMode外,还可以在Java代码中设置SoftInInputMode

getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);

四、Window的操作

WindowManager对Window进行管理,有对Window的添加、更新、和删除操作,我们把他们统称为对Window的操作。对于Window的操作,最终都是交给WMS来进行处理的。

窗口的操作分为两大部分,一部分是WindowManager处理部分,另一部分是WMS处理部分。我们知道Window分为三大类,分别是Application Window、Sub Window和System Window,对于不同类型的窗口添加过程会有所不对,但是对于WMS处理部分,添加的过程基本上是一样的。如图所示

请添加图片描述

系统窗口的添加过程

Window分为三大类型,不同类型的Window的添加过程不尽相同,我们主要看系统窗口的添加过程。系统窗口的添加过程也会根据不同的系统窗口有所不同。

下面主要是系统窗口StatusBar为例,StatusBar是SystemUi的重要组成部分,具体指系统状态栏,用于显示时间、电量、信号等信息。

下面是系统窗口 StatusBar 的添加过程的时序图

请添加图片描述

下面是StatusBar的addStatusBarWindow方法,这个方法负责为Status添加Window。

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

private void addStatusBarWindow() {
  //构建StatusBar的视图
  makeStatusBarView();
  mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
  mRemoteInputController = new RemoteInputController(mHeadsUpManager);
  //2.调用StatusBarWindowManager的add方法
  mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
}

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

public void add(View statusBarView, int barHeight) {
  mLp = new WindowManager.LayoutParams(
    ViewGroup.LayoutParams.MATCH_PARENT,
    barHeight,
    //1.设置窗口类型为TYPE_STATUS_BAR
    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");
  mLp.packageName = mContext.getPackageName();
  mStatusBarView = statusBarView;
  mBarHeight = barHeight;
  //2.调用WindowManager的addView方法,实际上是调用了WindowManagerImpl的addView方法
  mWindowManager.addView(mStatusBarView, mLp);
  mLpChanged = new WindowManager.LayoutParams();
  mLpChanged.copyFrom(mLp);
}

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

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
  applyDefaultToken(params);
  //调用WindowManagerGlobal的addView
  mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

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

public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {
  ...
  final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
  //如果parentWindow存在(当前窗口要作为子窗口)
  if (parentWindow != null) {
    //1.根据父窗口对子窗口的WindowManager.LayoutParams类型的wparams进行相应调整
    parentWindow.adjustLayoutParamsForSubWindow(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) {
    ...
    //2.创建ViewRootImpl并赋值给root
    root = new ViewRootImpl(view.getContext(), display);
    view.setLayoutParams(wparams);
    //3.将添加的View保存到View列表中
    mViews.add(view);
    //4.将root存入到ViewRootImpl列表中
    mRoots.add(root);
    //5.将窗口的参数保存到布局参数列表中。
    mParams.add(wparams);
    try {
      //6.将窗口和窗口的参数通过setView方法设置到ViewRootImpl中
      root.setView(view, wparams, panelParentView);
    } catch (RuntimeException e) {
      ...
    }
  }
}

WindowManagerGlobal中维护了和Window操作相关的3个列表,在窗口的添加、更新和删除过程中都会涉及这3个列表。他们分别是View列表(ArrayList<View> mViews)、布局参数列表(ArrayList<WindowManager.LayoutParams>mParams)和ViewRootImpl列表(ArrayList<ViewRootImpl>mRoots)

可以从上面注释6处看到,view的添加是交给了ViewRootImpl来进行的。

ViewRootImpl负担了很多职责,主要有一下几点

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

下面我们看 ViewRootImpl 的setView方法

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

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
  synchronized (this) {
    if (mView == null) {
      ...
      try {
        mOrigWindowType = mWindowAttributes.type;
        mAttachInfo.mRecomputeGlobalAttributes = true;
        collectViewAttributes();
        //调用IWindowSession的addToDisplay方法
        res = mWindowSession.addToDisplay(
          mWindow, mSeq, mWindowAttributes,
          getHostVisibility(), mDisplay.getDisplayId(),
          mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
          mAttachInfo.mOutsets, mInputChannel);
      } catch (RemoteException e) {
        ...
      } finally {
        ...
      }
      ...
    }
  }
}

上面主要调用了mWindowSession的addToDisplay方法,mWindowSession是 IWindowSession 类型的,它是一个 Binder 对象,用于进程间通信,IWindowSession 是 Client 端的代理,它的 Server 端的实现为 Session,此前的代码逻辑都是运行在本地进程的,而 Session 的 addToDisplay 方法则运行在 WMS 所在的进程(SystemServer 进程)中,如图所示

请添加图片描述

本地进程的 ViewRootImpl 要想和 WMS 进行通信需要经过 Session。下面看下 Session 的 addToDisplay 方法

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

public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
           int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
           Rect outOutsets, InputChannel outInputChannel) {
  //调用 WMS 的 addWindow 方法。并将自身作为参数传入
  return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                            outContentInsets, outStableInsets, outOutsets, outInputChannel);
}

这里 Session 调用 WMS 的 addWindow 方法。并将自身作为参数传入。

每个应用程序都会对应一个 Session,WMS 会用 ArrayList 来保存这些 Session。这也是上图中 WMS 包含 Session 的原因。

Session 调用 WMS 的 addWindow 方法后,剩下的工作交给 WMS 来处理,在 WMS 中会为这个添加的窗口分配 Surface,并确定窗口的显示次序,所以负责显示界面的其实是画布 Surface,而不是窗口本身。

WMS 会将它所管理的 Surface 交给 SurfaceFlinger 处理,SurfaceFlinger 会将这些 Surface 混合并绘制到屏幕上。

Activity 的添加过程

无论那种窗口,它的添加过程在 WMS 处理部分中基本是类似的(只不过会在权限和窗口显示次序等方面会有些不同)。但是在 WindowManager 处理处理部分会有所不同,这里以应用程序窗口 Activity 为例,

Activity 在启动过程中,如果 Activity 所在的进程不存在则会创建新的进程,创建新的进程之后就会运行代表主线程的实例 ActivityThread,ActivityThread 管理着当前应用程序进程的线程,这在 Activity 的启动过程中运用的明显,当界面要与用户进行交互时,会调用 ActivityThread 的 handleResumeActivity 方法。

/frameworks/base/core/java/android/app/ActivityThread.java

final void handleResumeActivity(IBinder token,boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
  ...
  //1.最终调用 Activity 的 onResume 方法。
  r = performResumeActivity(token, clearHide, reason);
  if (r != null) {
    ...
    if (r.window == null && !a.mFinished && willBeVisible) {
      r.window = r.activity.getWindow();
      View decor = r.window.getDecorView();
      decor.setVisibility(View.INVISIBLE);
      //2.获得 ViewManager 类型的 wm 对象,实际实现是 WindowManagerImpl 类型
      ViewManager wm = a.getWindowManager();
      WindowManager.LayoutParams l = r.window.getAttributes();
      a.mDecor = decor;
      l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
      l.softInputMode |= forwardBit;
      if (r.mPreserveWindow) {
        a.mWindowAdded = true;
        r.mPreserveWindow = false;
        ViewRootImpl impl = decor.getViewRootImpl();
        if (impl != null) {
          impl.notifyChildRebuilt();
        }
      }
      if (a.mVisibleFromClient) {
        if (!a.mWindowAdded) {
          a.mWindowAdded = true;
          //3.调用 WindowManager 的 addView 方法
          wm.addView(decor, l);
        } else {
          a.onWindowAttributesChanged(l);
        }
      }
      ...
    }
    ...
  } 
  ...
}

最终也是调用了 WindowManagerImpl 的 addView 方法

Window 的更新过程

Window 的更新过程和 Window 的添加过程是类似的。需要调用 ViewManager 的 updateViewLayout 方法,updateViewLayout 方法在 WindowManagerImpl 中实现,而 WindowManagerImpl 的 updateViewLayout 方法会调用 WindowManagerGlobal 的 updateViewLayout 方法

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

public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
  if (view == null) {
    throw new IllegalArgumentException("view must not be null");
  }
  if (!(params instanceof WindowManager.LayoutParams)) {
    throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
  }
  final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
  //1.将要更新的参数设置到 View 中
  view.setLayoutParams(wparams);
  synchronized (mLock) {
    //2.得到要更新的窗口在 View 列表中的索引
    int index = findViewLocked(view, true);
    //3.根据索引得到窗口dui'ying
    ViewRootImpl root = mRoots.get(index);
    //4.删除老的参数
    mParams.remove(index);
    //5.添加新的参数
    mParams.add(index, wparams);
    //6.将更新的参数设置到 ViewRootImpl 中
    root.setLayoutParams(wparams, false);
  }
}

最后调用了 ViewRootImpl 的 setLayoutParames 方法,ViewRootImpl方法最终会调用 ViewRootImpl scheduleTraversals 方法

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

void scheduleTraversals() {
  if (!mTraversalScheduled) {
    mTraversalScheduled = true;
    mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
    //1.将mTraversalRunnable发送到下一帧渲染执行
    mChoreographer.postCallback(
      Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    if (!mUnbufferedInputDispatch) {
      scheduleConsumeBatchedInput();
    }
    notifyRendererOfFramePending();
    pokeDrawLockIfNeeded();
  }
}

Choreographer译为舞蹈指导,用于接受显示系统的 VSync 信号,在下一个帧渲染时控制执行一些操作。Choreography 的 postCallback 方法用于发起方法回调,这个添加的回调将在下一个帧被渲染时执行。这个添加的回调将在下一帧被渲染时执行。这个添加的回调指的是注释1处的 TraversalRunable 类型的 mTraversalRunnable,如下所示

final class TraversalRunnable implements Runnable {
  @Override
  public void run() {
    //执行doTraversal
    doTraversal();
  }
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void doTraversal() {
  if (mTraversalScheduled) {
    mTraversalScheduled = false;
    mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
    if (mProfile) {
      Debug.startMethodTracing("ViewAncestor");
    }
    //执行performTraversals,使ViewTree 开始 View 的工作流程
    performTraversals();
    if (mProfile) {
      Debug.stopMethodTracing();
      mProfile = false;
    }
  }
}
private void performTraversals() {
  ...
  if (mFirst || windowShouldResize || insetsChanged ||
      viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
    ...
    try {
      ...
      //1.里面会调用 IWindowSession 的 relayout 方法来更新 Window 视图
      relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
      ...
    } catch (RemoteException e) {
    }
    ...
    if (!mStopped || mReportNextDraw) {
      ...
      if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
          || mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
          updatedConfiguration) {
        int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
        int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
        //2.内部调用 View 的 measure方法开始 View 的测量流程
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
      ...
    }
  }
  ...
  final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
  ...
  if (didLayout) {
    //3.内部调用 View 的 layout开启 View 的布局流程
    performLayout(lp, mWidth, mHeight);
    ...
  }
  ...
  if (!cancelDraw && !newSurface) {
    if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
      for (int i = 0; i < mPendingTransitions.size(); ++i) {
        mPendingTransitions.get(i).startChangingAnimations();
      }
      mPendingTransitions.clear();
    }
    //4.内部调用 View 的 draw 方法开启 View 的绘制流程
    performDraw();
  } 
  ...
}

在performTraversals 方法中更新了 Window 视图,又执行 Window 的 View 工作流程。这样就完成了 Window 的更新

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-07-04 23:03:58  更:2022-07-04 23:07:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 2:34:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码