Android 设计模式 笔记 - 深入了解WindowManager
来源:互联网 发布:iphone6s网络不可用 编辑:程序博客网 时间:2024/05/09 22:52
了解一:
所有的可以显示到屏幕上的内容都是通过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
- Android 设计模式 笔记 - 深入了解属性动画
- 设计模式之深入了解
- 深入了解 Window和WindowManager
- Android设计模式(二)- 续:WindowManager
- 深入了解iOS代理设计模式
- android对启动模式的深入了解
- 设计模式系列17---深入了解的访问者模式设计模式系列17---深入了解的访问者模式
- 透过深入研究pureMVC了解设计模式(一)
- java基础 深入了解单例设计模式
- android 设计模式笔记
- Android中WindowManager学习笔记
- 深入了解Android
- Android 深入了解Surface
- Android深入了解
- Android学习笔记---09_深入了解各种布局技术
- Android开发笔记----Intent变量的深入了解
- 了解设计模式
- 小项目--迷宫
- 使用 matlab 进行正太拟合
- java(8)------方法访问权限控制(public,protected,package,private)
- Android之属性动画
- PMP项目管理认证
- Android 设计模式 笔记 - 深入了解WindowManager
- 图片缓存SDWebImage分析
- SQL查询语句表行列部分转换为行
- linux jboss服务器发布简单java项目
- Redis学习笔记01--NoSQL/Redis概述
- 直播要解决的问题
- group by 执行流程图
- View的事件分发机制《android开发艺术与探索》
- ServletConfig&ServletContext