Android 源码解析之WindowManager删除窗口
来源:互联网 发布:贪吃蛇java小游戏代码 编辑:程序博客网 时间:2024/05/21 19:29
一,写在前面
在博客Android 源码解析之WindowManager添加窗口 中,先是介绍了如何通过WindowManager添加一个系统窗口,并从源码角度分析了PhoneWindow的创建时机,Window中创建WindowManagerImpl对象,以及创建ViewRootImpl对象,并调用setView方法完成添加窗口的操作。在ViewRootImpl类内部,通过一次IPC调用(基于Binder机制)和系统服务WindowManagerService进行通信,获取到Session对象。finally,在Session类的内部,将添加窗口的操作交给WindowManagerService处理。
二,进入主题
本篇文章是基于窗口的添加基础上进行介绍,强烈建议阅读本篇文章的哥们,先了解WindowManager是如何添加窗口,见Android 源码解析之WindowManager添加窗口 。因此,直接从WindomManagerImpl开始分析,WindomManagerImpl有这样两个方法都可以删除窗口。
源码如下:
@Override public void removeView(View view) { mGlobal.removeView(view, false); } @Override public void removeViewImmediate(View view) { mGlobal.removeView(view, true); }可以看出两个方法都调用了WindomManagerGlobal的removeView方法,只是第二个参数的boolean值不同,调用removeViewImmediate时传入true,调用removeView传入false。
查看WindomManagerGlobal$removeView源码:
public void removeView(View view, boolean immediate) { if (view == null) { throw new IllegalArgumentException("view must not be null"); } synchronized (mLock) { int index = findViewLocked(view, true); View curView = mRoots.get(index).getView(); removeViewLocked(index, immediate); if (curView == view) { return; } throw new IllegalStateException("Calling with view " + view + " but the ViewAncestor is attached to " + curView); } }
removeView最开始对参数view进行判空检查,为空则抛出异常。为避免多线程同时删除一个View,在同步代码块中处理删除窗口的操作,继续分析代码块中内容。
其中,WindomManagerGlobal$findViewLocked源码如下:
private int findViewLocked(View view, boolean required) { final int index = mViews.indexOf(view); if (required && index < 0) { throw new IllegalArgumentException("View=" + view + " not attached to window manager"); } return index; }在文章Android 源码解析之WindowManager添加窗口介绍过,mViews是一个存放View的List集合,添加窗口时,会将View添加到mViews中。首先调用list.indexOf获取该View的索引位置,若没有找到该View则抛出异常。在实际开发中,如果该View没有添加到窗口中,那么删除该View会报错。可以在执行删除操作前进行判断,避免抛出异常。也即是说,findViewLocked方法是获取到被删除的View对象在集合中的索引。
回到WindomManagerGlobal$removeView方法,内部的调用了removeViewLocked方法。
查看WindomManagerGlobal$removeViewLocked源码:
private void removeViewLocked(int index, boolean immediate) { ViewRootImpl root = mRoots.get(index); View view = root.getView(); if (view != null) { InputMethodManager imm = InputMethodManager.getInstance(); if (imm != null) { imm.windowDismissed(mViews.get(index).getWindowToken()); } } boolean deferred = root.die(immediate); if (view != null) { view.assignParent(null); if (deferred) { mDyingViews.add(view); } } }第2行,通过索引位置index,获取list集合中ViewRootImpl对象。前篇文章中有讲过,在添加窗口的流程中,每添加一次窗口,就会创建一个ViewRootImpl方法,并添加到集合mRoots中。
第3行,调用ViewRootImpl类的方法,获取被删除的View对象。
第11行,真正删除窗口的操作是交给ViewRootImpl$die方法。
ViewRootImpl$die源码如下:
/** * @param immediate True, do now if not in traversal. False, put on queue and do later. * @return True, request has been queued. False, request has been completed. */ boolean die(boolean immediate) { // Make sure we do execute immediately if we are in the middle of a traversal or the damage // done by dispatchDetachedFromWindow will cause havoc on return. if (immediate && !mIsInTraversal) { doDie(); return false; } if (!mIsDrawing) { destroyHardwareRenderer(); } else { Log.e(mTag, "Attempting to destroy the window while drawing!\n" + " window=" + this + ", title=" + mWindowAttributes.getTitle()); } mHandler.sendEmptyMessage(MSG_DIE); return true; }第8行,若immediate为true,也就是调用removeViewImmediate删除窗口,直接调用doDie(),并返回false;
第19行,若immediate为false,也就是调用removeView删除窗口,则使用Handler发送一个消息,并返回true;消息的处理也是调用doDie(),只是多了一步,通过消息机制来处理。
消息处理源码如下:
final class ViewRootHandler extends Handler {//...@Override public void handleMessage(Message msg) {//... case MSG_DIE:doDie();break;//...}//... }
回到WindomManagerGlobal$removeViewLocked方法,第14行,deferred为true时,也就是调用removeView删除窗口时,将该View对象添加到集合mDyingViews中。注意此时,只是执行了mHandler.sendEmptyMessage(MSG_DIE),发送了一个消息,还并未调用doDie()。因此,mDyingViews存放的是需要删除的View,但仍未真正删除的View。
通过前面的分析可知:不管使用哪种方式删除窗口,区别在于是否通过消息机制来删除,但最终都是调用了doDie()来处理。
ViewRootImpl$doDie源码如下:
void doDie() { checkThread(); if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface); synchronized (this) { if (mRemoved) { return; } mRemoved = true; if (mAdded) { dispatchDetachedFromWindow(); }//...code mAdded = false; } WindowManagerGlobal.getInstance().doRemoveView(this); }第10行,删除窗口操作放在 dispatchDetachedFromWindow方法中;
第17行,在完成删除窗口操作后,更新WindowManagerGlobal中维护的四个集合的数据。
查看WindowManagerGlobal$doRemoveView源码:
void doRemoveView(ViewRootImpl root) { synchronized (mLock) { final int index = mRoots.indexOf(root); if (index >= 0) { mRoots.remove(index); mParams.remove(index); final View view = mViews.remove(index); mDyingViews.remove(view); } } if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) { doTrimForeground(); } }
查看ViewRootImpl$dispatchDetachedFromWindow源码如下:
void dispatchDetachedFromWindow() { if (mView != null && mView.mAttachInfo != null) { mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false); mView.dispatchDetachedFromWindow(); } mAccessibilityInteractionConnectionManager.ensureNoConnection(); mAccessibilityManager.removeAccessibilityStateChangeListener( mAccessibilityInteractionConnectionManager); mAccessibilityManager.removeHighTextContrastStateChangeListener( mHighContrastTextManager); removeSendWindowContentChangedCallback(); destroyHardwareRenderer(); setAccessibilityFocus(null, null); mView.assignParent(null); mView = null; mAttachInfo.mRootView = null; mSurface.release(); if (mInputQueueCallback != null && mInputQueue != null) { mInputQueueCallback.onInputQueueDestroyed(mInputQueue); mInputQueue.dispose(); mInputQueueCallback = null; mInputQueue = null; } if (mInputEventReceiver != null) { mInputEventReceiver.dispose(); mInputEventReceiver = null; } try { mWindowSession.remove(mWindow); } catch (RemoteException e) { } // Dispose the input channel after removing the window so the Window Manager // doesn't interpret the input channel being closed as an abnormal termination. if (mInputChannel != null) { mInputChannel.dispose(); mInputChannel = null; } mDisplayManager.unregisterDisplayListener(mDisplayListener); unscheduleTraversals(); }首先是做一些垃圾回收的工作,执行View的一些回调方法,注销了一些接口。
重点注意第35行,内部执行mWindowSession.remove(mWindow)删除窗口,这里的变量mWindowSession就是Session对象。变量mWindowSession的实现类的获取:内部基于Binder机制,与系统服务WindowManagerService进行通信,获取到Session对象。详情可以查看文章Android 源码解析之WindowManager添加窗口,这里不再阐述。
查看Session$remove源码如下:
@Override public void remove(IWindow window) { mService.removeWindow(this, window); }mService就是WindowManagerService对象,因此,删除窗口的操作最终是交给WindowManagerService处理了。本篇文章主要介绍WindowManager删除窗口的内部实现原理,分析到这里就结束了。暂不对WindowManagerService删除窗口的具体流程进行介绍,有需要的哥们可以查阅老罗的博客解惑吧~
三,总结
值得一提的是,WindowManager删除窗口这种说法,内部其实是删除View。每一个View都需要依赖于Window,View不能独立的存在的去添加,删除,更新。窗口Window是一种抽象的概念,抽取出了一些方法,例如,可以创建以及获取WindowManagerImpl的实例。因此,通过WindowManager增加,删除,更新窗口,内部其实是对View的增加,删除,更新。从WindowManagerImpl方法的命名就可以看出,均跟view相关。
本篇文章介绍了WindowManager删除窗口的的内部实现原理,WindowManagerImpl有removeView,removeViewImmediate两种方式删除窗口。区别在于removeView通过消息机制,在处理的消息时才调用doDie(),removeViewImmediate则是直接调用doDie()。diDie()内部调用dispatchDetachedFromWindow方法,在dispatchDetachedFromWindow方法中调用Session对象的remove方法,内部是将删除窗口的操作交给WindowManagerService处理。
另外,在文章Android 源码解析之WindowManager添加窗口中介绍了窗口得添加,调用了Session对象的addToDisplay方法,内部也是将添加窗口的操作交给WindowManagerService来处理。由此可见,通过WindowManager删除,添加窗口的操作,最终都是交给系统服务WindowManagerService来处理了。可以猜测更新窗口的操作也是这样的,当然确实如此啦~
阅读全文
0 0
- Android 源码解析之WindowManager删除窗口
- Android 源码解析之WindowManager添加窗口
- Android 源码解析之WindowManager更新窗口
- android源码分析之windowmanager (android悬浮窗口的实现)
- Android 之 Window、WindowManager 与窗口管理
- Android 之 Window、WindowManager 与窗口管理
- Android 之 Window、WindowManager 与窗口管理
- Android 之 Window、WindowManager 与窗口管理
- Android 之 Window、WindowManager 与窗口管理
- Android之悬浮窗口实现(WindowManager)
- Android 之 Window、WindowManager 与窗口管理
- Android 之 Window、WindowManager 与窗口管理
- Android 之 Window、WindowManager 与窗口管理
- Android 之 Window、WindowManager 与窗口管理
- Android 之 Window、WindowManager 与窗口管理
- Android 之 Window、WindowManager 与窗口管理
- Android之悬浮窗口实现(WindowManager)
- Android 之 Window、WindowManager 与窗口管理
- 汉诺塔【python】
- jq中创建表格及删除事件
- 浅谈编程
- 数组的引用
- Linux 下 mysql 的安装 (编译源码的方式)
- Android 源码解析之WindowManager删除窗口
- jq中三种显示及隐藏方法
- lnmp “.user.ini”无法删除解决方法
- 两个变量交换的三种方法
- 沐枫NOI 21. Restoring Password
- 数据分析的统计学基础-假设检验
- Spring+SpringMVC+Mybatis+mysql整合详解
- 如何制作Ubuntu安装U盘
- HTML和CSS实例讲解