Android 设计模式 笔记 - 深入了解WindowManager
Posted 鲨鱼丶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 设计模式 笔记 - 深入了解WindowManager相关的知识,希望对你有一定的参考价值。
了解一:
所有的可以显示到屏幕上的内容都是通过windowManager来操作的。包括Activity等。
了解二:
WindowManager是一个非常重要的子系统。简称WMS
了解三:
和WindowManager联系上的第一步就是通过Context中的getSystemService()方法。
我们已经了解到各种系统的服务都会注册到ContextImpl的一个map容器里,然后通过该服务的字符串键进行获取,而WindowManager就是ContextImpl中注册的众多服务之一。
了解四:
WindowManager在java代码中的具体实现是WindowManagerImpl。
- 实践 -- Dialog中的WindowManager
追踪Dialog代码的构造方法:
Dialog(Context context, int theme, boolean createContextThemeWrapper)
if (createContextThemeWrapper)
if (theme == 0)
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme,
outValue, true);
theme = outValue.resourceId;
mContext = new ContextThemeWrapper(context, theme);
else
mContext = context;
mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
Window w = PolicyManager.makeNewWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
可以看到Dialog最终是通过setWindowManager方法把window和WindowManager建立了关系。
追踪setWindowManager方法:
public void setWindowManager(WindowManager wm, IBinder appToken, String appName)
setWindowManager(wm, appToken, appName, false);
/**
* Set the window manager for use by this Window to, for example,
* display panels. This is <em>not</em> used for displaying the
* Window itself -- that must be done by the client.
*
* @param wm The window manager for adding new windows.
*/
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);
可以看到最后一行代码处,有一个WindowManagerImpl的方法,即createLocalWindowManager方法,继续跟踪这个方法:
public WindowManagerImpl createLocalWindowManager(Window parentWindow)
return new WindowManagerImpl(mDisplay, parentWindow);
单纯的构建了一个WindowManagerImpl对象,不过和ContextImpl里面注册WindowManager不同的是这里多了一个参数,
parentWindow
这个参数的增加说明构建WindowManagerImpl的这个方法是于具体的window相关联的。表示java层上的Window对象已经和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;
我们看到在WindowManagerImpl中有一个WindowManagerGlobal的单例对象,而WindowManagerImpl的具体实现方法也是调用WindowManagerGlobal中的,也就是说WindowManagerGlobal是具体的实现类,而WindowManagerImpl只是一个架子罢了,我们又有了解的是,每一次的创建出一个界面显示到屏幕上所调用的是addView方法我,我们现在主要看的是addView方法,看看WindowManagerGlobal到底是怎么显示界面的:
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow)
if (view == null)
throw new IllegalArgumentException("view must not be null");
if (display == null)
throw new IllegalArgumentException("display must not be null");
if (!(params instanceof WindowManager.LayoutParams))
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
if (parentWindow != null)
parentWindow.adjustLayoutParamsForSubWindow(wparams);
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);
int index = findViewLocked(view, false);
if (index >= 0)
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对象
- 把参数布局设置到view
- 存储ViewRootImpl,View,LayoutParams到列表
- 通过ViewRootImpl的setView方法把view显示到窗口界面上。
值得注意的是ViewRootImpl并不是一个view而是一个继承Handler类的,作为native和java层view系统的一个通信桥梁。
代码分析到这里我们就已经到了Android的Framework层了。
下面才是Framework和Native层建立通讯关系的具体类,即使ViewRootImpl
因为addView的第一步就是创建ViewRootImpl的对象root,我们可以看下他的代码:
public ViewRootImpl(Context context, Display display)
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
mDisplay = display;
mBasePackageName = context.getBasePackageName();
mDisplayAdjustments = display.getDisplayAdjustments();
mThread = Thread.currentThread();
/**
*省略代码
**/
我们看到代码里有一个WindowManagerGlobal的方法,我们不得不怀疑这个代码和ViewRootImpl的作用有很大的关系,所以我们去追踪这个方法的实现:
public static IWindowSession getWindowSession()
synchronized (WindowManagerGlobal.class)
if (sWindowSession == null)
try
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
imm.getClient(), imm.getInputContext());
float animatorScale = windowManager.getAnimationScale(2);
ValueAnimator.setDurationScale(animatorScale);
catch (RemoteException e)
Log.e(TAG, "Failed to open window session", e);
return sWindowSession;
public static IWindowManager getWindowManagerService()
synchronized (WindowManagerGlobal.class)
if (sWindowManagerService == null)
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
return sWindowManagerService;
在getWindowSession方法中,首先根据getWindowManagerService方法获得了IWindowManager对象,而getWindowManagerService方法是通过ServiceManager.getService("window")方法获取WindowManagerService,并且通过IWindowManager.Stub.asInterface方法把这个函数直接转换成IWindowManager对象。
我们下面一步步来,先去看getService方法的实现:
public static IBinder getService(String name)
try
IBinder service = sCache.get(name);
if (service != null)
return service;
else
return getIServiceManager().getService(name);
catch (RemoteException e)
Log.e(TAG, "error in getService", e);
return null;
返回的是一个IBinder对象。也就是说Android Framework和WindowManagerService通过的是binder机制进行通讯的,我们这边到这一步才真正的和WindowManagerService建立初步的联系。
接下来我们继续分析这个getWindowSession方法的实现:
我们获取到IWindowManager对象之后,使用openSession方法来和WMS建立一个通信会话,以后如果有各种需求只需要通过这个session就好了。
和WMS建立了session关系之后,就是我们已经完成了ViewRootImpl对象的创建,之后我们就可以使用setView方法进行显示界面了,这个方法会向WMS发送显示界面的请求。
我们看下setView的代码:
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView)
synchronized (this)
requestLayout();
try
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mInputChannel);
代码简化之后如上,源代码非常复杂,但是我们主要关注的是setView通知显示界面,所以我们在setView上面只需要关注请求布局的方法(requestLayout),和发送请求的方法( res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mInputChannel);)
请求布局的方法实现:
@Override
public void requestLayout()
if (!mHandlingLayoutInLayoutRequest)
checkThread();
mLayoutRequested = true;
<pre name="code" class="java"> void scheduleTraversals()
if (!mTraversalScheduled)
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
scheduleConsumeBatchedInput();
();
void scheduleConsumeBatchedInput()
if (!mConsumeBatchedInputScheduled)
mConsumeBatchedInputScheduled = true;
mChoreographer.postCallback(Choreographer.CALLBACK_INPUT,
mConsumedBatchedInputRunnable, null);
也就是向handler中发送了一个消息,这个消息会触发整个视图的绘制操作。即performTraversals()方法,看下这个方法:
private void performTraversals()
额...方法太复杂,我们要知道的是在这个方法里面总共做了四件事情:
- 对象,获取Surface对象,用于图形的绘制
- 丈量,丈量整个视图书的各个View的大小,performMeasure方法
- 布局,使用performLayout方法布局整个视图树
- 绘制,使用performDraw绘制真个视图树
绘制界面的代码就不专门的进行表现了,我们仔细研究了performDraw的代码之后发现具体实现绘制视图界面的方法是draw方法,在这个方法里面,我们想要显示图形到界面要经过一下几个步骤:
- 判断是CPU绘制还是GPU绘制
- 获取Surface对象
- 通过surface对象获取并且锁住Canvas绘图对象
- 从DecorView开始发起整个视图树的绘制
- 从Surface解锁Canvas,并且通知SurfaceFlinger更新视图
以上是关于Android 设计模式 笔记 - 深入了解WindowManager的主要内容,如果未能解决你的问题,请参考以下文章