Title: 对Window/WindowManager和WindowManagerSystem的理解
date: 2016-09-20 12:08
status: public
tags: [View,Touch]
本文发表于KuTear’s Blog,转载请注明
WindowsManager
Window的type
The general type of window. There are three main classes of
window types:
<ul>
<li> <strong>Application windows</strong> (ranging from
{@link #FIRST_APPLICATION_WINDOW} to
{@link #LAST_APPLICATION_WINDOW}) are normal top-level application
windows. For these types of windows, the {@link #token} must be
set to the token of the activity they are a part of (this will
normally be done for you if {@link #token} is null).
<li> <strong>Sub-windows</strong> (ranging from
{@link #FIRST_SUB_WINDOW} to
{@link #LAST_SUB_WINDOW}) are associated with another top-level
window. For these types of windows, the {@link #token} must be
the token of the window it is attached to.
<li> <strong>System windows</strong> (ranging from
{@link #FIRST_SYSTEM_WINDOW} to
{@link #LAST_SYSTEM_WINDOW}) are special types of windows for
use by the system for specific purposes. They should not normally
be used by applications, and a special permission is required
to use them.
</ul>
—WindowManager.java
以上来源WindowManager#LayoutParams,介绍了Window分为Application windows
,Sub-windows
,System windows
三种。
getSystemService(str)的实现原理
我们知道Context
的实现类是ContextImpl
,通过查看源码,得到函数的实现为源码。
//ContextImpl.java
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
OK,在此可以看见,是调用SystemServiceRegistry
的静态函数getSystemService(..)
来实现的,我们跟着深入,这个函数的实现为源码
//SystemServiceRegistry.java
/**
* Gets a system service from a given context.
*/
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
OK,进一步知道是从ServiceFetcher
中获取到的,而ServiceFetcher
是从SYSTEM_SERVICE_FETCHERS
获取到的,跟进知道SYSTEM_SERVICE_FETCHERS
的申明为
private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
new HashMap<String, ServiceFetcher<?>>();
是个HashMap
,现在我们就要看看数据是在哪里put
进去的,通过分析查找,put
只在函数registerService()
中调用过,所以这些服务一定是通过这里注册的。由于是static final
,所以只能在构造函数或静态块进行初始化,这就很方便我们查找。根据查找,可看见实现源码。
static {
registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,
new CachedServiceFetcher<AccessibilityManager>() {
@Override
public AccessibilityManager createService(ContextImpl ctx) {
return AccessibilityManager.getInstance(ctx);
}});
......
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher<WindowManager>() {
@Override
public WindowManager createService(ContextImpl ctx) {
return new WindowManagerImpl(ctx);
}});
......
}
/**
* Statically registers a system service with the context.
* This method must be called during static initialization only.
*/
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);
}
回到上面的fetcher.getService(ctx)
函数,它的实现为源码。
//代码去除了无用部分
public Object getService(ContextImpl ctx) {
service = createService(ctx);
cache.set(mContextCacheIndex, service);
return service;
看到这里,我们知道平时使用WindowManager
的真实对象其实是WindowManagerImpl
。
Window的添加过程
根据上面的分析,知道了WindowManagerImpl
才是幕后的凶手,但是真的是这样的吗?我们接着分析。在它的源码中,我们可以看看源码
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Display mDisplay;
private final Window mParentWindow;
public WindowManagerImpl(Display display) {
this(display, null);
}
private WindowManagerImpl(Display display, Window parentWindow) {
mDisplay = display;
mParentWindow = parentWindow;
}
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mDisplay, parentWindow);
}
public WindowManagerImpl createPresentationWindowManager(Display display) {
return new WindowManagerImpl(display, mParentWindow);
}
@Override
public void addView(View view, ViewGroup.LayoutParams params) {
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
@Override
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
mGlobal.updateViewLayout(view, params);
}
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
@Override
public void removeViewImmediate(View view) {
mGlobal.removeView(view, true);
}
@Override
public Display getDefaultDisplay() {
return mDisplay;
}
}
卧槽,添加View
并不是它在操作,看来真实另有隐情啊。Window
的三大操作(add,remove,update)都是有WindowManagerGlobal
来实现的。下面分析WindowManagerGlobal
,先看看它的内部字段的含义。
//存储所有Window对应的View
private final ArrayList<View> mViews = new ArrayList<View>();
//存储所有Window对应的ViewRootImpl
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
//存储所有Window对应的布局参数
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
//存储正在被删除的View(已经调用remove但remove还没有完成)
private final ArraySet<View> mDyingViews = new ArraySet<View>();
接下来就是核心部分了,分析addView
的源码。
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...... 参数合法判断
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
if (parentWindow != null) {
//parentWindow != null 表示新建的Window的type为子Window,如Dialog,其主要作用是修改token
//子Window回共用父Window的token.
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
// If there's no parent and we're running on L or above (or in the
// system context), assume we want hardware acceleration.
final Context context = view.getContext();
if (context != null
&& context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// Start watching for system property changes.
if (mSystemPropertyUpdater == null) {
mSystemPropertyUpdater = new Runnable() {
@Override public void run() {
synchronized (mLock) {
for (int i = mRoots.size() - 1; i >= 0; --i) {
mRoots.get(i).loadSystemProperties();
}
}
}
};
SystemProperties.addChangeCallback(mSystemPropertyUpdater);
}
//view在mViews中的位置
int index = findViewLocked(view, false);
//找到说明该View已经添加到某个Window
if (index >= 0) {
//正在remove
if (mDyingViews.contains(view)) {
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
// The previous removeView() had not completed executing. Now it has.
}
// If this is a panel window, then find the window it is being
// attached to for future reference.
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
看了这么多,我们发现还是没有看见核心代码,那就继续往下看咯,ViewRootImpl
这个类。ViewRootImpl
是FrameWork
层与Native
层的通信桥梁。在上面的代码中有实例化它,我们看看具体的实现.
public ViewRootImpl(Context context, Displaydisplay) {
// ① 从WindowManagerGlobal中获取一个IWindowSession的实例。它是ViewRootImpl和WMS进行通信的代理
mWindowSession= WindowManagerGlobal.getWindowSession(context.getMainLooper());
// ② 保存参数display,在后面setView()调用中将会把窗口添加到这个Display上
mDisplay= display;
CompatibilityInfoHolder cih = display.getCompatibilityInfo();
mCompatibilityInfo = cih != null ? cih : new CompatibilityInfoHolder();
// ③ 保存当前线程到mThread。这个赋值操作体现了创建ViewRootImpl的线程如何成为UI主线程。在ViewRootImpl处理来自控件树的请求时(如请求重新布局,请求重绘,改变焦点等),会检查发起请求的thread与这个mThread是否相同。倘若不同则会拒绝这个请求并抛出一个异常
mThread= Thread.currentThread();
......
// ④ mDirty用于收集窗口中的无效区域。**所谓无效区域是指由于数据或状态发生改变时而需要进行重绘的区域。举例说明,当应用程序修改了一TextView的文字时,TextView会将自己的区域标记为无效区域,并通invalidate()方法将这块区域收集到这里的mDirty中。当下次绘制时,TextView便可以将新的文字绘制在这块区域上
mDirty =new Rect();
mTempRect = new Rect();
mVisRect= new Rect();
// ⑤ mWinFrame,描述了当前窗口的位置和尺寸。与WMS中WindowState.mFrame保持着一致
mWinFrame = new Rect();
// ⑥ 创建一个W类型的实例,W是IWindow.Stub的子类。即它将在WMS中作为新窗口的ID,以及接收来自WMS的回调
mWindow= new W(this);
......
// ⑦ 创建mAttachInfo。mAttachInfo是控件系统中很重要的对象。它存储了此当前控件树所以贴附的窗口的各种有用的信息,并且会派发给控件树中的每一个控件。这些控件会将这个对象保存在自己的mAttachInfo变量中。mAttachInfo中所保存的信息有WindowSession,窗口的实例(即mWindow), ViewRootImpl实例,窗口所属的Display,窗口的Surface以及窗口在屏幕上的位置等等。所以,当要需在一个View中查询与当前窗口相关的信息时,非常值得在mAttachInfo中搜索一下
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display,this, mHandler, this);
// ⑧ 创建FallbackEventHandler。**这个类如同PhoneWindowManger一样定义在android.policy包中,其实现为PhoneFallbackEventHandler。FallbackEventHandler是一个处理未经任何人消费的输入事件的场所。在6.5.4节中将会介绍它
mFallbackEventHandler =PolicyManager.makeNewFallbackEventHandler(context);
......
//⑨ 创建一个依附于当前线程,即主线程的Choreographer,用于通过VSYNC特性安排重绘行为
mChoreographer= Choreographer.getInstance();
......
}
以上分析来源于GITBOOK.接着看看mWindowSession
是怎么来的。
public static IWindowSession getWindowSession() {
//代码删除干扰部分
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(),
imm.getInputContext());
return sWindowSession;
}
通过Google搜索,我们发现IWindowManager
其实是一个AIDL
文件,代码在此,也就是说,这里的实现其实是进程间的通信,继续跟进下getWindowManagerService()
的实现。
public static IWindowManager getWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
......
}
return sWindowManagerService;
}
}
根据我们已有的AIDL的知识,我们知道ServiceManager.getService("window")
返回的其实是一个IBinder
,这里接着看看到底返回的是什么?在源码中查看。
public static IBinder getService(String name) {
...
return getIServiceManager().getService(name);
...
}
public static void addService(String name, IBinder service) {
getIServiceManager().addService(name, service, false);
}
所以,所有的服务都是通过addService
添加到ServiceManager
,然后通过getService
取出,但是是怎么添加,每个服务具体的类是什么?我们只要看看哪里调用addService
即可。源码
try {
power = new PowerManagerService();
ServiceManager.addService(Context.POWER_SERVICE, power);
....
wm = WindowManagerService.main(context, power,
factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);//Context.WINDOW_SERVICE = "window"
}
...
在WindowManagerService.main(...)
中,我们可以看到返回的其实是 WindowManagerService
。而WindowManagerService
的声明为
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs{}
class IWindowManager.Stub extends android.os.Binder{}
public class Binder implements IBinder{}
这里可以看到,这是很普通的一个AIDL的实现.回到函数getWindowSession()
,
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(),
imm.getInputContext());
我们知道了windowManager
其实是WindowManagerService
,所以看看openSession
的具体实现。
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
Session session = new Session(this, callback, client, inputContext);
return session;
}
可以看见Session
的签名如下,我们知道它是IWindowSession.aidl
的具体实现.
final class Session extends IWindowSession.Stub
implements IBinder.DeathRecipient{}
This class represents an active client session. There is generally one
Session object per process that is interacting with the window manager.
WindowManagerGlobal
的addView()
最终调用的了ViewRootImpl
的setView()
方法,那么这个方法又是做什么用的?
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
...
try {
...
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {} finally {}
上面代码中的requestLayout()
会调用scheduleTraversals()
方法,而该方法最终会调用performTraversals()
,该函数就是android系统View树遍历工作的核心。执行过程可简单概括为根据之前所有设置好的状态,判断是否需要计算视图大小(measure)、是否需要重新安置视图的位置(layout),以及是否需要重绘(draw)视图,这里我不会进一步分析.下面看看mWindowSession.addToDisplay()
的调用,我们知道mWindowSession
就是Session
,这个函数的实现为
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
//mService 是WindowManagerService ,构造函数中赋的值
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
可以看出,Window的添加请求就交给WindowManagerService去处理了。addView大概一个过程如下:
WindowManagerImpl——>WindowManagerGobal——>ViewRootImpl——>Session——>WindowManagerService