Android 8应用获取上层系统全局Window的区域

Android 8应用获取上层系统全局Window的区域的实现

简介

项目中遇到了一个需求,就是需要知道一个Activity或者通过WindowManager.addView添加的上层所有视图的区域,比如其他Activity,或者其他windowManager通过addView接口添加的window,以供业务实现使用。同时还需要在window更新位置,消失,显示时候,通知客户端。
经过对源码的了解,确定了大体方案。

原理

我们先了解下系统添加window的大体流程。

添加window流程
我们就通过这个流程,添加相应的获取接口到WindowManagerService,得到结果,再返回给我们。下面了解下相关类的基本信息。

  1. WindowManager继承自ViewManager,WindowManagerImpl是客户端WindowManager管理接口的实现,WindowManagerImpl内部维护一个单例的WindowManagerGlobal对象,WindowManagerImpl通过该对象转发客户端的窗口管理请求。客户端在创建窗口时首先调用getWindowManager获得本地窗口管理对象,并调用其addView、removeView、UpdateViewLayout为窗口进行布局控制

  2. ViewManagerImp是Viewmanager的实现,该类并没有直接实现Window的操作,而是由WindowmanagerGlobal进行操作。

  3. WindowManagerGlobal对象内部维护一个ViewRootImpl实例数组和一个View视图对象数组,WindowmanagerGlobal的addView函数首先查看要添加的视图是否已经存在,若不存在则实例化一个ViewRootImpl对象,并把view和ViewRootImpl对象及布局参数保存到本地数组中,接着调用ViewRootImpl对象的setView函数;removeView通过调用ViewRootImpl的die方法进行,最终调用dispatchDetachedFromWindow进行移除;updateViewLayout首先更新View的LayoutParams并替换掉老的LayoutParams,接着更新ViewRootImpl的Layoutparams,通过调用scheduleTraversals对View重新布局

  4. ViewRootImpl是视图处理类,是客户端视图的处理类,客户端的视图通过该类与窗口管理服务交互,因此ViewRootImpl是一个中介类。ViewRootImpl内部包含一个从IWindow.Stub派生的内部类(ViewRootImpl.W),窗口管理服务通过该对象可以与客户端反向通讯。
    setView内部通过requestLayout来完成异步刷新请求,scheduleTraversals实际是View绘制的入口,通过调用performTraversals对View进行measure、layout、draw,最后通过pokeDrawLockIfNeeded进行IPC

  5. Session对象也是一个服务端的桩对象,用来为客户端提供交互接口(IWindowSession),包括窗口的创建、销毁、布局等接口。每个与窗口管理服务交互的进程通常都需要打开一个Session对象来实现交互,用来保证一个Session会话期间窗口状态的一致。Session对象在一个进程新建第一个视图时使用窗口管理服务的的openSession接口函数创建,第一个视图新建期间也创建一个SurfaceSession对象用来实现视图的绘制操作。SurfaceSession对象用来与SurfaceFliger服务建立连接,实现视图及包含的子视图在显示硬件上的实际输出工作。

  6. WindowManagerService服务是一个系统服务类,是整个窗口管理机制实现的核心。我们添加View,最终会调用WMS的addToDisplay,添加相应的WindowState,每个窗口,都用一个WindowState代表。另外还有WIndowToken,DiaplayContent类。

  7. WindowToken将属于同一个应用组件的窗口组织在了一起,WMS对窗口的管理过程中,用WindowToken指代一个应用组件,WindowToken具有令牌的作用,是对应用组件的行为进行规范管理的一个手段。WindowToken由应用组件或其管理者负责向WMS声明并持有。应用组件在需要新的窗口时,必须提供WindowToken以表明自己的身份,并且窗口的类型必须与所持有的WindowToken的类型一致

  8. WindowState表示一个窗口的所有属性,所以它是WMS中事实上的窗口。这些属性将在后面遇到时再做介绍。类似于WindowToken,WindowState在显示组件一侧也有个对应的类型:IWindow.Stub。IWindow.Stub提供了很多与窗口管理相关通知的回调,例如尺寸变化、焦点变化等。另外,从WindowManagerService.addWindow()函数中看到新的WindowState被保存到mWindowMap中,键值为IWindow的Bp端。mWindowMap是整个系统所有窗口的一个全集。

  9. 如果说WindowToken按照窗口之间的逻辑关系将其分组,那么DisplayContent则根据窗口的显示位置将其分组。隶属于同一个DisplayContent的窗口将会被显示在同一个屏幕中。每一个DisplayContent都对应这一个唯一的ID,在添加窗口时可以通过指定这个ID决定其将被显示在那个屏幕中。DisplayContent是一个非常具有隔离性的一个概念。处于不同DisplayContent的两个窗口在布局、显示顺序以及动画处理上不会产生任何耦合。因此,就这几个方面来说,DisplayContent就像一个孤岛,所有这些操作都可以在其内部独立执行。因此,这些本来属于整个WMS全局性的操作,变成了DisplayContent内部的操作了。
    一个 DisplayContent就包含了这个显示屏幕里的所有窗口,并且是被按Z序排列的,此类继承WindowContainer,通过forAllWindows(Consumer callback, boolean traverseTopToBottom)就可以对其child进行遍历,通过这个,我们就可以根据指定窗口,获取其上部的WIndowState,从而通过WIndowState.mFrame获取指定Rect区域了。

  10. window昨天更新回调通知客户端,这块,还没细研究,就新建了个回调,然后,触发点临时加在了DisplayContent的Consumer mPerformLayout里,因为这里系统窗口变化,每次都会更新,但是调用会比较频繁,监控端要做判断处理。

具体实现

  1. MainActivity
    获取WMS回调通知
onCreate->WindowManager windowManager = getWindowManager();
windowManager.setSystemWindowChangeCallback(root, new SystemWindowChangeCallback() {
@Override
public void onSystemWindowChangeCallback() {
LogUtil.d(TAG,"onSystemWindowChangeCallback...");
mMainHandler.removeCallbacks(mSystemWindowChangeCallback);
mMainHandler.postDelayed(mSystemWindowChangeCallback, 50);
}
});

获取Activity上层窗口区域列表:

private Runnable mSystemWindowChangeCallback = new Runnable() {
@Override
public void run() {
View root = getWindow().peekDecorView();
List<Rect> listRect = AboveLayerManager.getInstance().getAboveWindowRectListByView(root, MainActivity.this);
LogUtil.d(TAG,"listRect:"+listRect);
}
};
  1. 获取Activity上层区域系统接口,通过WindowManager添加的view可以直接将添加的view传入,即获取此view上层的区域。
// 使用反射调用,在as中引入framework.jar包后也可以直接
//使用windowManager.getAboveWindowRectListByView
public ArrayList<Rect> getAboveWindowRectListByView(View view, Context context) {
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
ArrayList<Rect> ret = null;
LogUtil.d(TAG, "windowManager.getClass():" + windowManager.getClass());
try {
Method method_getConfigData = windowManager.getClass().getMethod("getAboveWindowRectListByView", View.class);
ret = (ArrayList<Rect>) method_getConfigData.invoke(windowManager, view);
LogUtil.d(TAG, "getAboveWindowRectListByView ret:" + ret);
} catch (Exception e) {
e.printStackTrace();
}
return ret;
}
  1. 在WindowManager.java接口添加以下接口:
public List<Rect> getAboveWindowRectListByView(View view);

public void setSystemWindowChangeCallback(View view, SystemWindowChangeCallback callback);

public void removeSystemWindowChangeCallback(View view);

4.WindowManagerImpl里添加实现接口:

@Override
public List<Rect> getAboveWindowRectListByView(View view) {
return mGlobal.getAboveWindowRectListByView(view);
}

@Override
public void setSystemWindowChangeCallback(View view, SystemWindowChangeCallback callback) {
mGlobal.setSystemWindowChangeCallback(view, callback);
}

@Override
public void removeSystemWindowChangeCallback(View view) {
mGlobal.removeSystemWindowChangeCallback(view);
}
  1. 在WindowManagerGlobal.java里实现如下代码:
public void setSystemWindowChangeCallback(View view, SystemWindowChangeCallback callback) {
if (view == null) {
throw new IllegalArgumentException("Get above list, view must not be null");
}
synchronized (mLock) {
int index = findViewLocked(view, true); 
ViewRootImpl root = mRoots.get(index);
if (root != null) {
root.setSystemWindowChangeCallback(callback);
}
}
}

public void removeSystemWindowChangeCallback(View view) {
if (view == null) {
throw new IllegalArgumentException("Remove system window callback, view must not be null");
}
synchronized (mLock) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
if (root != null) {
root.removeSystemWindowChangeCallback();
}
}
}

public List<Rect> getAboveWindowRectListByView(View view) {
if (view == null) {
throw new IllegalArgumentException("Get above list, view must not be null");
}
synchronized (mLock) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
if (root != null) {
return root.getAboveWindowRectList();
} else {
return null;
}
}
}
  1. 修改IWindow.aidl接口:

增加void systemWindowChanged(); 用来WMS通知应用layout更新

  1. ViewRootImpl.java添加如下代码:
    回调代码在static class W extends IWindow.Stub里实现systemWindowChanged:
@Override
public void systemWindowChanged() {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.systemWindowChanged();
}
} 

然后添加函数实现发送handler异步处理消息:

public void systemWindowChanged() {
Slog.d(TAG,"systemWindowChanged");
final int what = MSG_SYSTEM_WINDOW_CHANGE;
mHandler.removeMessages(what);
mHandler.sendEmptyMessage(what);
}

然后添加通知处理消息函数:

void notifySystemWindowChanged() {
	Slog.d(mTag, "notifySystemWindowChanged.....callback:" + mSystemWindowChangeCallback);
	if (mSystemWindowChangeCallback != null) {
	mSystemWindowChangeCallback.onSystemWindowChangeCallback();
	}
}

在handleMessage里添加系统window更新消息处理:

case MSG_SYSTEM_WINDOW_CHANGE: {
notifySystemWindowChanged();
} break;

定义mSystemWindowChangeCallback,一个ViewRootImpl对应一个callback。

获取区域代码:

public List<Rect> getAboveWindowRectList() {
Slog.d(mTag, "getAboveWindowRectListByView");
List<Rect> ret = null;
try {
ret = mWindowSession.getAboveWindowRectList(mWindow, mSeq, mDisplay.getDisplayId());
} catch (RemoteException e) {
e.printStackTrace();
}
Slog.d(mTag, "getAboveWindowRectListByView: ret " + ret);
return ret;
}
  1. 修改IWindowSession接口:
    List getAboveWindowRectList(IWindow window, int seq, in int layerStackId);
    在Session.java实现接口:
@Override
public List<Rect> getAboveWindowRectList(IWindow window, int seq, int displayId) {
return mService.getAboveWindowRectList(this, window, seq, displayId);
}
  1. WindowManagerService.java代码添加
public List<Rect> getAboveWindowRectList(Session session, IWindow client, int seq, int displayId) {
//Slog.d(TAG_WM, "getAboveWindowRectList seq:"+ seq +" displayId:" + displayId);
synchronized(mWindowMap) {
Slog.d(TAG_WM, "getAboveWindowRectListt seq:"+ seq +" displayId:" + displayId);
final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
if (displayContent == null) {
Slog.d(TAG_WM, "Attempted to get a display that does not exist: "
+ displayId + ". Aborting getAboveWindowRectList");
return null;
}

if (!mWindowMap.containsKey(client.asBinder())) {
Slog.d(TAG_WM, "Window " + client + " is not be added in function getAboveWindowRectList");
return null;
}

WindowState state = mWindowMap.get(client.asBinder());
if (state != null) {
return displayContent.getAboveWindowRectList(state);
} else {
Slog.d(TAG_WM, "getWindowState null in function getAboveWindowRectList");
}
}
Slog.d(TAG_WM, "getAboveWindowRectList return null seq:"+ seq +" displayId:" + displayId);
return null;
}
  1. DisplayContent.java里添加
    layout通知:
    在private final Consumer mPerformLayout = w -> {}里添加
if (!gone) {
try {
w.mClient.systemWindowChanged();
} catch (RemoteException e) {
// so sorry
}
}

获取列表:

boolean matchState = false;
public List<Rect> getAboveWindowRectList(WindowState state) {
if (state == null) {
Slog.w(TAG, "getAboveWindowRectList state null");
return null;
}
Slog.d(TAG, "getAboveWindowRectList state:" + state.getName() + " mBaseDisplayWidth:" + mBaseDisplayWidth + " mBaseDisplayHeight:" + mBaseDisplayHeight);
ArrayList<Rect> rectList = new ArrayList();
matchState = false;
forAllWindows((w) -> {
Slog.d(TAG, "getAboveWindowRectList iterate:" + w.getName()+" frame:"+w.mFrame);
if (w.getName().equals(state.getName())) {
Slog.d(TAG, "getAboveWindowRectList find same WindowState");
matchState = true;
}

final boolean gone = (mTmpWindow != null && mService.mPolicy.canBeHiddenByKeyguardLw(w))
|| w.isGoneForLayoutLw();
if (!gone && !matchState && w.isReadyForDisplay() && w.mFrame.width() > 0 && w.mFrame.width() < mBaseDisplayWidth 
&& w.mFrame.height() > 0 && w.mFrame.height() < mBaseDisplayHeight) {
Slog.d(TAG, "getAboveWindowRectList add : " + w);
rectList.add(w.mFrame);
} else {
Slog.d(TAG, "getAboveWindowRectList skip : " + w + " gone:" + gone + " matchState:" + matchState + " w.mFrame:" + w.mFrame);
}
}, true /* traverseTopToBottom */);
return rectList;
}

至此,系统添加接口和功能完成。
编译apk,framework.jar和service.jar
得到结果如下:
应用获取的Rect列表
上层WIndow
对你有帮助,帮忙点个赞,加个关注,下次不迷路,后续也会写一些其他的文章,这篇是处女篇,不好的地方还请担待。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值