SurfaceFlinge/InputFlinger分析-android画面缩放后依然点击正常原理分析

hi,粉丝朋友们:
这两天刚好在做自由窗口相关国内需求,刚好遇到一个疑惑,那就是画面进行缩放后发现依然触摸画面可以正常反映问题。
在这里插入图片描述

具体疑惑背景

疑问点如下:
坐标是针对屏幕的,按钮也是相对Activity的,Activity本身宽度和屏幕一样只是画面被缩放了,Activity和View其实依然是屏幕的宽度,那么为啥屏幕坐标点点击到缩放Activity时候,按钮依然可以正常相应呢?

疑惑解答坐标追踪:

回忆坐标传递流程:
触摸事件坐标肯定是 inputdispatcher -----> app

inputdispatcher坐标确定

那么先确定inputDispatcher传递坐标,这个inputdispatcher的坐标其实是可以通过dumpys来查看:

  RecentQueue: length=10
    MotionEvent(deviceId=11, eventTime=37449374871000, source=TOUCHSCREEN | STYLUS, displayId=0, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=22.8, yPrecision=11.1, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (1011.9, 909.9)]), policyFlags=0x62000000, age=1525ms
    MotionEvent(deviceId=11, eventTime=37449382843000, source=TOUCHSCREEN | STYLUS, displayId=0, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=22.8, yPrecision=11.1, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (1011.9, 901.0)]), policyFlags=0x62000000, age=1517ms
    MotionEvent(deviceId=11, eventTime=37449390835000, source=TOUCHSCREEN | STYLUS, displayId=0, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=22.8, yPrecision=11.1, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (1011.9, 898.9)]), policyFlags=0x62000000, age=1509ms
    MotionEvent(deviceId=11, eventTime=37449470965000, source=TOUCHSCREEN | STYLUS, displayId=0, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=22.8, yPrecision=11.1, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (1011.9, 901.0)]), policyFlags=0x62000000, age=1429ms
    MotionEvent(deviceId=11, eventTime=37449478951000, source=TOUCHSCREEN | STYLUS, displayId=0, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=22.8, yPrecision=11.1, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (1011.9, 903.0)]), policyFlags=0x62000000, age=1421ms
    MotionEvent(deviceId=11, eventTime=37449486891000, source=TOUCHSCREEN | STYLUS, displayId=0, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=22.8, yPrecision=11.1, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (1011.9, 907.9)]), policyFlags=0x62000000, age=1413ms
    MotionEvent(deviceId=11, eventTime=37449599726000, source=TOUCHSCREEN | STYLUS, displayId=0, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=22.8, yPrecision=11.1, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (1011.9, 904.9)]), policyFlags=0x62000000, age=1300ms
    MotionEvent(deviceId=11, eventTime=37449607604000, source=TOUCHSCREEN | STYLUS, displayId=0, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=22.8, yPrecision=11.1, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (1011.9, 901.0)]), policyFlags=0x62000000, age=1292ms
    MotionEvent(deviceId=11, eventTime=37449615411000, source=TOUCHSCREEN | STYLUS, displayId=0, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=22.8, yPrecision=11.1, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (1011.9, 896.9)]), policyFlags=0x62000000, age=1284ms
    MotionEvent(deviceId=11, eventTime=37449783797000, source=TOUCHSCREEN | STYLUS, displayId=0, action=UP, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=22.8, yPrecision=11.1, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (1011.9, 896.9)]), policyFlags=0x62000000, age=1116ms

明显发现坐标pointers=[0: (1011.9, 901.0)]), 这个属于屏幕的绝对坐标,这个时候在inputdispatcher的传递时候并没有被变化成和Activity的View一致坐标,即这里基本可以断定inputdispatcher传递到app依然是屏幕的绝对坐标。

app端坐标确定

app端的坐标,这里一般可以通过debug调试方式,或之打印log的方式:
在这里插入图片描述

这里在最初的的dispatchInputEvent进行了断点,发现坐标已经变成了 1341,1633了

   private void dispatchInputEvent(int seq, InputEvent event) {
    
    
        mSeqMap.put(event.getSequenceNumber(), seq);
        onInputEvent(event);
    }

即坐标一旦到了java层面其实就已经变成了和Activity正常的坐标了,那么只能继续往app层面地方往下追,这里就需要对native层面的input流程比较了解了,这里可以看马哥的input专题有讲解
在这里插入图片描述
那就来重点看看NativeInputEventReceiver::consumeEvents和InputConsumer.consume方法读取相关input数据情况,最终发现MotionEvent是在void InputConsumer::initializeMotionEvent进行构造出来的:

void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {
    
    
    uint32_t pointerCount = msg->body.motion.pointerCount;
    PointerProperties pointerProperties[pointerCount];
    PointerCoords pointerCoords[pointerCount];
    for (uint32_t i = 0; i < pointerCount; i++) {
    
    
        pointerProperties[i].copyFrom(msg->body.motion.pointers[i].properties);
        pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
        ALOGE("initializeMotionEvent: pointerCount x= %f y = %f" ,pointerCoords[i].getX(),pointerCoords[i].getY());
    }
		//这里是坐标变化的关键转换矩阵
    ui::Transform transform;
    transform.set({
    
    msg->body.motion.dsdx, msg->body.motion.dtdx, msg->body.motion.tx,
                   msg->body.motion.dtdy, msg->body.motion.dsdy, msg->body.motion.ty, 0, 0, 1});
    ui::Transform displayTransform;
    displayTransform.set({
    
    msg->body.motion.dsdxRaw, msg->body.motion.dtdxRaw,
                          msg->body.motion.txRaw, msg->body.motion.dtdyRaw,
                          msg->body.motion.dsdyRaw, msg->body.motion.tyRaw, 0, 0, 1});
    event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source,
                      msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action,
                      msg->body.motion.actionButton, msg->body.motion.flags,
                      msg->body.motion.edgeFlags, msg->body.motion.metaState,
                      msg->body.motion.buttonState, msg->body.motion.classification, transform,
                      msg->body.motion.xPrecision, msg->body.motion.yPrecision,
                      msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition,
                      displayTransform, msg->body.motion.downTime, msg->body.motion.eventTime,
                      pointerCount, pointerProperties, pointerCoords);
    ALOGE("initializeMotionEvent:2  pointerCount x= %f y = %f" ,event->getX(0),event->getY(0));
}

这里加入了相关的打印如下:

06-15 23:40:24.413  1850  1850 E InputTransport: initializeMotionEvent: pointerCount x= 1045.942383 y = 930.961914
06-15 23:40:24.413  1850  1850 E InputTransport: initializeMotionEvent:2  pointerCount x= 1371.884766 y = 1693.923828

即可以看出,其实第一步从socket接受到的event坐标依然是和inputdispatcher一样,但是经过event->initialize方法后,再打印时候就发现已经变化了,变成和实际Activity一样的相匹配的坐标了。
其实本质原因还是因为initialize时候有传递相关的transform影响的,因为getX方法时候实际会调用到对应方法进行坐标转换返回:

 /**
     * Get the X coordinate of the latest sample in this MotionEvent for pointer 'pointerIndex'.
     * Identical to calling getHistoricalX(pointerIndex, getHistorySize()).
     */
    inline float getX(size_t pointerIndex) const {
    
    
        return getAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex);
    }
    float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const {
    
    
    return getHistoricalAxisValue(axis, pointerIndex, getHistorySize());
}

float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,
                                          size_t historicalIndex) const {
    
    
    const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
    //注意这个时候就是会有对应的mTransform进行影响,导致坐标和屏幕坐标产生差异
    return calculateTransformedAxisValue(axis, mSource, mTransform, coords);
}

上面清楚了,那剩下问题就是这个mTransform根源是来自哪里呢?
这里就又要回到inputdispatcher是有传递类似这种mTransform的。

   // Publish the motion event.
                status = connection->inputPublisher
                                 .publishMotionEvent(dispatchEntry->seq,
                                                     dispatchEntry->resolvedEventId,
                                                     motionEntry.deviceId, motionEntry.source,
                                                     motionEntry.displayId, std::move(hmac),
                                                     dispatchEntry->resolvedAction,
                                                     motionEntry.actionButton,
                                                     dispatchEntry->resolvedFlags,
                                                     motionEntry.edgeFlags, motionEntry.metaState,
                                                     motionEntry.buttonState,
                                                     motionEntry.classification,
                                                     dispatchEntry->transform,//这里就是关键的transform也会传递
                                                     motionEntry.xPrecision, motionEntry.yPrecision,
                                                     motionEntry.xCursorPosition,
                                                     motionEntry.yCursorPosition,
                                                     dispatchEntry->rawTransform,
                                                     motionEntry.downTime, motionEntry.eventTime,
                                                     motionEntry.pointerCount,
                                                     motionEntry.pointerProperties, usingCoords);

这个transform其实dumpsys的input中也有展示:

      5: name='692067c com.example.myapplication11/com.example.myapplication11.MainActivity', id=163, displayId=0, inputConfig=0x0, alpha=1.00, frame=[360,84][1080,972], globalScale=1.000000, applicationInfo.name=ActivityRecord{
    
    f9088f2 u0 com.example.myapplication11/.MainActivity} t465}, applicationInfo.token=0x7079fae2c370, touchableRegion=[308,32][1133,1025], ownerPid=1850, ownerUid=10116, dispatchingTimeout=5000ms, hasToken=true, touchOcclusionMode=BLOCK_UNTRUSTED
        transform (ROT_0) (SCALE TRANSLATE)
            2.0000  -0.0000  -720.0000
            -0.0000  2.0000  -168.0000
            0.0000  0.0000  1.0000

可以看到这个transform,大致矩阵即可以看出有放大2倍和偏移-720.0000,-168.0000,也正是这个转换才导致的由屏幕的绝对坐标转换到Activity的绝对坐标,转换后Activity可以获取到正常的坐标进行响应。

那么问题又来了请问inputdispatcher这个transform的转换又来自哪里呢?

哈哈这里就又回到之前input的专题讲解的,这个windowinfo信息实际是来自于SurfaceFlinger的,这里来看看dumpsys SurfaceFlinger是否又相关信息:

* Layer 0x71a7e2b523f0 (com.example.myapplication11/com.example.myapplication11.MainActivity#268)
      isSecure=false geomUsesSourceCrop=true geomBufferUsesDisplayInverseTransform=false geomLayerTransform (ROT_0) (SCALE TRANSLATE)
    0.5000  0.0000  360.0000
    0.0000  0.5000  84.0000
    0.0000  0.0000  1.0000

找到对应的window Layer确实看到了一个矩阵,但是好像这个矩阵和input的矩阵不一样,但是好像有点关系,这个关系是啥呢?哈哈就是一个类是逆向过程,相当于surface显示时候缩小到原来0.5,那么回去时候就需要放大2倍才可以还原。而surfaceflinger这个矩阵就是进行surfacecontrol进行操作的缩放矩阵。

故到此整个环节就已经清楚了,总结一下如下图:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/learnframework/article/details/131240264