理解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) {
...
mWindow = new PhoneWindow(this, window, activityConfigCallback);
...
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) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
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) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
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,
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);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
在注释1处调用了WindowManagerGlobal的addView方法,其中最后一个参数mParentWindow就是上面创建WindowManagerImpl时传入的Window。
可以看出WindowManagerImpl虽然是WindowManager的实现类,但是没有实现什么功能,而是将功能实现委托给了WindowManagerGlobal。这里用到的是桥接模式
我们再看一下WindowManagerImpl中如何定义WindowManagerGlobal
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Context mContext;
private final Window mParentWindow;
...
private WindowManagerImpl(Context context, Window parentWindow) {
mContext = context;
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(系统窗口),每个大类型中又包含了很多类型
-
应用程序窗口 Activity就是一个典型的应用程序窗口,应用程序窗口包含的类型如下 /frameworks/base/core/java/android/view/WindowManager.java
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,这个数值的大小涉及窗口的层级 -
子窗口 子窗口不能独立存在,需要附着在其他窗口才可以,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;
-
系统窗口 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;
public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;
public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;
public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;
...
public static final int LAST_SYSTEM_WINDOW = 2999;
系统窗口的type值范围为2000~2999 -
窗口显示次序 当一个进程向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() {
makeStatusBarView();
mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
mRemoteInputController = new RemoteInputController(mHeadsUpManager);
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,
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;
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);
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;
if (parentWindow != null) {
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) {
...
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
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();
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) {
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) {
...
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);
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;
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;
view.setLayoutParams(wparams);
synchronized (mLock) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
mParams.remove(index);
mParams.add(index, wparams);
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();
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();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
private void performTraversals() {
...
if (mFirst || windowShouldResize || insetsChanged ||
viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
...
try {
...
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);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
}
}
...
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
...
if (didLayout) {
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();
}
performDraw();
}
...
}
在performTraversals 方法中更新了 Window 视图,又执行 Window 的 View 工作流程。这样就完成了 Window 的更新
|