简述
WindowManagerService,Android的窗口管理,WMS错综复杂,与 ActivityManagerService、InputManagerService、SurfaceFlinger关系也很紧密,本文深入探讨一下WMS的管理设计,以便理解View、Activity、Window、Task等之间的关系。
首先还是从WindowManagerService服务的启动开始,在Android开机流程中,我们知道WMS是在引导服务和核心服务启动之后才会开始的:
private void startOtherServices(@NonNull TimingsTraceAndSlog t){
...
t.traceBegin("StartWindowManagerService");
// WMS needs sensor service ready
mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE);
wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
*/
wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
mSystemServerExt.getSubPhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
t.traceEnd();
t.traceBegin("SetWindowManagerService");
mActivityManagerService.setWindowManager(wm);
t.traceEnd();
t.traceBegin("WindowManagerServiceOnInitReady");
wm.onInitReady();
t.traceEnd();
}
这里眼尖的同学发现了,WMS的启动必须等待SensorService的启动完毕,为什么呢,后面给出答案
WSM启动
从上面也看出了,WMS的启动是先调用了其静态方法main
, 并传入context
, inputManager
及ActivityTaskManager
等,还有一个WindowManagerPolicy
子类PhoneWindowManager
的对象
WindowManagerService.main
public static WindowManagerService main(final Context context,final
InputManagerService im,
final boolean showBootMsgs,final boolean onlyCore,
WindowManagerPolicy policy,
ActivityTaskManagerService atm){
return main(context,im,showBootMsgs,onlyCore,policy,atm,
SurfaceControl.Transaction::new,Surface::new,
SurfaceControl.Builder::new);
}
/**
* Creates and returns an instance of the WindowManagerService. This call
* allows the caller
* to override factories that can be used to stub native calls during test.
*/
@VisibleForTesting
public static WindowManagerService main(final Context context,final
InputManagerService im,
final boolean showBootMsgs,final boolean onlyCore,
WindowManagerPolicy policy,
ActivityTaskManagerService atm,
Supplier<SurfaceControl.Transaction>transactionFactory,
Supplier<Surface> surfaceFactory,
Function<SurfaceSession, SurfaceControl.Builder>
surfaceControlFactory){
// 使用"android.display"线程初始化WindowManagerService
// 初始化没有结束时,调用main的线程会被阻塞
DisplayThread.getHandler().runWithScissors(()->
sInstance=new WindowManagerService(context,im,showBootMsgs,
onlyCore,policy,
atm,transactionFactory,surfaceFactory,
surfaceControlFactory),0);
return sInstance;
}
这⾥使⽤的双冒号"::"、Supplier
以及Function
都是java8引⼊的函数式编程语法。值得注意的是runWithScissors
⽅法。
DisplayThread.getHandler
public final class DisplayThread extends ServiceThread {
private static DisplayThread sInstance;
private static Handler sHandler;
private DisplayThread() {
// DisplayThread runs important stuff, but these are not as
important as things running in
// AnimationThread. Thus, set the priority to one lower.
super("android.display", Process.THREAD_PRIORITY_DISPLAY + 1, false
/*allowIo*/);
}
private static void ensureThreadLocked() {
if (sInstance == null) {
sInstance = new DisplayThread();
sInstance.start();
sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
sHandler = new Handler(sInstance.getLooper());
}
}
public static Handler getHandler() {
synchronized (DisplayThread.class) {
ensureThreadLocked();
return sHandler;
}
}
...
}
由此可以看出DisplayThread其实就是Android⾥的"android.display"线程,优先级仅仅⽐THREAD_PRIORITY_DISPLAY低⼀档。
Handler.runWithScissors
/**
* @hide
*/
public final boolean runWithScissors(@NonNull Runnable r, long timeout) {
if (r == null) {
throw new IllegalArgumentException("runnable must not be null");
}
if (timeout < 0) {
throw new IllegalArgumentException("timeout must be non-negative");
}
if (Looper.myLooper() == mLooper) {
r.run();
return true;
}
BlockingRunnable br = new BlockingRunnable(r);
return br.postAndWait(this, timeout);
}
这⾥可以看到该⽅法⾸先检查了参数合法性,然后判断当前允许的线程是否是Handler处理线程:
- 如果是
Handler
处理线程,则执⾏runnable
- 如果不是,则创建⼀个
BlockingRunnable
,执⾏其postAndWait
⽅法
从postAndWait
名字中可以推测,必定是需要等待指定runnable
在Handler
处理线程上执⾏完毕后,调⽤
runWithScissors
的线程才能继续运⾏。
private static final class BlockingRunnable implements Runnable {
private final Runnable mTask;
private boolean mDone;
public BlockingRunnable(Runnable task) {
mTask = task;
}
@Override
public void run() {
try {
mTask.run();
} finally {
// runnable执行完毕后标记mDone为true,然后通知所以等待线程
synchronized (this) {
mDone = true;
notifyAll();
}
}
}
public boolean postAndWait(Handler handler, long timeout) {
// 当handler中已经存在此runnable时,返回false
if (!handler.post(this)) {
return false;
}
synchronized (this) {
if (timeout > 0) {
final long expirationTime = SystemClock.uptimeMillis() +
timeout;
while (!mDone) {
long delay = expirationTime -
SystemClock.uptimeMillis();
if (delay <= 0) {
return false; // timeout
}
try {
// 等待delay时长
wait(delay);
} catch (InterruptedException ex) {
}
}
} else {
while (!mDone) {
try {
wait();
} catch (InterruptedException ex) {
}
}
}
}
return true;
}
}
所以runWithScissors
⽅法为啥会标记为hide呢,明明很好的实现了将某个任务放到指定线程执⾏并等待该任务
执⾏完毕后继续。
- 考虑到如果等待超时,
postAndWait
返回的也是false,但是对应runnable
的message
仍旧处于Handler的
MessageQueue
之中,这样该runnable
最终还是会被执⾏的。 - 可能造成死锁,因为
runWithScissors
所处线程会⼀直等待,除⾮超时。⽐如任务所处的handler的
Looper被调⽤了quit()
退出时。我们知道quit⽅法是会清理Looper的MessageQueue
中所有消息,如果
此时timeout的时间设置的是0,那么runWithScissors
⽅法所处的线程会⼀直等到天荒地⽼。除⾮Looper
退出时是调⽤的quitSafely()
, 该⽅法只会清空MessageQueue
中当前时间点之后的message,在这之前的
message仍会被执⾏。
⽽我们知道,DisplayThread
代表线程是不会退出的(除⾮关机), 所以这⾥调⽤runWithScissors
是安全的。
private WindowManagerService(Context context,InputManagerService
inputManager,
boolean showBootMsgs,boolean onlyCore,WindowManagerPolicy policy,
ActivityTaskManagerService atm,
Supplier<SurfaceControl.Transaction>transactionFactory,
Supplier<Surface> surfaceFactory,
Function<SurfaceSession, SurfaceControl.Builder>
surfaceControlFactory){
// 在LockGuard中创建代表WINDOW的Lock, 可以帮助检测系统服务内部锁的机制
installLock(this,INDEX_WINDOW);
...
// 标记当前设备是否是可触摸的
mInTouchMode=context.getResources().getBoolean(
com.android.internal.R.bool.config_defaultInTouchMode);
// 设置是否允许触摸,现在手机、平板设备一般都是可触摸的
inputManager.setInTouchMode(mInTouchMode);
...
// 通过surfaceControlFactory可以直接生成SurfaceControl.Builder的对象
mSurfaceControlFactory=surfaceControlFactory;
// 其实是实现了Supplier接口的SurfaceControl.Transaction::new
mTransactionFactory=transactionFactory;
// 其实是实现了Supplier接口Surface::new
mSurfaceFactory=surfaceFactory;
// 创建Transaction对象, 注意每次调用get方法生成的都是新的
SurfaceControl.Transaction对象
mTransaction=mTransactionFactory.get();
// 这个policy就是PhoneWindowPolicy
mPolicy=policy;
// 创建WindowAnimator
mAnimator=new WindowAnimator(this);
// 创建RootWindowContainer
mRoot=new RootWindowContainer(this);
...
// 創建WindowSurfacePlacer,如其名,用於定位窗口及其表面
// 通过计算窗口的框架来设置窗口的位置,然后根据这些框架来定位Surface
mWindowPlacerLocked=new WindowSurfacePlacer(this);
// 創建TaskSnapshotController,用於获取相应任务的快照(位图)并将其放入缓存
mTaskSnapshotController=new TaskSnapshotController(this);
...
// 创建WakeLock,用于在转屏等动作时保持cpu不休眠
mScreenFrozenLock=mPowerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,"SCREEN_FROZEN");
mScreenFrozenLock.setReferenceCounted(false);
// 用于管理监听Display的层次属性变化的监听器
mDisplayNotificationController=new
DisplayWindowListenerController(this);
...
// 创建WakeLock,在必要时保持屏幕常量
mHoldingScreenWakeLock=mPowerManager.newWakeLock(
PowerManager.SCREEN_BRIGHT_WAKE_LOCK|
PowerManager.ON_AFTER_RELEASE,TAG_WM);
mHoldingScreenWakeLock.setReferenceCounted(false);
// 用于执行不需要持有WindowManager的锁的窗口动画
mSurfaceAnimationRunner=new
SurfaceAnimationRunner(mTransactionFactory,
mPowerManagerInternal);
...
// 用于任务拖动定位的控制器
mTaskPositioningController=new TaskPositioningController(
this,mInputManager,mActivityTaskManager,mH.getLooper());
// 该类处理并组合从多个视图中生成的拖动事件,然后向任何已经注册了回调的ondragdroplistener触发事件
mDragDropController=new DragDropController(this,mH.getLooper());
// 用于强制显示高刷新率的包的Denylist
mHighRefreshRateDenylist=
HighRefreshRateDenylist.create(context.getResources());
...
// 创建DisplayAreaPolicyProvider,用于后续的DisplayAreaPolicy的构建
mDisplayAreaPolicyProvider=DisplayAreaPolicy.Provider.fromResources(
mContext.getResources());
...
// 设置全局阴影
setGlobalShadowSettings();
mAnrController=new AnrController(this);
// 管理创建和释放启动窗口Surface
mStartingSurfaceController=new StartingSurfaceController(this);
}
这⾥省略的很多设置代码,仅贴了⼀些⽐较重要或者有趣的类对象初始化。
Activity的Window创建
对于每个应用,Activity在onCreate⽅法中setContentView设置了UI布
局,之后在该Activity第⼀次resume时,初始化ViewRootImpl
,当该Activity是应⽤进程第⼀个被resume的Activity时,建⽴进程与WMS的通信通道,也就是WindowSession
最后ViewRootImpl的setView⽅法中,通过binder调⽤WindowManager的addToDisplay,之后进⼊到SystemServer进程调⽤到WMS.addWindow,在这⾥创建该Activity对应的WindowState,并保存在mWindowMap中。
如下即是WMS中addWindow的关键代码:
public int addWindow(Session session,IWindow client,LayoutParams
attrs,int viewVisibility,
int displayId,int requestUserId,InsetsState
requestedVisibility,
InputChannel outInputChannel,InsetsState outInsetsState,
InsetsSourceControl[]outActiveControls){
......
synchronized (mGlobalLock){
......
final DisplayContent displayContent=
getDisplayContentOrCreate(displayId,attrs.token);
......
ActivityRecord activity=null;
final boolean hasParent=parentWindow!=null;
WindowToken token=displayContent.getWindowToken(
hasParent?parentWindow.mAttrs.token:attrs.token);
final int rootType=hasParent?parentWindow.mAttrs.type:
type;
boolean addToastWindowRequiresToken=false;
final IBinder windowContextToken=attrs.mWindowContextToken;
if(token==null){
if(hasParent){
......
}else{
// 一般走这里创建WindowToken
final IBinder binder=attrs.token!=null?
attrs.token:client.asBinder();
token=new WindowToken(this,binder,type,false,
displayContent,
session.mCanAddInternalSystemWindow,
isRoundedCornerOverlay);
}
}
...
// 初始化WindowState
final WindowState win=new WindowState(this,session,client,
token,parentWindow,
appOp[0],attrs,viewVisibility,session.mUid,userId,
session.mCanAddInternalSystemWindow);
......
// 创建SurfaceSession, 用来和SurfaceFlinger通信
win.attach();
// 保存WindowState
mWindowMap.put(client.asBinder(),win);
......
win.mToken.addWindow(win);
......
......
}
我们可以看到WMS中⼏个关键的类:WindowState
,WindowToken
,ActivityRecord
等。之前我们就绘制过
这个类图:
1. WindowToken: 是在WindowManagerService 中定义的⼀个基类,顾名思义,它是⽤来标识某⼀个窗
⼝。可以把WindowToken看成是⼀个显⽰令牌,⽆论是系统窗⼝还是应⽤窗⼝,添加新的窗⼝时需要使
⽤这个令牌向WMS表明⾃⼰的⾝份,添加窗⼝(addWindow)时会创建WindowToken,销毁窗⼝的时候
移除WindowToken(removeWindowToken⽅法)。
2. ActivityRecord(原AppWindowToken): 顾名思义,它是⽤来标识app, 跟准确的说法,是⽤来标识某个具
体的Activity. App每个的Activity对应⼀個ActivityRecord。其中的appToken為IApplicationToken.Stub
类型,有了它就可以找到对应的ActivityRecord.
从上⾯的关系图也能发现,这个WindowToken中token是关键点,我们尚未理清这个值的含义,接下来我们分析⼀下这个token的来源:
PhoneWindow的创建
简单回顾⼀下 Activity 对应的 PhoneWindow的创建,注意这⾥仅仅是App进程中的Window.
我们从handleLaunchActivity开始:
public Activity handleLaunchActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, Intent customIntent) {
......
final Activity a = performLaunchActivity(r, customIntent);
......
}
performLaunchActivity
private Activity performLaunchActivity(ActivityClientRecord r, Intent
customIntent) {
ActivityInfo aInfo = r.activityInfo;
......
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
// 通过反射生成Activity
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
......
}
try {
// 创建应用Application
Application app = r.packageInfo.makeApplication(false,
mInstrumentation);
......
if (activity != null) {
......
// 这里传入的window一般是null的
activity.attach(appContext, this, getInstrumentation(),
r.token,
r.ident, app, r.intent, r.activityInfo, title,
r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window,
r.configCallback,
r.assistToken, r.shareableActivityToken);
......
synchronized (mResourcesManager) {
// 将该Activity保存至mActivities集合中,key值就是
ActivityClientRecord中的token
mActivities.put(r.token, r);
}
}
......
}
每次调⽤performLaunchActivity
就⼀定会创建⼀个新的Activity
对象,并保存在mActivities
集合中。
Activity.attach
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,
IBinder assistToken,
IBinder shareableActivityToken) {
......
// 创建Activity对应的PhoneWindow
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(mWindowControllerCallback);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode !=
WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
......
// 设置PhoneWindow的WindowManager
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
// 当Activity的parent不为空时,该Activity的PhoneWindow对应的容器就是其parent对应的窗口
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
......
}
可以看到⼀个Activity对应⼀个PhoneWindow
, ⽽且PhoneWindow
是在Activity launch的时候才创建的。
WindowToken中token的作⽤
我们假设添加window的Activity是该应⽤第⼀个被resume的activity:
//WMS.addWindow函数中:
final IBinder binder = attrs.token != null ? attrs.token :
client.asBinder();
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
// WindowToken.java
WindowToken(WindowManagerService service, IBinder _token, int type, boolean
persistOnEmpty,
DisplayContent dc, boolean ownerCanManageAppTokens, boolean
roundedCornerOverlay) {
this(service, _token, type, persistOnEmpty, dc,
ownerCanManageAppTokens,
roundedCornerOverlay, false /* fromClientToken */, null /*
options */);
}
WindowToken(WindowManagerService service, IBinder _token, int type, boolean
persistOnEmpty,
DisplayContent dc, boolean ownerCanManageAppTokens, boolean
roundedCornerOverlay,
boolean fromClientToken, @Nullable Bundle options) {
super(service);
token = _token;
windowType = type;
mOptions = options;
mPersistOnEmpty = persistOnEmpty;
mOwnerCanManageAppTokens = ownerCanManageAppTokens;
mRoundedCornerOverlay = roundedCornerOverlay;
mFromClientToken = fromClientToken;
if (dc != null) {
dc.addWindowToken(token, this);
}
}
从WindowToken
的构造函数和其初始化的传参中可以看到,WindowToken
中的token就是attrs的token,或者是client(IWindow)
转换来的。
LayoutParams中的token
溯源addWindow,
在ViewRootImpl.setView
时,会将该LayoutParams
作为参数传⼊,注意,有两个LayoutParams
,⼀个是WindowManager.LayoutParams
, ⼀个是其⽗类ViewGroup.LayoutParams
:
⽽ViewRootImpl.setView
也是在 WindowManagerGlobal.addView
时调⽤的,也就是ActivityThread
处理该Activity的Resume状态时:
public void handleResumeActivity(ActivityClientRecord r, boolean
finalStateRequest,
boolean isForward, String reason) {
......
final Activity a = r.activity;
......
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
// 现将该View设置为不可见
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
// 从这里拿到的params, 所以token也需要查一下这个LayoutParams
// 最后可以发现是在PhoneWindow创建时候创建的默认值,token还是null的
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
// 注意这里type表示为应用窗口
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
// 这里的wm是Activity中保存的WindowManager, 是该Activity被
创建
// 后调用其attach函数创建Window时的WindowManagerImpl,这一部
分我
// 们在Activity的显示(1)中有分析
wm.addView(decor, l);
......
}
可以看出只有当该Activity的window是尚未被添加且此次resume状态时是需要可⻅的时候,才会将该Activity对应View添加⾄WindowManager中:
// WindowManagerImpl.addView
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams
params) {
applyTokens(params);
// 这里的WMI是在Activity调用attach时通过createLocalWindowManager创建的
// mParentWindow就是创建该WMI的window,即Activity里的PhoneWindow
mGlobal.addView(view, params, mContext.getDisplayNoVerify(),
mParentWindow,
mContext.getUserId());
}
private void applyTokens(@NonNull ViewGroup.LayoutParams params) {
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be
WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams =
(WindowManager.LayoutParams) params;
// Only use the default token if we don't have a parent window and
a token.
if (mDefaultToken != null && mParentWindow == null && wparams.token
== null) {
// token在此赋值吗?其实一般不是的,这个mDefaultToken只有
AccessibilityService中会设置
// 所以mDefaultToken还是null的
wparams.token = mDefaultToken;
}
wparams.mWindowContextToken = mWindowContextToken;
}
虽然不是在此追踪,但是我们可以发现mGlobal.addView
中的LayoutParams
仍然是ViewGroup.LayoutParams
类型,其并不带有token成员。
// WindowManagerGlobal.java
public void addView(View view,ViewGroup.LayoutParams params,
Display display,Window parentWindow,int userId){
......
// 因为WindowManager.LayoutParams是ViewGroup.LayoutParams子类,所以可以强制转换
// 但是token默认是null的
final WindowManager.LayoutParams wparams=
(WindowManager.LayoutParams)params;
// parentWindow一般不为null,如果是ApplicationType的window,这就是resume
的Activity里的PhoneWindow自身
if(parentWindow!=null){
// 调整 WindowManager.LayoutParams
parentWindow.adjustLayoutParamsForSubWindow(wparams);
}else{
......
synchronized (mLock){
// 2.3 创建ViewRootImpl
root=new ViewRootImpl(view.getContext(),display);
......
root.setView(view,wparams,panelParentView,userId);
......
}
}
注意每个Activity对应⼀个PhoneWindow
, ⼀个PhoneWindow
对应⼀个WindowManager
(即WindowManagerImpl
), ⽽PhoneWindow
继承⾃Window。PhoneWindow在其对应的Activity被调⽤attach时
创建, 然后会创建WindowManagerImpl
,保存在PhoneWindow
的mWindowManager
成员中。
// Window.java
// Activity.attach -> PhoneWindow.setWindowManager->
public void setWindowManager(WindowManager wm,IBinder appToken,String
appName,
boolean hardwareAccelerated){
// 这个就是Activity中的 mToken, 对应的是
LocalActivityManager.LocalActivityRecord
// 顺便说一下这个token是在startActivity时创建的
mAppToken=appToken;
mAppName=appName;
mHardwareAccelerated=hardwareAccelerated;
if(wm==null){
wm=
(WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager=
((WindowManagerImpl)wm).createLocalWindowManager(this);
}
// ActivityThread.handleResumeActivity->WindowManagerImpl.addView-
>WindowManagerGlobal.addView->
void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp){
CharSequence curTitle=wp.getTitle();
// 在handleResumeActivity我们可以看到这个type被指明为TYPE_BASE_APPLICATION
if(wp.type>=WindowManager.LayoutParams.FIRST_SUB_WINDOW&&
wp.type<=WindowManager.LayoutParams.LAST_SUB_WINDOW){
......
}else{
// 到目前为止,token确实是null的
if(wp.token==null){
// mContainer是包含此窗户的容器,没有设置时,DecorWindow将作为顶级窗口
wp.token=mContainer==null?mAppToken:
mContainer.mAppToken;
}
......
......
}
⼀般Window的mContainer
是null的,所以wp.token
就是Window的mAppToken,也即Activity中的mToken, 对应LocalActivityManager.LocalActivityRecord
, 通过此变量可以快速找到Activity、ActivityInfo、Window等。所以最后Activity对应的WindowState
中对应的WindowToken
⾥的token
成员变量就是Activity中的mToken
.
Activity的View与Window的关系
在应⽤的⼀个Activity对象即将resume时,会通过WindowManagerImpl
将View与该Activity对应的PhoneWindow
关联起来。这部分发⽣WindowManagerGlobal.addView
中:
// WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
......
synchronized (mLock) {
// 创建ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
......
root.setView(view, wparams, panelParentView, userId);
......
}
}
这⾥传⼊的View 就是 r.window.getDecorView() 获取的decorView; ⽽parentWindow就是该Activity⾸次
launch时创建的PhoneWindow(如果没有parent Activity时)
简单回顾下 PhoneWindow 的 DecorView是怎么来的:
- 在Activity被launch后,会调⽤其
onCreate
⽅法 - 应⽤会在这个⽅法中调⽤
setContentView
, 将该Activity对应的View
或者layout
资源ID传⼊ - 然后在调⽤该Activity对应PhoneWindow的
setContentView
⽅法- 如果是⾸次调⽤,则先调⽤
installDecor
⽅法创建DecorView
- 之后通过PhoneWindow中的
mLayoutInflater
将该View实例化 - 最后将该View保存在PhoneWindow的ViewGroup对象
mContentParent``中 这部分的流程⽐如
installDecor`⽅法我们之后讨论View的⽣成时再详细分析。所以简单来说,Activity对应的
layout视图作为⼀个⼦视图保存在这个Activity对应PhoneWindow的DecorView中。
- 如果是⾸次调⽤,则先调⽤
IWindowSession - PhoneWindow与WMS的交互
Activity对应的Window和WMS的交互肯定是双向的,那么:
- Activity对应的PhoneWindow需要保存WMS的client端
- WMS需要保存该PhoneWindow的client端
毫⽆疑问,PhoneWindow中保存WMS的client端就是对应的IWindowManager
, 保存在WindowManagerGlobal
中。⽽PhoneWindow会⽤在其对应的ViewRootImpl
中继承了IWindow.Stub的W
类的对象,通过binder传递到WMS中,作为PhoneWindow在WMS中的client端:
// ViewRootImpl.java
public ViewRootImpl(Context context, Display display) {
this(context, display, WindowManagerGlobal.getWindowSession(),
false /* useSfChoreographer */);
}
public ViewRootImpl(@UiContext Context context, Display display,
IWindowSession session,
boolean useSfChoreographer) {
mContext = context;
mWindowSession = session;
......
// 创建W对象
mWindow = new W(this);
......
}
public void setView(View view, WindowManager.LayoutParams attrs, View
panelParentView,
int userId) {
.......
res = mWindowSession.addToDisplayAsUser(mWindow,
mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
userId,
mInsetsController.getRequestedVisibility(),
inputChannel, mTempInsets,
mTempControls);
......
}
由此可⻅,ViewRootImpl
发挥这⾄关重要的作⽤,其不仅包含Activity对应的View, PhoneWindow及该
Window的属性LayoutParams
, 还保存着作为WMS中该Activity的Window的服务端W类的对象。
之前我们略有分析mWindowSession
的创建,这⾥我们直接给出时序图:
之前我们也说过:
IWindowManager
对应WindowManagerService, 应⽤进程端通过这个接⼝即可向WMS传递消息.IWindowSession
通过这个Session和WMS中的Session交互.暂时还不理解这个作⽤W extends IWindow
对应应⽤进程端的窗⼝, WMS通过这个向应⽤进程的Window发送消息.
现在我们分析IWindowSession
的⽅法其实不难理解,IWindowSession
是为了⽅便管理某个应⽤的某个Window
与WMS通信的,虽然最后也是与WMS通信,但是将通信过程也视为⼀个对象,⽅便了管理。
小结
本⽂中分析了Android显⽰框架中Framework层最重要的WMS服务启动过程,以及复习了Activity窗⼝的创建过
程。
WMS服务启动
- WMS服务是在引导服务和核⼼服务启动之后启动的,⽽且必须等待Sensor服务的启动完毕。
- WMS服务的启动⾸先初始化了"android.display"线程,然后将启动过程放在此线程中处理,主线程阻塞
等待该启动完毕。 - WMS服务的创建过程:
- 创建了WindowAnimator
- 创建RootWindowContainer
- 创建WindowSurfacePlacer,如其名,⽤於定位窗⼝及其表⾯
- 创建TaskSnapshotController,⽤於获取相应任务的快照(位图)并将其放⼊缓存
- 设置全局阴影等等
- WMS服务创建完毕后,通过ServiceManager将其发布,服务名为Context.WINDOW_SERVICE
Activity、Window与WindowManagerService
- Activity在第⼀次启动后,调⽤attach⽅法时,会创建PhoneWindow以及对应的WindowManagerImpl
- 在Activity第⼀次resume时,调⽤该Activity的WindowManagerImpl创建ViewRootImpl
- 在创建ViewRootImpl之前,会通过adjustLayoutParamsForSubWindow将该Activity的mToken保
存在PhoneWindow的LayoutParams中 - 创建ViewRootImpl过程中,会通过IWindowManager与WMS通信,创建IWindowSession, ⽤于该
Activity的Window和WMS通信
- 在创建ViewRootImpl之前,会通过adjustLayoutParamsForSubWindow将该Activity的mToken保
- 创建ViewRootImpl之后,再通过期间创建的IWindowSession与将该Activity的IWindow、相关Window属性传递给WMS.
后记
这个系列源于一些笔记和看源码的心得,可能会有些杂乱和口语化、笔者属菜鸡,若有错误,请各位大佬及时指出。