windowsManager介绍

8. windowsManager

Window是 一个抽象类,它的具体实现是PhoneWindow。创建一个Window是很简单的事,只需要通过WindowManager即可完成。WindowManager是外界访问Window的入口,Window的具体实现位于 WindowManagerService中,WindowManager和Window-ManagerService的交互是一个IPC过程。Android中所有的视图都是通过Window来呈现的,不管是Activity、Dialog还是Toast,它们的视图实际上都是附加在 Window上的,因此Window实际是View的直接管理者

 mLayoutParams = new WindowManager.LayoutParams(
                    LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0,
                    PixelFormat.TRANSPARENT);
            mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
                    | LayoutParams.FLAG_NOT_FOCUSABLE
                    | LayoutParams.FLAG_SHOW_WHEN_LOCKED;
                    
            mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR ;
            mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
            //显示位置
            mLayoutParams.x = 100;
            mLayoutParams.y = 300;
            //支持半透明
        	mLayoutParams.format = PixelFormat.TRANSLUCENT
        	//使用系统动画
        	mLayoutParams.windowAnimations = android.R.style.Animation_Toast
        	
            mFloatingButton.setOnTouchListener(this);
            mWindowManager.addView(mFloatingButton, mLayoutParams);

8.1 WindowManager.LayoutParams参数

  1. Flags参数表示Window的属性

FLAG_NOT_FOCUSABLE 表示Window不需要获取焦点,也不需要接收各种输入事件,此标记会同时启用FLAG_NOT_TOUCH_MODAL,最终事件会直接传递给下层的具有焦点的Window。
FLAG_NOT_TOUCH_MODAL 在此模式下,系统会将当前Window区域以外的单击事件传递给底层的Window,当前Window区域以内的单击事件则自己处理。这个标记很重要,一般来说都需要开启此标记,否则其他Window将无法 收到单击事件。
FLAG_SHOW_WHEN_LOCKED 开启此模式可以让Window显示在锁屏的界面上。

  1. Type参数表示Window的类型,三种类型,分别是
  • 应用类Window对应着一个Activity。
  • 子Window不能单独存在,它需要附属在特定的父Window之中,比如常见的一些Dialog就是一个子Window。
  • 系统Window是需要声明权限在能创建的Window,比如Toast和系统状态栏这些都是系统Window。

Window是分层的,每个Window都有对应的z-ordered,层级大的会覆盖在层级小的Window的上面。
在三类Window中,
应用Window的层级范围是1~99,
子 Window的层级范围是1000~1999,
系统Window的层级范围是2000~2999,
这些层级范围对应着WindowManager.LayoutParams的type参数一般我们可以选用TYPE_SYSTEM_OVERLAY或者TYPE_SYSTEM_ERROR,

//Start of system-specific window types. These are not normally created by applications.
public static final int FIRST_SYSTEM_WINDOW     = 2000;

public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38;
 

8.1.1 权限

如果采用TYPE_SYSTEM_ERROR,只需要为type参数指定 这个层级即可:mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;同时声明权限:<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />。因为系统类型的Window是需要 检查权限的,如果不在AndroidManifest中使用相应的权限,那么创建Window的时候就会报错。

Caused by: android.view.WindowManager$BadToken- Exception: Unable to add window android.view.ViewRootImpl$W@42882fe8 -- permission denied for this window type

WindowManager所提供的功能很简单,常用的只有三个方法,即添加View、更新View和删除View,

public interface ViewManager { 
	public void addView(View view,ViewGroup.LayoutParams params); 
	public void updateViewLayout(View view,ViewGroup.LayoutParams params);
	public void removeView(View view);
}

8.1.2 拖动的Window效果

拖动的Window效果,其实是很好实现的,只需要根据手 指的位置来设定LayoutParams中的x和y的值即可改变Window的位置。首先给View设置onTouchListener:mFloatingButton.setOnTouchListener(this)。

 @Override
    public boolean onTouch(View v, MotionEvent event) {
        int rawX = (int) event.getRawX();
        int rawY = (int) event.getRawY();
        switch (event.getAction()) {
       ...
        case MotionEvent.ACTION_MOVE: {
            int x = (int) event.getX();
            int y = (int) event.getY();
            mLayoutParams.x = rawX;
            mLayoutParams.y = rawY;
            mWindowManager.updateViewLayout(mFloatingButton, mLayoutParams);
            break;
        }
     ...
        return false;
    }

8.2 Window的的内部机制

WindowManager继承ViewManager,提供的三个接口方法addView、updateViewLayout以及removeView都是针对View的,这说明View才是Window存在的实体。在实际使用中无法直接访问Window,对 Window的访问必须通过WindowManager。

8.2.1 Window的的添加过程

Window的添加过程需要通过WindowManager的addView来实现,WindowManager是一个接口,它的真正实现是WindowManagerImpl类。在WindowManagerImpl中Window的三大操作

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
     mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
                mContext.getUserId());
 }
 @Override
 public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
     applyDefaultToken(params);
     mGlobal.updateViewLayout(view, params);
 }
   @Override
 public void removeView(View view) {
     mGlobal.removeView(view, false);
 }

Window的添加过程需要通过WindowManager的addView来实现,WindowManager是一个接口,它的真正实现是WindowManagerImpl,WindowManagerImpl并没有直接实现Window的三大操作,而是全部交给了WindowManagerGlobal来处理。

  1. 检查参数是否合法
 
  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);
  } 

2.创建ViewRootImpl,将view添加到列表里

 @UnsupportedAppUsage
private final ArrayList<View> mViews = new ArrayList<View>();
@UnsupportedAppUsage
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
@UnsupportedAppUsage
ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
private final ArraySet<View> mDyingViews = new ArraySet<View>();

mViews存储的是所有Window所对应的View,
mRoots存储的是所有Window所对应的ViewRootImpl,
mParams存储的是所有Window所对应的布局参数,
mDyingViews则存储了那些正在 被删除的View对象,或者说是那些已经调用removeView方法但是删除操作还未完成的Window对象。

root = new ViewRootImpl(view.getContext(),display); view.setLayoutParams(wparams); 
mViews.add(view); 
mRoots.add(root); 
mParams.add(wparams);
  1. 通过ViewRootImpl来更新界面并完成window添加过程
    View的绘制过程是由ViewRootImpl来完成的, 在setView内部会通过requestLayout来完成异步刷新请求。在下面的代码中,scheduleTraversals实际是View绘制的入口:
public void requestLayout() {
	if (!mHandlingLayoutInLayoutRequest) {
		checkThread();
		mLayoutRequested = true;
		scheduleTraversals();
	}
}

接着会通过WindowSession最终来完成Window的添加过程。在下面的代码中,mWindowSession的类型是IWindowSession,它是一个Binder对象,真正的实现类是Session,也就是Window的添加过程是一次IPC调用

mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow,mSeq,mWindowAttributes,
		getHostVisibility(),mDisplay.getDisplayId(),mAttachInfo.mContentInsets,mInputChannel);

在Session内部会通过WindowManagerService来实现Window的添加

public int addToDisplay(IWindow window,int seq,WindowManager.LayoutParams
	attrs,int viewVisibility,int displayId,Rect outContentInsets,InputChannel outInputChannel) {
	
	return mService.addWindow(this,window,seq,attrs,viewVisibility,displayId,
			outContentInsets,outInputChannel);
}

Window的添加请求就交给WindowManagerService去处理了,在WindowManagerService内部会为每一个应用保留一个单独的Session

8.2 Window的的删除过程

先通过WindowManagerImpl后,再进一步通过WindowManagerGlobal来实现

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);
	}
}

首先通过findViewLocked来查找待删除的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来完成删除操作的。在WindowManager中提供了两种删除接口removeView和removeViewImmediate,它们分别表示异步删除和同步删除,其中removeViewImmediate使用起来需要特别注意,一般来说不需要使用此方法来删除Window以免发生意外的错误。这里主要说异步删除的情况,具体的删除操作由ViewRoot-Impl的die方法来完成。在异步删除的情况下,die方法只是发送了一个请求删除的消息后就立刻返回了,这个时候View并没有完成删除操作,所以最后会将其添加到mDyingViews中,mDyingViews表示待删除的View列表

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;

}

在die方法内部只是做了简单的判断,如果是异步删除,那么就发送一个MSG_DIE的消息,ViewRootImpl中的Handler会处理此消息并调用doDie方法,如果是同步删除(立即删除),那么就不发消息直
接调用doDie方法,这就是这两种删除方式的区别。在doDie内部会调用dispatchDetachedFromWindow方法,真正删除View的逻辑在dispatchDetachedFromWindow方法的内部实现。dispatchDetachedFromWindow
方法主要做四件事:
(1)垃圾回收相关的工作,比如清除数据和消息、移除回调。
(2)通过Session的remove方法删除Window:mWindowSession.remove(mWindow),这同样是一个IPC过程,最终会调用WindowManagerService的removeWindow方法。
(3)调用View的dispatchDetachedFromWindow方法,在内部会调用View的onDetached-FromWindow()以及onDetachedFromWindowInternal()。对于onDetachedFromWindow()大家一定不陌生,当View从Window
中移除时,这个方法就会被调用,可以在这个方法内部做一些资源回收的工作,比如终止动画、停止线程等。
(4)调用WindowManagerGlobal的doRemoveView方法刷新数据,包括mRoots、mParams以及mDyingViews,需要将当前Window所关联的这三类对象从列表中删除。

8.3 Window的更新过程

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.Layout-Params)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);
		}
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值