Window内部机制浅谈
来源:互联网 发布:java 异步服务器 编辑:程序博客网 时间:2024/05/17 00:17
Window 基本概念:
什么是Window,顾名思义 Window 表示一个窗口的概念。在日常开发中我们直接接触 Window 的机会并不多,但是在某些特殊情况下我们需要使用Window 来实现。
Android中所有视图都是由 Window 展示的,无论是Activity、Dialog、Toast 它们的视图实际上都是附属在Window上的 ,因此Window实际是View 的直接管理者。
源码中Window是一个抽象类;
public abstract class Window{}
而它的具体实现是通过PhoneWindow 来实现的;
public class PhoneWindow extends Window implements MenuBuilder.Callback {}
如何创建一个Window 窗口
创建一个Window窗口是很简单的,只需要通过WindowManager 即可完成创建,WindowManager是外部访问Window 的入口,Window创建的具体实现是在WindowManagerService中, 而 WindowManager 与WindowManagerService 的交互是一个IPC过程。
以下是 通过WindowManager 添加Window的示例代码:
WindowManager windowManager = (WindowManager)getSystemService(Context.WINDOW_SERVICE); Button button = new Button(this); button.setText("Hello Window"); //参数说明: LayoutParams(int w, int h, int _type, int _flags, int _format); //1:layoutParams w //2:layoutParams h //3:窗口类型 //4:行为选项、旗标 //5:设置Window的背景支持半透明 WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT,0,0, PixelFormat.TRANSPARENT); layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; layoutParams.gravity = Gravity.TOP| Gravity.LEFT; layoutParams.x = 100; layoutParams.y = 300; //添加 windowManager.addView(button,layoutParams); //更新 windowManager.updateViewLayout(button,layoutParams); //删除 windowManager.removeView(button);
WindowManager.LayoutParams 中flags 与type 这两个参数是比较重要的,
下面对其常用的选项进行简单的介绍,如果读者想了解更所,可以参考官网api
Flags参数 表示Window的属性,通过这些属性 可以控制Window的显示特性。
FLAG_NOT_TOUCH_MODAL:表示Window 不需要获取焦点,也不需要接受各种输入事件,此标记会同时启用FLAG_NOT_TOUCH_MODAL,最终事件会直接传递给下层具有焦点的Window
FLAG_NOT_FOCUSABLE: 此模式下,系统会将当前Window区域以外的单击事件传递给底层的Window,当前Window区域以内的单击事件则自己处理,这个标记很重要,一般来说都需要开启此标记,否则其他Window将无法接受到单击事件。
FLAG_SHOW_WHEN_LOCKED:开启该模式 表示可以让Window显示在锁屏界面上。
Type :
在Android中大致可以将Window 分为三层,每个Window都也有自己的z-ordered ,层次大的会覆盖在层次小的Window 上面。在这三层中:
应用Window的层级范围 0~99,
子Window的层级范围 1000~1999;
系统Window的层级范围 2000~ 2999;
很显然系统Window的层级是最大的,如果我们选用系统Window ,一般情况下可以选用TYPE_SYSTEM_OVERLAY 或者 TYPE_SYSTEM_ERROR 如果采用 TYPE_SYSTEM_ERROR 我们只需要 :
layoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR
并同时 添加权限
注:
android版本大于6.0之后 针对 SYSTEM_ALERT_WINDOW 该权限需要通过代码去打开启动授权界面来玩成
if (Settings.canDrawOverlays(this)) { .... } else { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); startActivity(intent); }
WindowManager 内部方法解析
WindowManager中常用的只有三个方法:
public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view);
而在源码中我们可以看到这三个方法主要是定义在 ViewManager 接口中;
public interface ViewManager{ public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view);}
WindowManager 同样也是一个接口,只不过是继承了ViewManager 该接口;
public interface WindowManager extends ViewManager {}
WindowManager的具体实现类是;
public final class WindowManagerImpl implements WindowManager{}
由此可见当调用WindowManager中的添加,删除,修改等方法时,其实是调用了 WindowManagerImpl
中的 各个方法。
下面我们针对WindowManagerImpl中的 addView、updateViewLayout、removeView
这三个方法来进一步分析 Window 的添加,更新,删除。
1:WindowManager.addView(….)
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();@Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mDisplay, mParentWindow); }
通过addView 源码我们可以看出 Window的添加实际是通过 WindowManagerGlobal. addVeiw方法完成的。
以下我们只需要看 WindowManagerGlobal. addVeiw 方法即可
private final ArrayList<View> mViews = new ArrayList<View>(); private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>(); private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>(); private final ArraySet<View> mDyingViews = new ArraySet<View>(); 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; ..... ViewRootImpl root; View panelParentView = null; ..... root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams); } .... 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; } }
该方法比较长,我们只需要关注我们想要关注的内容即可,
注:通过源码可以看到,内部会将View、roots、wparams 存储到不同的 ArrayList 内, 可以发现mView 存储的是所有Window所对应的View,mRoots 存储的是所有Window 所对应的ViewRootImpl,mParams存储的是所有Window所对应的布局参数,而mDyingViews 则存储了那些正在被删除的view 对象,或者说是那些已经调用removeView方法 但是删除操作还没有完成的Window对象。
在addView 中我们可以看出 最终 是调用了 ViewRootImpl.setView(….)方法来实现Window的添加
final IWindowSession mWindowSession; public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { ..... // 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(); ..... mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel); } }
改方法中我们只需要关注 reqestLayout() 该方法即可,该方法主要是来完成异步刷新请求。
接下来我们看一下 requestLayout()方法内部是如何实现的。
@Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } }
内部调用了scheduleTraversals 方法,根据View的绘制原理,我们知道scheduleTraversals就是View的绘制入口。
当View绘制完成之后,接着通过WindowSession 来完成Window的添加 ,在setView中 我们可以看到内部调用了 mWindowSession.addToDisplay(…..)
而mWIndowSession的类型是IWindowSession ,真正的实现类是Session 而通过Session的源码
final class Session extends IWindowSession.Stub implements IBinder.DeathRecipient
我们可以看到Window的添加过程其实就是一个IPC过程。
而当mWindowSession.addToDisplay(….)方法我们可以看出
final WindowManagerService mService; @Override public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) { return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outContentInsets, outStableInsets, outOutsets, outInputChannel); }
在session内部我们看到内部调用了mService.addWindow,而mService对象 是 WindowManagerService 如此一来,Window的添加请求,就交给了WindowManagerService 去处理了,到现在我们对Window的添加流程大致了解清除了,而对于实际通过WindowManagerService 添加一个WIndow的具体实现,读者可以 通过查看源码进行进一步的分析,
内部源码 有兴趣的读者 可以自行查看。
2:WindowManager.removeView(….)
接下来我们看以下Window的删除,在WindowManagerImpl中我们可以看到
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();@Override public void removeView(View view) { mGlobal.removeView(view, false); }
内部删除时调用了 WindowManagerGlobal.removeView(….) 方法,
而在WindowManagerGlobal.removeView(….) 方法内部,我们可以看到
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();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); } }
内部先找到带删除的索引 index 然后通过该索引得到当前要删除的View,之后调用 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); } } }
可以看到 removeViewLocked 方法内部是通过 ViewRootImpl 来实现删除操作的。在代码内部我们可以看到具体的删除操作时通过 root.die(immediate) 来完成的。
该 root.die(immediate)的源码 我们可以看出
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(TAG, "Attempting to destroy the window while drawing!\n" + " window=" + this + ", title=" + mWindowAttributes.getTitle()); } mHandler.sendEmptyMessage(MSG_DIE); return true; }***************************** case MSG_DIE: doDie(); break;
die内部有两种形式的删除,一种是同步删除 则直接调用了doDie方法,另外一种是异步删除 首先发送了一条Message 接着Handler在接受到该Message之后,再调用doDie方法。因此无论是同步还说异步都会调用到doDie方法,我们只需看该方法内是如何实现Window是如何删除的。
void doDie() { checkThread(); if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface); synchronized (this) { if (mRemoved) { return; } mRemoved = true; if (mAdded) { dispatchDetachedFromWindow(); } ..... } WindowManagerGlobal.getInstance().doRemoveView(this); }
可以看出该方法内部会调用dispatchDetachedFromWindow方法,而真正删除Window的逻辑是在dispatchDetachedFromWindow方法内实现的。
final IWindowSession mWindowSession void dispatchDetachedFromWindow() { ..... try { mWindowSession.remove(mWindow); } catch (RemoteException e) { } ..... }
而在改方法内部我们可以看出 内部实际上是调用了mWindowSession.remove(….)方法,根Window的添加一样,remove方法最终会交给 WindowManagerService 中去实现。
以下是WindowManagerService 中removeWindowToken的实现细节;
public void removeWindow(Session session, IWindow client) { synchronized(mWindowMap) { WindowState win = windowForClientLocked(session, client, false); if (win == null) { return; } removeWindowLocked(win); } }
内部的详细代码 感兴趣的童鞋可以仔细研读。
之后紧接着调用 WindowManagerGlobal.getInstance().doRemoveView(this) 该方法 刷新数据。
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 (HardwareRenderer.sTrimForeground && HardwareRenderer.isAvailable()) { doTrimForeground(); } }
该方法内部我们可以看出(包括mRoots、mParams、以及mDYingView) 从当前Window所关联的这三类对象从列表中删除。
至此 Window的删除流程我们大致已经了解了。
3:WindowManager.updateViewLayout(….)
接下来我们看WIndow的更新过程,我们还是从WindowManagerImpl方法开始
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); @Override public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.updateViewLayout(view, params); }
内部调用到 WindowManagerGlobal.updateViewLayout
public void updateViewLayout(View view, ViewGroup.LayoutParams params) { if (view == null) { throw new IllegalArgumentException("view must not be null"); } if (!(params instanceof WindowManager.LayoutParams)) { throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); } final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; view.setLayoutParams(wparams); synchronized (mLock) { int index = findViewLocked(view, true); ViewRootImpl root = mRoots.get(index); mParams.remove(index); mParams.add(index, wparams); root.setLayoutParams(wparams, false); } }
而在 WindowManagerGlobal.updateViewLayout 内部我们可以看出哪部更新实现是 首先更新View的LayoutParams并替换掉老掉LayoutParams 接着再更新ViewRootImpl中LayoutParams。
在ViewRootImpl 中会通过 scheduleTraversals方法来对View 重新布局,同时还会通过WindowSession来更新Window视图。
void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) { synchronized (this) { ....... if (newView) { mSoftInputMode = attrs.softInputMode; requestLayout(); } ..... scheduleTraversals(); } }
这个过程最终会调用到WindowManagerService.relayoutWindow 来更新窗口,这同样是一个IPC过程;
总结: 针对Window的添加、删除、更新,我们可以看出其实都是一个IPC过程,最终无论是添加、删除、更新 都是在WindowManagerService中具体实现的。
- Window内部机制浅谈
- Window的内部机制
- 浅谈Asp.Net内部机制
- 浅谈ASP.NET的内部机制(一)
- 浅谈ASP.NET的内部机制(一)
- 浅谈ASP.NET的内部机制(二)
- 浅谈ASP.NET内部机制(三)
- 浅谈ASP.NET内部机制(四)
- 浅谈ASP.NET的内部机制(一)
- [转]浅谈ASP.NET内部机制(三)
- 浅谈ASP.NET的内部机制(二)
- 浅谈ASP.NET的内部机制(一)
- 浅谈ASP.NET内部机制(二)
- 浅谈ASP.NET内部机制(三)
- 浅谈ASP.NET内部机制(四)
- 浅谈ASP.NET内部机制(五)
- 浅谈ASP.NET的内部机制
- [转]浅谈ASP.NET的内部机制(一)
- 页面设计经验1
- 分析我关于应用商店 的猥琐言论
- 即插即用demo系列——结巴分词并计算权重
- 【华为OJ14】句子逆序
- 今天wangjuan生了个儿子,祝福啊
- Window内部机制浅谈
- 推荐 wp插件 wp-cumulus
- oracle数据库修改表列名
- 数据库(2):sql语句例子
- 清晨惊魂 -- 亲身经历
- 关于SVN上传代码,.so文件.a文件自动被过滤的问题
- 伤感: 小爸今天过世
- Google浏览器Chrome使用技巧
- 无意中搜索出自己做的私活 无语了 ....