WindowManager

news2025/1/10 16:41:56

1 Window、WindowManager 和 WMS

Window 是一个抽象类,具体的实现类为 PhoneWindow,它对 View 进行管理。WindowManager 是一个接口类,继承自接口ViewManager,它是用来管理 Window 的,它的实现类为 WindowManagerImpl。如果我们想要对 Window(View)进行添加、更新和删除操作就可以使用 WindowManager,WindowManager 会将具体的工作交由 WMS 来处理,WindowManager 和 WMS 通过 Binder 来进行跨进程通信,WMS 作为系统服务有很多 API 是不会暴露给 WindowManager 的,这一点与 ActivityManager 和 AMS 的关系有些类似。

Window、WindowManager 和 WMS 的关系可以简略的用下图来表示:

Window、WindowManager 和 WMS 的关系

Window 包含了 View 并对 View 进行管理,Window 用虚线来表示是因为 Window 是一个抽象概念,用来描述一个窗口,并不是真实存在的,Window 的实体其实也是 View。WindowManager 用来管理 Window,而 WindowManager 所提供的功能最终会由 WMS 进行处理。

2 WindowManager 的关联类

以下是相关类:

public abstract class Window { }
public class PhoneWindow extends Window implements MenuBuilder.Callback { }

public interface WindowManager extends ViewManager { }
public interface ViewManager {
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}
public final class WindowManagerImpl implements WindowManager { 
	private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
}

public final class WindowManagerGlobal { }

public class WindowManagerService extends IWindowManager.Stub
         implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs { }

WMS的继承关系

**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);

WindowManager.getDefaultDisplay() 方法能够得知这个 WindowManager 实例将 Window 添加到哪个屏幕上了,换句话说,就是的到 WindowManager 所管理的屏幕(Display)。WindowManager.removeViewImmediate 方法则固定在这个方法换回钱要立即执行 View.onDetachedFromWindow(),来完成传入的 View 的相关的销毁工作。

**Window 是一个抽象类,它的具体实现类为 PhoneWindow。在 Activity 启动过程中会调用 ActivityThread.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) {
          attachBaseContext(context);
  
          mFragments.attachHost(null /*parent*/);
  
          mWindow = new PhoneWindow(this, window); // 1
          ...
          mWindow.setWindowManager(
                  (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                  mToken, mComponent.flattenToString(),
                  (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0); // 2
          if (mParent != null) {
              mWindow.setContainer(mParent.getWindow());
          }
          mWindowManager = mWindow.getWindowManager();
          mCurrentConfig = config;
      }

在注释 1 处创建了 PhoneWindow,在注释 2 出调用了 PhoneWindow.setWindowManager 方法,这个方法在 PhoneWindow 的父类 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) {
        wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); // 1
    }
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); // 2
}

如果传入的 WindowManager == null ,就会在注释 1 处调用 Context.getSystemService 方法,并传入服务名称 Context.WINDOW_SERVICE(值为 window),具体在 ContextImpl 中实现,如下所示:

// /frameworks/base/core/java/android/app/ContextImpl.java
@Override
public Object getSystemService(String name) {
    return SystemServiceRegistry.getSystemService(this, name);
}

ContextImpl.getSystemService 方法中会调用 SystemServiceRegister.getSystemService 方法:

// /frameworks/base/core/java/android/app/SystemServiceRegistry.java
private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
             new HashMap<String, ServiceFetcher<?>>();
private static final HashMap<Class<?>, String> SYSTEM_SERVICE_NAMES =
              new HashMap<Class<?>, String>();

public static Object getSystemService(ContextImpl ctx, String name) {
    ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    return fetcher != null ? fetcher.getService(ctx) : null;
}

static abstract interface ServiceFetcher<T> {
    T getService(ContextImpl ctx);
}

SYSTEM_SERVICE_FETCHERS 是一个 HashMap 类型的数据,它用来存储服务的名称,那么传入的 Context.WINDOW_SERVICE 到底对应着什么?接着往下看:

// /frameworks/base/core/java/android/app/SystemServiceRegistry.java
private SystemServiceRegistry() { }

static {
    ...
    registerService(Context.WINDOW_SERVICE, WindowManager.class,
                    new CachedServiceFetcher<WindowManager>() {
                        @Override
                        public WindowManager createService(ContextImpl ctx) {
                            return new WindowManagerImpl(ctx); // 1
                        }});
    ...
}

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

在 SystemServieRegistry 的静态代码块中会调用多个 registerService 方法。registerService 方法内部会将传入的服务的名称存入到 SYSTEM_SERVICE_FETCHERS 中。从注释 1 处可以看出,传入的 Context.WINDOW_SERVICE 对应的就是 WindowManagerImpl 实例,因此得出结论,Context.getSystemService 方法得到的是 WindowManagerImpl 实例。再回到 Window.setWindowManager 方法,在注释 1 处得到 WindowManagerImpl.createLocalWindowManager 方法:

// /frameworks/base/core/java/android/view/WindowManagerImpl.java
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
    return new WindowManagerImpl(mContext, parentWindow);
}

WindowManagerImpl.createLocalWindowManager 方法同样也是创建 WindowManagerImpl,不同的是这次创建 WindowManagerImpl 时将创建它的 Window 作为参数传了进来,这样 WindowManagerImpl 就持有了 Window 的引用,可以对 Window 进行操作,比如在 Window 中天添加 View,会调用 WindowManagerImpl.addView 方法,如下所示:

// /frameworks/base/core/java/android/view/WindowManagerImpl.java
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); // 1
}

在注释 1 处调用了 WindowManagerGlobal.addView 方法,其中最后一个参数 mParentWindow 就是上面提到的 Window,可以看出 WindowManagerImpl 虽然是 WindowManager 的实现类,但是没有实现什么功能,而是将功能实现委托给了 WindowManagerGlobal,这里用到的是桥接模式。下面来看一下 WindowManagerImpl 中时如何定义 WindowMangerGlobal 的,如下所示:

// /frameworks/base/core/java/android/view/WindowManagerImpl.java
public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); // 1
    private final Context mContext;
    private final Window mParentWindow; // 2

    ...

    private WindowManagerImpl(Context context, Window parentWindow) {
        mContext = context;
        mParentWindow = parentWindow; // 3
    }
    ...
}

在注释 1 处可以看出 WindowManagerGlobal 是一个单例,说明在一个进程中只有一个 WindowManagerGlobal 实例。注释 2 处的代码结合注释 3 处的代码说明这个 WindowManagerImpl 实例会作为哪个 Window 的子 Window,这也就说明在一个进程中 WinodwManagerImpl 可能会有多个实例。

以下是时序图:

WindowManager时序图

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

3 Window 的操作

WindowManager 对 Window 进行管理,比如 Window 的添加、更新和删除操作。对于这些操作,最终都是交由 WMS 来进行处理的。窗口的操作分为两大部分,一部分是 WindowManager 处理部分,另一部分是 WMS 处理部分。

Window 分为三大类,分别是 Application Window(应用程序窗口)、Sub Window(子窗口)、System Window(系统窗口),对于不同类型的窗口添加过程会有所不同,但是对 WMS 处理部分,添加的过程基本上是一样的,WMS 对于这三大类的窗口基本是“一视同仁”的:

Window操作

3.1 系统窗口的添加过程

Window 分为三大类型,不同类型的 Window 的添加过程也不尽相同,这里主要讲系统窗口的添加过程。系统窗口的添加过程也会根据不同的类型有所区别,这里主要是以系统窗口 StatusBar 为例,StatusBar 是 SystemUI 的重要组成部分,具体就是指系统状态栏,用于显示时间、电量和信号等信息。

首先来看 StatusBar.addStatusBarWindow() 方法,这个方法负责为 StatusBar 添加 Window,如下所示:

// /frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
private void addStatusBarWindow() {
    makeStatusBarView(); // 1
    mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
    mRemoteInputController = new RemoteInputController(mHeadsUpManager);
    mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight()); // 2
}

在注释 1 处用于构建 StatusBar 的视图。在注释 2 处调用了 StatusBarWindowManager.add 方法,并将 StatusBar 的视图(mStatusBarWindow)和 StatusBar 的高度传进去,StatusBarWindowManager.add 方法如下所示:

// /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,
        WindowManager.LayoutParams.TYPE_STATUS_BAR, // 1
        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;
    mWindowManager.addView(mStatusBarView, mLp); // 2
    mLpChanged = new WindowManager.LayoutParams();
    mLpChanged.copyFrom(mLp);
}

首先通过创建 LayoutParams 来配置 StatusBar 视图的属性,包括 WidthHeightTypeFlagGravitySoftInputMode 等。关键在注释 1 处,设置了 TYPE_STATUS_BAR,表示 StatusBar 视图的窗口类型是状态栏。在注释 2 处调用了 WindowManager.addView 方法,addView 方法定义在 WindowManager 的父类接口 ViewManager 中,而 addView 方法则是在 WindowManagerImpl 中实现的,如下所示:

// /frameworks/base/core/java/android/view/WindowManagerImpl.java
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

WindowManagerImpl.addView 方法的第一个参数类型为 View,说明窗口也是以 View 的形式存在的。WindowManagerImpl.addView 方法会调用 WindowManagerGlobal.addView 方法,如下所示:

// /frameworks/base/core/java/android/view/WindowManagerGlobal.java
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<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); // 1
    } else {
        // If there's no parent, then hardware acceleration for this view is
        // set from the application's hardware acceleration setting.
        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); // 2
        view.setLayoutParams(wparams);
        mViews.add(view); // 3
        mRoots.add(root); // 4
        mParams.add(wparams); // 5
        try {
            root.setView(view, wparams, panelParentView); // 6
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            if (index >= 0) {
                removeViewLocked(index, true);
            }
            throw e;
        }
    }
}

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

接着分析 WindowManagerGlobal.addView 方法,首先会对参数 viewparamsdisplay 进行检查。在注释 1 处,如果当前窗口作为子窗口,就会根据父窗口对子窗口的 WindowManager.LayoutParams 类型的 wparams 对象进行相应调整。在注释 3 处将添加的 View 保存到 View 列表中。在注释 5 处将窗口的参数保存到布局参数列表中。在注释 2 处创建了 ViewRootImpl 并赋值给 root,紧接着在注释 4 处将 root 存入到 ViewRootImpl 列表中。在注释 6 处将窗口和窗口的参数通过 setView 方法设置到 ViewRootImpl 中,可见添加窗口这一操作是通过 ViewRootImpl 来进行的。

ViewRootImpl 有很多的职责,主要有以下几点:

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

了解了 ViewRootImpl 的职责后,接着来看 ViewRootImpl.setView 方法:

// /frameworks/base/core/java/android/view/ViewRootImpl.java
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);
            } 
        ...
}

ViewRootImpl.setView 方法中主要是调用了 mWindowSession.addToDisplay 方法,mWindowSessionIWindowSession 类型的,它是一个 Binder 对象,用于进行进程间通信,IWindowSessionClient 端的代理,它的 Server 端的实现为 Session ,此前的代码逻辑都是运行在本地进程的,而 Session.addToDisplay 方法则运行在 WMS 所在的进程(system_server 进程),如下所示:

ViewRootImpl

从上图中可以看出,本地进程的 ViewRootImpl 想要和 WMS 进行通行需要 Session ,那么 Session 为何包含在 WMS 中呢?接着往下看 Session.addToDisplay 方法,如下所示:

// /frameworks/base/telecomm/java/android/telecom/Logging/Session.java
@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);
}

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

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

StatusBar时序图

3.2 Activity 的添加过程

无论是哪种窗口,它的添加过程在 WMS 处理部分中基本是类似的,只不过会在权限和窗口显示次序等方面有些不同。但是在 WindowManager 处理部分会有所不同,以最典型的应用程序窗口 Activity 为例,Activity 在启动过程中,如果 Activity 所在的进程不存在则会创建新的进程,创建新的进程之后就会运行代表主线程的实例 ActivityThreadActivityThread 管理着当前应用程序进程的线程,这在 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) {
    ...
    r = performResumeActivity(token, clearHide, reason); // 1
        ...
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager(); // 2
            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;
                    wm.addView(decor, l); // 3
                } else {
                    a.onWindowAttributesChanged(l);
                }
            }

           ...
               
}

注释 1 处的 performResumeActivity 方法最终会调用 Activity.onResume 方法。在注释 2 处得到 ViewManager 类型的 wm 对象,在注释 3 处调用了 ViewManager.addView 方法,而 addView 方法是在 WindowManagerImpl 中实现的,此后的过程在上面的系统窗口 StatusBar 的添加过程中已经讲述过,唯一需要注意的是 ViewManager.addView 方法的第一个参数是 DecorView,这说明 Activity 窗口中会包含 DecorView

3.3 Winodow 的更新过程

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;

    view.setLayoutParams(wparams); // 1

    synchronized (mLock) {
        int index = findViewLocked(view, true); // 2
        ViewRootImpl root = mRoots.get(index); // 3
        mParams.remove(index); // 4
        mParams.add(index, wparams); // 5
        root.setLayoutParams(wparams, false); // 6
    }
}

注释 1 处将更新的参数设置到 View 中,注释 2 处得到要更新的窗口在 View 列表中的索引,注释 3 处在 ViewRootImpl 列表中根据索引得到窗口 ViewRootImpl ,注释 4 处和注释 5 处用于更新布局参数列表,注释 6 处调用 ViewRootImpl.setLayoutParams 方法将更新的参数设置到 ViewRootImpl 中。ViewRootImpl.setLayoutParams 方法在最后会调用 ViewRootImpl.scheduleTraveesals 方法,如下所示:

// /frameworks/base/core/java/android/view/ViewRootImpl.java
void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
            Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); // 1
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

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

// /frameworks/base/core/java/android/view/ViewRootImpl.java
final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}

TraversalRunnable.run() 方法中调用 doTraversal() 方法,如下所示:

// /frameworks/base/core/java/android/view/ViewRootImpl.java
void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

        if (mProfile) {
            Debug.startMethodTracing("ViewAncestor");
        }

        performTraversals();

        if (mProfile) {
            Debug.stopMethodTracing();
            mProfile = false;
        }
    }
}

doTraversal 方法中又调用了 performTraversals() 方法,performTraversals() 方法使得 ViewTree 开始 View 的工作流程,如下所示:

// /frameworks/base/core/java/android/view/ViewRootImpl.java
private void performTraversals() {
        try {
            ...
            relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); // 1
			...
        } catch (RemoteException e) {
        }

        if (!mStopped) {
            ...
            int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
            int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); // 2
        }
            
	...
    final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
    if (didLayout) {
        performLayout(lp, mWidth, mHeight); // 3
		,,,
    }

    ...
    boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;

    if (!cancelDraw && !newSurface) {
        if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
            for (int i = 0; i < mPendingTransitions.size(); ++i) {
                mPendingTransitions.get(i).startChangingAnimations();
            }
            mPendingTransitions.clear();
        }

        performDraw(); // 4
    } 
    ...
}

注释 1 处的 relayoutWindow 方法内部会调用 IWindowSessionrelayout 方法来更新 Window视图,和 3.1 的原理是一样的,最终会调用 WMSrelayoutWindow 方法。除此之外,performTraversals 方法还会在注释 2、3、4 处分别调用 performMeasureperformLayoutperformDraw 方法,它们内部又会调用 Viewmeasurelayoutdraw 方法,这就完成了 View 的工作流程。在 performTraversals 方法中更新了 Window 视图,又执行了 Window 中的 View 的工作流程,这样就完成了 Window 的更新。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/146410.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[Leetcode] 二叉树的深度、平衡二叉树

题目链接&#xff1a;二叉树的最大深度 https://leetcode.cn/problems/maximum-depth-of-binary-tree/submissions/二叉树的最小深度 https://leetcode.cn/problems/minimum-depth-of-binary-tree/平衡二叉树 https://leetcode.cn/problems/balanced-binary-tree1.二叉树的最大…

微信小程序实现上下左右滑动触发联动选项卡、绝对值、事件、parse、stringify、Math、atan、abs、findIndex

文章目录序言1、HTML部分2、JavaScript部分&#xff08;上下左右滑动均触发&#xff09;3、JavaScript部分&#xff08;左右滑动触发&#xff09;4、效果演示序言 最近在写原生微信小程序项目的时候遇到了左右滑动内容更新数据&#xff0c;同时改变tabBar的高亮效果。于是就写了…

B. Camp Schedule(KMPnext数组)

Problem - 1137B - Codeforces 在全国范围内广为人知的春季编程训练营即将开始。因此&#xff0c;所有友好的策展人和教师团队开始组成营地的时间表。经过不断的讨论&#xff0c;他们想出了一个时间表&#xff0c;可以表示为一个二进制字符串&#xff0c;其中第i个符号是 "…

前后端的身份认证

1、Web 开发模式 目前主流的 Web 开发模式有两种&#xff0c;分别是&#xff1a; 基于服务端渲染的传统 Web 开发模式基于前后端分离的新型 Web 开发模式 1.1、服务端渲染的 Web 开发模式 服务端渲染的概念&#xff1a;服务器发送给客户端的 HTML 页面&#xff0c;是在服务器…

【Linux】进程间通信(万字详解) —— 下篇

&#x1f387;Linux&#xff1a; 博客主页&#xff1a;一起去看日落吗分享博主的在Linux中学习到的知识和遇到的问题博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a; 看似不起波澜的日复一日&#xff0c;一定会在某一天让你看见坚持…

我的周刊(第073期)

我的信息周刊&#xff0c;记录这周我看到的有价值的信息&#xff0c;主要针对计算机领域&#xff0c;内容主题极大程度被我个人喜好主导。这个项目核心目的在于记录让自己有印象的信息做一个留存以及共享。&#x1f3af; 项目zlib-searcher[1]zlib 开源搜索方案&#xff08;zli…

基于朴素贝叶斯算法的激光雷达点云分类

前言激光雷达技术是一种采集三维数据的、重建三维模型的手段&#xff0c;运用在各个行业&#xff0c;随着激光雷达技术的发展与广泛运用本文采用监督分类中的朴素贝叶斯算法进行地基于激光雷达的地物分类。首先根据点云的几何位置建立邻域范围&#xff0c;借助邻域点的集合计算…

分享98个PHP源码,总有一款适合您

PHP源码 分享98个PHP源码&#xff0c;总有一款适合您 PHP源码下载链接&#xff1a;https://pan.baidu.com/s/1ZNcdj0bLY51UXNoXq8tgFg?pwdwn4b 提取码&#xff1a;wn4b 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0…

ORB-SLAM2 --- LoopClosing::ComputeSim3 函数

目录 1.什么是sim3&#xff0c;为什么要做sim3 2.函数流程 3.code 4.函数解析 4.1 准备工作 4.2 遍历闭环候选帧集&#xff0c;初步筛选出与当前关键帧的匹配特征点数大于20的候选帧集合&#xff0c;并为每一个候选帧构造一个Sim3Solver 4.3 对每一个候选帧用Sim3Sol…

实例分析Linux内存泄漏检测方法

一、mtrace分析内存泄露 mtrace&#xff08;memory trace&#xff09;&#xff0c;是 GNU Glibc 自带的内存问题检测工具&#xff0c;它可以用来协助定位内存泄露问题。它的实现源码在glibc源码的malloc目录下&#xff0c;其基本设计原理为设计一个函数 void mtrace ()&#x…

解决Tinkphp的success跳转“使用路由别名后模块和路由器访问不了”问题

遇到的问题&#xff1a;我的thinkphp5网站添加了以下路由别名&#xff1a;Route::alias([ index>index/index, ]);使用http://域名/Index/user/password.html访问正常但使用http://域名/index/user/password.html就访问失败使用$this->success(修改密码成功);进行提示跳转…

Java 链表与LinkedList

链表的组合形式 ①有头结点、无头结点 ②单向链表、双向链表 ③循环链表、非循环链表 根据自由组合&#xff0c;可以得到8种不同形式的链表&#xff0c;那么在刷题种常碰到的是不带有头结点的单向非循环链表和不带头结点的双向非循环链表。 模拟实现不带头结点的单向非循环链表…

Notes可以手动签名了

大家好&#xff0c;才是真的好。 Notes/Domino 12.0.2陆续有人下载测试了&#xff0c;关于Notes的新功能中&#xff0c;我们上一篇也介绍到了可以手动签名。 字面上的意思&#xff0c;就是你可以调出手写板&#xff0c;然后使用触屏或鼠标来进行签名&#xff0c;可以在Nomad …

javaEE 初阶 — 定时器

文章目录定时器1 什么是定时器2 标准库中定时器3 实现一个定时器3.1 实现的思路3.2 为什么要使用优先级队列来保存任务3.3 开始实现定时器 1 什么是定时器 定时器 类似于一个 “闹钟”&#xff0c;达到一个设定的时间之后&#xff0c;就执行某个指定好的代码。 定时器是一种实…

印染行业APS智能排程排产的应用意义

不得不说的印染之“痛” 在印染行业&#xff0c;因排产无法自动化、智能化&#xff0c;企业在交期、成本、生产管理方面承受着巨大的压力&#xff0c;尤其当下印染企业生产管理正从传统的粗放式转向精细化&#xff0c;这些痛点愈加凸显。 一方面&#xff0c;客户和企业面临一个…

httpd安装

一、离线安装 1、去 https://pkgs.org/ 下载httpd所依赖的7个rpm包 [基于CentOS 7 x86_64系统&#xff0c;如需其他环境可前往官网直接下载] apr-1.4.8-5.el7.x86_64.rpm apr-util-1.5.2-6.el7.x86_64.rpm apr-util-ldap-1.5.2-6.el7.x86_64.rpm postgresql-libs-9.2.24-1.el…

互联互通-标准化成熟度指标分析(未完成)

整体分析1 医疗机构基本情况2 数据资源标准化建设情况&#xff08;30 分&#xff09;2.1数据集标准化情况&#xff08;15 分&#xff09;2.1.1电子病历基本数据集 第1部分&#xff1a;病历概要&#xff08;1-4数据集&#xff09;2.1.2电子病历基本数据集 第2部分&#xff1a;门…

Jetpack Compose UI创建布局绘制流程+原理 —— 内含概念详解(手撕源码)

本文是我去年首发于稀土掘金平台的文章 全文较长&#xff1a;共1万5千字&#xff0c;适合有耐心❤️的人学习 有些概念不懂的可以去4.部分概念详解这个目录先稍微学习一下 Compose源码基于最新的Compose 版本&#xff1a;1.0.1 系统源码基于最新的Android11 版本 注意&#xff…

【蓝桥杯基础题】2020年省赛填空题—回文日期

&#x1f451;专栏内容&#xff1a;蓝桥杯刷题⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录一、题目背景二、题目描述1.问题描述2.输入格式3.输出格式4.一个例子5. 评测用例规模与约定三、题目分析1.获取位数2.回文…

236页10万字精选数据中台建设方案2022版

【版权声明】本资料来源网络&#xff0c;知识分享&#xff0c;仅供个人学习&#xff0c;请勿商用。【侵删致歉】如有侵权请联系小编&#xff0c;将在收到信息后第一时间删除&#xff01;完整资料领取见文末&#xff0c;部分资料内容&#xff1a; 目录 1. 数据中台平台建设方案 …