Android基础知识 Window相关

WindowManager添加Window的实现

Button mFloatButton = new Button(this);
mFloatButton.setText("button");
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.gravity = Gravity.LEFT | Gravity.TOP;
//mLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
mLayoutParams.type = WindowManager.LayoutParams.TYPE_TOAST;
mLayoutParams.x = 100;
mLayoutParams.y = 300;
mWindowManager.addView(mFloatButton, mLayoutParams);
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 显示在锁屏的界面上

2、Type参数,表示Window的类型

Window有三种类型,分别对应【应用Window、子Window、系统Window】

  • 应用类Window,对应一个Activity
  • 子Window,不能单独存在,需依附特定的父Window中
    比如,Dialog就是一个子Window。
  • 系统Window,需要声明权限才能创建,
    比如,Toast、系统状态栏 都是系统Window。
3、Window 的分层

每个Window 都有对应的 z-ordered,层级大的,覆盖在层级小的 Window 上面

  • 应用Window 层级范围 1-99
    WindowManager.FIRST_APPLICATION_WINDOW - WindowManager.LAST_APPLICATION_WINDOW
  • 子Window 层级范围 1000-1999
    WindowManager.FIRST_SUB_WINDOW - WindowManager.LAST_SUB_WINDOW
  • 系统Window 层级范围 2000-2999
    WindowManager.FIRST_SYSTEM_WINDOW - WindowManager.LAST_SYSTEM_WINDOW

想要 Window 位于所有Window的最顶层,采用最大的层级即可

  • 设置type为系统Window
    需要权限
    <uses-permission android:name=“android.permission.SYSTEM_ALERT_WINDOW”/>
4、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);
}

public interface WindowManager extends ViewManager {
}

可拖动的Window,实现
根据手指的位置来设定 LayoutParams 中的 x、y的值。
首先给View 设置 OnTouchListener ,然后在 onTouch方法中更新View的位置
mWindowManager.updateViewLayout()

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

Window内部机制

Window是一个抽象的概念,每个Window,都对应着一个 View 和一个 ViewRootImpl
Window 和 View 通过 ViewRootImpl 来建立联系

Window并不实际存在,它是以View的形式存在的,View在是Window存在的实体

1、Window 的添加过程

WindowManagerImpl 中,把操作委托给了 WindowManagerGlobal

public final class WindowManagerImpl implements WindowManager {
	private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
	
	@Override
	public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
		applyDefaultToken(params);
		mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
	}

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

public final class WindowManagerGlobal {
	// 存储所有Window 对应的 View
	private final ArrayList<View> mViews = new ArrayList<View>();
	
	// 存储所有Window 对应的 ViewRootImpl
	private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
	
	// 存储所有Window 对应的布局参数
	private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
	
	// 存储正在被删除的 View 对象,那些调用了 removeView 方法,但是删除操作还未完成的Window对象
	private final ArraySet<View> mDyingViews = new ArraySet<View>();
	
	public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
		//1、检查参数是否合法
		...
		
		final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
		// 如果是 子Window 需要调整布局参数
		if (parentWindow != null) {
			parentWindow.adjustLayoutParamsForSubWindow(wparams);
		}
		...
		//2、创建 ViewRootImpl 并将 View 添加到列表中
		root = new ViewRootImpl(view.getContext(), display);
		view.setLayoutParams(wparams);
		mViews.add(view);
		mRoots.add(root);
		mParams.add(wparams);
		
		try {
			//3、通过 ViewRootImpl 更新界面并完成Window添加过程
			root.setView(view, wparams, panelParentView);
		} catch (RuntimeException e) {
			// BadTokenException or InvalidDisplayException, clean up.
			if (index >= 0) {
				removeViewLocked(index, true);
			}
			throw e;
		}
	}
}

ViewRootImpl.setView()中,通过 WindowSession 完成 Window 的添加
IWindowSession 【是一个Binder 对象,真正实现类是 com.android.server.wm.Session】
Window 的添加过程是一次 IPC 调用

public final class ViewRootImpl implements ViewParent, ... {
	public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
		...
		//完成View 的绘制
		requestLayout();
		...
		try {
               mOrigWindowType = mWindowAttributes.type;
               mAttachInfo.mRecomputeGlobalAttributes = true;
               collectViewAttributes();
               
			/* 通过 WindowSession 完成 Window 的添加
			 * IWindowSession 【是一个Binder 对象,真正实现类是 Session】
			 * com.android.server.wm.Session 
			 * Window 的添加过程是一次 IPC 调用
			 */
               res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                       getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                       mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                       mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
           } catch (RemoteException e) {
               mAdded = false;
               mView = null;
               mAttachInfo.mRootView = null;
               mInputChannel = null;
               mFallbackEventHandler.setView(null);
               unscheduleTraversals();
               setAccessibilityFocus(null, null);
               throw new RuntimeException("Adding window failed", e);
           }
		...
	}
	
	@Override
	public void requestLayout() {
		if (!mHandlingLayoutInLayoutRequest) {
			checkThread();
			mLayoutRequested = true;
			//开始绘制 View
			scheduleTraversals();
		}
	}
}

IWindowSession 【是一个Binder 对象,真正实现类是 com.android.server.wm.Session】

package com.android.server.wm.Session;
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
	@Override
	public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
			int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
			Rect outStableInsets, Rect outOutsets,
			DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
		// 调用 WindowManagerService.addWindow()实现添加
		return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
				outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
	}
}

最终 Window 的添加交给了WindowManagerService.addWindow();处理。
WindowManagerService中,会为每个应用保留一个单独的 Session。

Window添加过程

WindowManager.addView
WindowManagerGlobal.addView
ViewRootImpl.setView
ViewRootImpl#requestLayout
measure layout draw绘制流程
IWindowSession.addToDisplay
com.android.server.wm.Session.addToDisplay
WindowManagerService.addWindow
2、Window 的删除过程
public final class WindowManagerGlobal {
	// 存储所有Window 对应的 View
	private final ArrayList<View> mViews = new ArrayList<View>();
	// 存储所有Window 对应的 ViewRootImpl
	private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
	// 存储所有Window 对应的布局参数
	private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
	// 存储正在被删除的 View 对象,那些调用了 removeView 方法,但是删除操作还未完成的Window对象
	private final ArraySet<View> mDyingViews = new ArraySet<View>();
	
	// 删除
	public void removeView(View view, boolean immediate) {
		if (view == null) {
			throw new IllegalArgumentException("view must not be null");
		}
		synchronized (mLock) {
			// 从 mViews 中找到对应的 View 索引
			int index = findViewLocked(view, true);
			View curView = mRoots.get(index).getView();
			// 根据索引删除View
			removeViewLocked(index, immediate);
			if (curView == view) {
				return;
			}

			throw new IllegalStateException("Calling with view " + view
					+ " but the ViewAncestor is attached to " + curView);
		}
	}
	
	private void removeViewLocked(int index, boolean immediate) {
		// 根据View 的索引,找到 对应的 ViewRootImpl
		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());
			}
		}
		//  ViewRootImpl.die() 删除View
		boolean deferred = root.die(immediate);
		if (view != null) {
			view.assignParent(null);
			if (deferred) {//如果是异步删除,
				//将View,添加到 mDyingViews 待删除列表
				mDyingViews.add(view);
			}
		}
	}
}

public final class ViewRootImpl implements ViewParent, ... {
	/*
	 * immediate 表示是否立即删除;
	 *  		 true  立即删除,表示表示同步删除
	 * 			 false 异步删除
	 */
	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()方法执行删除
			doDie();
			return false;
		}
		if (!mIsDrawing) {
			destroyHardwareRenderer();
		} else {
			Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
					"  window=" + this + ", title=" + mWindowAttributes.getTitle());
		}
		/* 如果是异步删除,发送一个 MSG_DIE 给 ViewRootImpl 的 ViewRootHandler 
		 * handler 收到 消息后,执行  doDie() 方法
		 */
		mHandler.sendEmptyMessage(MSG_DIE);
		return true;
	}
}	

	doDie()内会调用  dispatchDetachedFromWindow() 方法,真正删除View的逻辑在 dispatchDetachedFromWindow 中。
	
	dispatchDetachedFromWindow:
		i.  垃圾回收相关工作,清除数据和消息、移除回调
		
		ii. 通过Sess
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值