WindowManager

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 的更新。

猜你喜欢

转载自blog.csdn.net/xingyu19911016/article/details/128518822