深入分析TaskView源码之触摸相关

问题背景

hi,粉丝朋友们:

大家好!android 10以后TaskView作为替代ActivityView的容器,在课程的分屏pip自由窗口专题也进行了相关的详细介绍分析

这里再补充一下相关的TaskView和桌面内嵌情况下的触摸分析

主要问题点:

1、明明TaskView属于CarLauncher的一个View,凭啥触摸自己的TaskView事件会让TaskView的Activity接收到

2、TaskView的Activity是怎么可以接收事件的呢?桌面怎么做到单独只排除TaskView其他地方都可以触摸呢?

哈哈,简单总结就是要搞清楚TaskView触摸响应原理。

问题分析切入点

一般说道触摸问题分析,一想到当然是窗口和input部分的dump信息分析

1、dumpsys activity containers

bash 复制代码
#1 DefaultTaskDisplayArea type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=[0,0][0,0] bounds=[0,0][1408,792]
        #2 Task=1000011 type=standard mode=multi-window override-mode=multi-window requested-bounds=[404,76][1408,696] bounds=[404,76][1408,696]
         #0 ActivityRecord{38f55a5 u10 com.android.car.mapsplaceholder/.MapsPlaceholderActivity t1000011} type=standard mode=multi-window override-mode=undefined requested-bounds=[0,0][0,0] bounds=[404,76][1408,696]
          #0 60a6fdf com.android.car.mapsplaceholder/com.android.car.mapsplaceholder.MapsPlaceholderActivity type=standard mode=multi-window override-mode=undefined requested-bounds=[0,0][0,0] bounds=[404,76][1408,696]
        #1 Task=1 type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1408,792]
         #0 Task=1000006 type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1408,792]
          #0 ActivityRecord{185661 u10 com.android.car.carlauncher/.CarLauncher t1000006} type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1408,792]
           #0 6845fdb com.android.car.carlauncher/com.android.car.carlauncher.CarLauncher type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1408,792]

明显看到地图Activity的Task,windowmode属于multi-window

#2 Task=1000011 type=standard mode=multi-window override-mode=multi-window requested-bounds=[404,76][1408,696] bounds=[404,76][1408,696]

在CarLauncher 的Task ,windowmode为fullscreen

#1 Task=1 type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1408,792]

注意一下地图的Task的bounds区域[404,76][1408,696] ,而CarLauncher的区域属于全屏的 bounds=[0,0][1408,792]

这么一看确实桌面的显示bound是包含了地图的Bound

所以这里可以初步理解为:

TaskView的地图是可以独立接受触摸时间的,因为它本身是独立的bounds区域,触摸到了这个区域就行

但是因为和CarLauncher有重叠,那么CarLauncher对这个TaskView部分的区域触摸是怎么处理的,会接受这一部分的触摸事件吗?毕竟这区域又是CarLauncher的一个TaskView区域,地图Activity也只是TaskView显示的内容而已

哈哈,这里就需要使用我们dumpsys input来解密

adb shell dumpsys input:

cpp 复制代码
Input Dispatcher State:
  DispatchEnabled: true
  DispatchFrozen: false
  InputFilterEnabled: false
  FocusedDisplayId: 0
  FocusedApplications:
    displayId=0, name='ActivityRecord{38f55a5 u10 com.android.car.mapsplaceholder/.MapsPlaceholderActivity t1000011}', dispatchingTimeout=5000ms
  FocusedWindows:
    displayId=0, name='60a6fdf com.android.car.mapsplaceholder/com.android.car.mapsplaceholder.MapsPlaceholderActivity'
  FocusRequests:
    displayId=0, name='60a6fdf com.android.car.mapsplaceholder/com.android.car.mapsplaceholder.MapsPlaceholderActivity' result='OK'
  Pointer Capture Requested: false
  Current Window with Pointer Capture: None
  TouchStates: <no displays touched>
  Display: 0
    logicalSize=1408x792
        transform (ROT_0) (IDENTITY)
    Windows:
      0: name='aa0d3fc BottomCarSystemBar', id=77, displayId=0, inputConfig=NOT_FOCUSABLE | TRUSTED_OVERLAY | WATCH_OUTSIDE_TOUCH, alpha=1.00, frame=[0,696][1408,792], globalScale=1.000000, applicationInfo.name=, applicationInfo.token=<null>, touchableRegion=[0,696][1408,792], ownerPid=1375, ownerUid=10150, dispatchingTimeout=5000ms, hasToken=0x7c04338b1ad0, touchOcclusionMode=BLOCK_UNTRUSTED
        transform (ROT_0) (TRANSLATE)
            1.0000  0.0000  -0.0000
            0.0000  1.0000  -696.0000
            0.0000  0.0000  1.0000
      1: name='e8091e TopCarSystemBar', id=76, displayId=0, inputConfig=NOT_FOCUSABLE | TRUSTED_OVERLAY | WATCH_OUTSIDE_TOUCH, alpha=1.00, frame=[0,0][1408,76], globalScale=1.000000, applicationInfo.name=, applicationInfo.token=<null>, touchableRegion=[0,0][1408,76], ownerPid=1375, ownerUid=10150, dispatchingTimeout=5000ms, hasToken=0x7c04338d6310, touchOcclusionMode=BLOCK_UNTRUSTED
        transform (ROT_0) (IDENTITY)
      2: name='aff2cfa com.android.car.rotary', id=101, displayId=0, inputConfig=NOT_FOCUSABLE | PREVENT_SPLITTING | TRUSTED_OVERLAY | WATCH_OUTSIDE_TOUCH, alpha=1.00, frame=[1408,76][1408,76], globalScale=1.000000, applicationInfo.name=, applicationInfo.token=<null>, touchableRegion=<empty>, ownerPid=1871, ownerUid=1010088, dispatchingTimeout=5000ms, hasToken=0x7c04338e0bd0, touchOcclusionMode=USE_OPACITY
        transform (ROT_0) (TRANSLATE)
            1.0000  0.0000  -1408.0000
            0.0000  1.0000  -76.0000
            0.0000  0.0000  1.0000
      3: name='2cdb9e1 ActivityRecordInputSink com.aospinsight.dummyaidlapp/.MainActivity', id=194, displayId=0, inputConfig=NO_INPUT_CHANNEL | NOT_VISIBLE | NOT_FOCUSABLE | NOT_TOUCHABLE, alpha=1.00, frame=[0,0][0,0], globalScale=0.000000, applicationInfo.name=, applicationInfo.token=<null>, touchableRegion=[-14079,-7919][14080,7920], ownerPid=701, ownerUid=1000, dispatchingTimeout=0ms, hasToken=<null>, touchOcclusionMode=BLOCK_UNTRUSTED
        transform (ROT_0) (IDENTITY)
      4: name='6845fdb com.android.car.carlauncher/com.android.car.carlauncher.CarLauncher', id=205, displayId=0, inputConfig=TRUSTED_OVERLAY, alpha=1.00, frame=[0,0][1408,792], globalScale=1.000000, applicationInfo.name=ActivityRecord{185661 u10 com.android.car.carlauncher/.CarLauncher t1000006}, applicationInfo.token=0x7c04e3536310, touchableRegion=[0,0][1408,76]|[0,76][404,696]|[0,696][1408,792], ownerPid=1658, ownerUid=1010079, dispatchingTimeout=5000ms, hasToken=0x7c04338f3cd0, touchOcclusionMode=BLOCK_UNTRUSTED
        transform (ROT_0) (IDENTITY)
      5: name='60a6fdf com.android.car.mapsplaceholder/com.android.car.mapsplaceholder.MapsPlaceholderActivity', id=223, displayId=0, inputConfig=0x0, alpha=1.00, frame=[404,76][1408,696], globalScale=1.000000, applicationInfo.name=ActivityRecord{38f55a5 u10 com.android.car.mapsplaceholder/.MapsPlaceholderActivity t1000011}, applicationInfo.token=0x7c04e355be70, touchableRegion=[404,76][1408,696], ownerPid=22792, ownerUid=1010122, dispatchingTimeout=5000ms, hasToken=0x7c0433933dd0, touchOcclusionMode=BLOCK_UNTRUSTED
        transform (ROT_0) (TRANSLATE)
            1.0000  0.0000  -404.0000
            0.0000  1.0000  -76.0000
            0.0000  0.0000  1.0000
      6: name='a9ab86e ActivityRecordInputSink com.android.car.mapsplaceholder/.MapsPlaceholderActivity', id=221, displayId=0, inputConfig=NO_INPUT_CHANNEL | NOT_FOCUSABLE, alpha=1.00, frame=[404,76][404,76], globalScale=0.000000, applicationInfo.name=, applicationInfo.token=<null>, touchableRegion=[404,76][1408,696], ownerPid=701, ownerUid=1000, dispatchingTimeout=0ms, hasToken=<null>, touchOcclusionMode=BLOCK_UNTRUSTED
        transform (ROT_0) (TRANSLATE)
            1.0000  0.0000  -404.0000
            0.0000  1.0000  -76.0000
            0.0000  0.0000  1.0000
      7: name='c2d4eae ActivityRecordInputSink com.android.car.carlauncher/.CarLauncher', id=126, displayId=0, inputConfig=NO_INPUT_CHANNEL | NOT_FOCUSABLE, alpha=1.00, frame=[0,0][0,0], globalScale=0.000000, applicationInfo.name=, applicationInfo.token=<null>, touchableRegion=[-14079,-7919][14080,7920], ownerPid=701, ownerUid=1000, dispatchingTimeout=0ms, hasToken=<null>, touchOcclusionMode=BLOCK_UNTRUSTED

这里我们只需要重点关注窗口4和窗口5:

--桌面Activity

4: name='6845fdb com.android.car.carlauncher/com.android.car.carlauncher.CarLauncher', id=205, displayId=0, inputConfig=TRUSTED_OVERLAY, alpha=1.00, frame=[0,0][1408,792], globalScale=1.000000, applicationInfo.name=ActivityRecord{185661 u10 com.android.car.carlauncher/.CarLauncher t1000006}, applicationInfo.token=0x7c04e3536310, touchableRegion=[0,0][1408,76]|[0,76][404,696]|[0,696][1408,792], ownerPid=1658, ownerUid=1010079, dispatchingTimeout=5000ms, hasToken=0x7c04338f3cd0, touchOcclusionMode=BLOCK_UNTRUSTED

transform (ROT_0) (IDENTITY)

--地图Activity

5: name='60a6fdf com.android.car.mapsplaceholder/com.android.car.mapsplaceholder.MapsPlaceholderActivity', id=223, displayId=0, inputConfig=0x0, alpha=1.00, frame=[404,76][1408,696], globalScale=1.000000, applicationInfo.name=ActivityRecord{38f55a5 u10 com.android.car.mapsplaceholder/.MapsPlaceholderActivity t1000011}, applicationInfo.token=0x7c04e355be70, touchableRegion=[404,76][1408,696], ownerPid=22792, ownerUid=1010122, dispatchingTimeout=5000ms, hasToken=0x7c0433933dd0, touchOcclusionMode=BLOCK_UNTRUSTED

transform (ROT_0) (TRANSLATE)

1.0000 0.0000 -404.0000

0.0000 1.0000 -76.0000

0.0000 0.0000 1.0000

明显发现触摸派发window层级方面还是以桌面为顶部window,地图为底下window,这样其实也可以理解,我们上面的说的情况一样,其实整个画面都是CarLauncher的,地图Activity也只是CarLauncher的一个View而已。所以当然把桌面作为顶部的派发事件window完全没有问题。

但是问题来了,桌面把fullscreen面积占了?那么怎么才可以轮到地图Activity

大家注意看看桌面的window的这个属性

touchableRegion=[0,0][1408,76]|[0,76][404,696]|[0,696][1408,792],

是不是有个touchableRegion,对他就是真相,他负责了划定CarLauncher这个window可以响应的触摸区域,这里明显看出,这个区域划分了看了如下部分:

0,76\]\[404,696\]\| ----排除TaskView的的地图Activity区域 正常地图Activity的区域是\[404,76\]\[1408,696\]从404开始到屏幕最右 \[0,76\]\[404,696\]\| 明显是只从0到404就截止了 这里就说明了CarLauncher虽然在顶部,但是他的touchRegion根本没有包含地图Activity的区域,所以派发事件时候根本不会抢地图Activity的,即地图区域的事件不会派发到桌面 总结: 问题关键找到了,就是靠touchRegion来让CarLauncher不接受地图Activity的Region ### 哪里设置的TouchRegion呢? 这里如果直接寻找比较麻烦可以用dumpsys window windows命令看看window是不是带了 ![在这里插入图片描述](https://file.jishuzhan.net/article/1726835491011039234/1bc837f0b2a9c8604962b712e31de3b5.webp) 下面就来分析这个桌面的TouchRegion是在哪里设置的呢?来看看对应dump代码 frameworks/base/services/core/java/com/android/server/wm/WindowState.java ```cpp if (dumpAll) { pw.println(prefix + "mGivenContentInsets=" + mGivenContentInsets.toShortString(sTmpSB) + " mGivenVisibleInsets=" + mGivenVisibleInsets.toShortString(sTmpSB)); if (mTouchableInsets != 0 || mGivenInsetsPending) { pw.println(prefix + "mTouchableInsets=" + mTouchableInsets + " mGivenInsetsPending=" + mGivenInsetsPending); Region region = new Region(); getTouchableRegion(region); pw.println(prefix + "touchable region=" + region); } pw.println(prefix + "mFullConfiguration=" + getConfiguration()); pw.println(prefix + "mLastReportedConfiguration=" + getLastReportedConfiguration()); } //再来看看getTouchableRegion方法 /** Get the touchable region in global coordinates. */ void getTouchableRegion(Region outRegion) { final Rect frame = mWindowFrames.mFrame; switch (mTouchableInsets) { default: case TOUCHABLE_INSETS_FRAME: outRegion.set(frame); break; case TOUCHABLE_INSETS_CONTENT: applyInsets(outRegion, frame, mGivenContentInsets); break; case TOUCHABLE_INSETS_VISIBLE: applyInsets(outRegion, frame, mGivenVisibleInsets); break; case TOUCHABLE_INSETS_REGION: {//走的是这里 outRegion.set(mGivenTouchableRegion); if (frame.left != 0 || frame.top != 0) { outRegion.translate(frame.left, frame.top); } break; } } cropRegionToRootTaskBoundsIfNeeded(outRegion); subtractTouchExcludeRegionIfNeeded(outRegion); } ``` 那么这里的mGivenTouchableRegion是哪里设置的呢? frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java ```cpp void setInsetsWindow(Session session, IWindow client, int touchableInsets, Rect contentInsets, Rect visibleInsets, Region touchableRegion) { int uid = Binder.getCallingUid(); int pid = Binder.getCallingPid(); final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { WindowState w = windowForClientLocked(session, client, false); if (DEBUG_LAYOUT) Slog.d(TAG, "setInsetsWindow " + w + ", contentInsets=" + w.mGivenContentInsets + " -> " + contentInsets + ", visibleInsets=" + w.mGivenVisibleInsets + " -> " + visibleInsets + ", touchableRegion=" + w.mGivenTouchableRegion + " -> " + touchableRegion + ", touchableInsets " + w.mTouchableInsets + " -> " + touchableInsets); if (w != null) { w.mGivenInsetsPending = false; w.mGivenContentInsets.set(contentInsets); w.mGivenVisibleInsets.set(visibleInsets); w.mGivenTouchableRegion.set(touchableRegion);//这里进行的设置 w.mTouchableInsets = touchableInsets; if (w.mGlobalScale != 1) { w.mGivenContentInsets.scale(w.mGlobalScale); w.mGivenVisibleInsets.scale(w.mGlobalScale); w.mGivenTouchableRegion.scale(w.mGlobalScale); } w.setDisplayLayoutNeeded(); w.updateSourceFrame(w.getFrame()); mWindowPlacerLocked.performSurfacePlacement(); w.getDisplayContent().getInputMonitor().updateInputWindowsLw(true); // We need to report touchable region changes to accessibility. if (mAccessibilityController.hasCallbacks()) { mAccessibilityController.onSomeWindowResizedOrMovedWithCallingUid( uid, w.getDisplayContent().getDisplayId()); } } } } finally { Binder.restoreCallingIdentity(origId); } } ``` 那么接下来再看看谁进行的setInsetsWindow调用: frameworks/base/services/core/java/com/android/server/wm/Session.java ```cpp @Override public void setInsets(IWindow window, int touchableInsets, Rect contentInsets, Rect visibleInsets, Region touchableArea) { mService.setInsetsWindow(this, window, touchableInsets, contentInsets, visibleInsets, touchableArea); } ``` 那么是谁调用了setInsets呢? frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java ```cpp public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) { // TODO(b/176854108): Consider to move the logic into gatherTransparentRegions since this // is dependent on the order of listener. // If there are multiple TaskViews, we'll set the touchable area as the root-view, then // subtract each TaskView from it. if (inoutInfo.touchableRegion.isEmpty()) { inoutInfo.setTouchableInsets( ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); View root = getRootView(); root.getLocationInWindow(mTmpLocation); mTmpRootRect.set(mTmpLocation[0], mTmpLocation[1], root.getWidth(), root.getHeight()); inoutInfo.touchableRegion.set(mTmpRootRect); } getLocationInWindow(mTmpLocation); mTmpRect.set(mTmpLocation[0], mTmpLocation[1], mTmpLocation[0] + getWidth(), mTmpLocation[1] + getHeight()); inoutInfo.touchableRegion.op(mTmpRect, Region.Op.DIFFERENCE); if (mObscuredTouchRegion != null) { inoutInfo.touchableRegion.op(mObscuredTouchRegion, Region.Op.UNION); } } ``` 然后在调用对于的setInsets 在 performTraversals() 中进行相关的调用操作 frameworks/base/core/java/android/view/ViewRootImpl.java ![在这里插入图片描述](https://file.jishuzhan.net/article/1726835491011039234/7fe3c84caf16fbf3e74e50f1fe0e7041.webp) setInsets ![在这里插入图片描述](https://file.jishuzhan.net/article/1726835491011039234/f118e9030f70c5977ab20e2b5b7ba8c6.webp) 更多framework干货课程如下(需要的可以私聊马哥 获取优惠 +V :androidframework007): ![在这里插入图片描述](https://file.jishuzhan.net/article/1726835491011039234/635d59976030ca6379bcc86a939767a2.webp)

相关推荐
a31582380616 小时前
SnapdragonCamera骁龙相机源码解析
android·数码相机·framework·高通
庆 、7 天前
Django REST framework 源码剖析-认证器详解(Authentication)
后端·python·django·framework·restful·authentication
千里马学框架11 天前
安卓15/aosp15/lineage21使用brunch编译老是报错OOM内存不足
android·车载系统·framework·系统开发·aosp·lineage
亚瑟-灰太狼24 天前
preloaded-classes裁剪
framework
Android小码家1 个月前
Android Framework startServices 流程
android·framework
画个太阳作晴天1 个月前
Android13修改多媒体默认音量
android·framework
Android小码家1 个月前
Android SystemUI开发(一)
android·framework·systemui
猿小帅011 个月前
androidnetflix手机版遥控器操作
android·framework
朝阳391 个月前
HTML【详解】input 标签
html·input
ItJavawfc2 个月前
Android13-包安装器PackageInstaller-之apk安装流程
framework·pms·packageinstal·包安装器·安装流程