Android 全局拖放事件完整调用链路分析

11 阅读4分钟

 

一、应用层调用链

流程图如下

 

1.1 应用层启动拖放

// 组装 数据ClipData,包括卡片信息
final ClipData data = assembleClipData();
//调用系统拖拽
boolean dragResult = dragView.startDragAndDrop(data, builder, null,
        View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_OPAQUE | DRAG_FLAG_CUSTOM_RETURN_ANIMATION | DRAG_FLAG_CUSTOM_CANCEL_ANIMATION);

1.2应用层监听拖拽事件

getDragLayer().setOnDragListener(new View.OnDragListener() {
    @Override
    public boolean onDrag(View v, DragEvent event) {
        
    }
});


View.dispatchDragEvent 就会将事件传递到这里

 

二、Framework 层调用链(View → WindowManagerService)

2.1 View.startDragAndDrop() 实现

转存失败,建议直接上传图片文件

源码位置: frameworks/base/core/java/android/view/View.java

// View.java
public final boolean startDragAndDrop(ClipData data, 
                                      DragShadowBuilder shadowBuilder,
                                      Object myLocalState, 
                                      
    //1. DRAG_FLAG_ACCESSIBILITY_ACTION 表示不需要动画,所以不需要surface                                                                                                      int flags) {
    if (a11yEnabled && (flags & View.DRAG_FLAG_ACCESSIBILITY_ACTION) != 0) {
      IBinder token = mAttachInfo.mSession.performDrag(
          mAttachInfo.mWindow, flags, null,
          mAttachInfo.mViewRootImpl.getLastTouchSource(),
          mAttachInfo.mViewRootImpl.getLastTouchDeviceId(),
          mAttachInfo.mViewRootImpl.getLastTouchPointerId(),
          0f, 0f, 0f, 0f, data);
    }
    
   
    // 2. 创建DragSurface,大小为view的大小
     final SurfaceControl surfaceControl = new SurfaceControl.Builder()
                .setName("drag surface")
                .setParent(root.getSurfaceControl())
                .setBufferSize(shadowSize.x, shadowSize.y)
                .setFormat(PixelFormat.TRANSLUCENT)
                .setCallsite("View.startDragAndDrop")
                .build();
    transaction.setMatrix(surfaceControl, 1 / overrideInvScale, 0, 0, 1 / overrideInvScale)
        .apply();
    final Surface surface = new Surface();
    surface.copyFrom(surfaceControl);
    //3.创建scanvas
    final Canvas canvas = isHardwareAccelerated()
        ? surface.lockHardwareCanvas()
        : surface.lockCanvas(null);
    


    canvas.drawColor(0, PorterDuff.Mode.CLEAR);
     //4.绘制内容,最终调用view.draw(canvas)
    shadowBuilder.onDrawShadow(canvas);




    //5.调用 ViewRootImpl.performDrag()
    result = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, flags, surfaceControl,
                        root.getLastTouchSource(), root.getLastTouchDeviceId(),
                        root.getLastTouchPointerId(), lastTouchPoint.x, lastTouchPoint.y,
                        shadowTouchPoint.x, shadowTouchPoint.y, data)!=null;
    
    
    mAttachInfo.mDragSurface = surface;
    return result;
}


public void onDrawShadow(@NonNull Canvas canvas) {
    final View view = mView.get();
    if (view != null) {
        view.draw(canvas); //绘制内容


    } else {
        Log.e(View.VIEW_LOG_TAG, "Asked to draw drag shadow but no view");
    }
}

 

2.2 Session.performDrag() 实现

源码位置: frameworks/base/services/core/java/com/android/server/wm/Session.java

    @Override
    public IBinder performDrag(IWindow window, int flags, SurfaceControl surface, int touchSource,
            int touchDeviceId, int touchPointerId, float touchX, float touchY, float thumbCenterX,
            float thumbCenterY, ClipData data) {
        final int callingUid = Binder.getCallingUid();
        final int callingPid = Binder.getCallingPid();
        // Validate and resolve ClipDescription data before clearing the calling identity
        validateAndResolveDragMimeTypeExtras(data, callingUid, callingPid, mPackageName);
        validateDragFlags(flags, callingUid);
        final long ident = Binder.clearCallingIdentity();
        try {
            return mDragDropController.performDrag(mPid, mUid, window, flags, surface, touchSource,
                    touchDeviceId, touchPointerId, touchX, touchY, thumbCenterX, thumbCenterY,
                    data);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

 

2.3 DragDropController .performDrag() 调用

转存失败,建议直接上传图片文件

源码位置: frameworks/base/services/core/java/com/android/server/wm/DragDropController.java

// IWindowSession.aidl
 IBinder performDrag(int callerPid, int callerUid, IWindow window, int flags,
            SurfaceControl surface, int touchSource, int touchDeviceId, int touchPointerId,
            float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data) {


  //1.初始化DragState
 mDragState = new DragState(mService, this, token, surface, flags, winBinder);
                    surface = null;
                    mDragState.mPid = callerPid;
                    mDragState.mUid = callerUid;
                    mDragState.mOriginalAlpha = alpha;
                    mDragState.mAnimatedScale = callingWin.mGlobalScale;
                    mDragState.mToken = dragToken;
                    mDragState.mDisplayContent = displayContent;
                    mDragState.mData = data;
                    mDragState.mCallingTaskIdToHide = shouldMoveCallingTaskToBack(callingWin,
                            flags);


//2.给InputFlinger注册拖拽标记
touchFocusTransferredFuture = mCallback.get().registerInputChannel(
                                mDragState, display, mService.mInputManager,
                                callingWin.mInputChannelToken);


//3.分发第一个拖拽事件到当前窗口,当前窗口需要 设置 PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP
 mDragState.broadcastDragStartedLocked(touchX, touchY)


//4.调整surface层级,重新绑定parent
displayContent.reparentToOverlay(transaction, surfaceControl);
}

三、系统服务层调用链(WindowManagerService)

 

3.1 WindowManagerInternal.registerInputChannel()调用

源码位置: frameworks/base/services/core/java/com/android/server/wm/WindowManagerInternal.java

 default CompletableFuture<Boolean> registerInputChannel(
                DragState state, Display display, InputManagerService service,
                IBinder sourceInputChannelToken) {
            return state.register(display)
                .thenApply(unused ->
                    service.startDragAndDrop(sourceInputChannelToken, state.getInputToken()));
        }

3.2 InputManagerService.startDragAndDrop()调用

源码位置: frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

  public boolean startDragAndDrop(@NonNull IBinder fromChannelToken,
            @NonNull IBinder dragAndDropChannelToken) {
        return mNative.transferTouchGesture(fromChannelToken, dragAndDropChannelToken,
                true /* isDragDrop */);
    }

3.3 com_android_server_input_InputManagerService. nativeTransferTouchGesture()调用

源码位置: frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

static jboolean nativeTransferTouchGesture(JNIEnv* env, jobject nativeImplObj,
                                           jobject fromChannelTokenObj, jobject toChannelTokenObj,
                                           jboolean isDragDrop) {
    if (fromChannelTokenObj == nullptr || toChannelTokenObj == nullptr) {
        return JNI_FALSE;
    }


    sp<IBinder> fromChannelToken = ibinderForJavaObject(env, fromChannelTokenObj);
    sp<IBinder> toChannelToken = ibinderForJavaObject(env, toChannelTokenObj);


    NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
    if (im->getInputManager()->getDispatcher().transferTouchGesture(fromChannelToken,
                                                                    toChannelToken, isDragDrop)) {
        return JNI_TRUE;
    } else {
        return JNI_FALSE;
    }
}

四、输入系统调用链(InputDispatcher)

转存失败,建议直接上传图片文件

4.1InputDispatcher.transferTouchGesture()标记拖拽

源码位置: frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp

bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
                                           bool isDragDrop) {
...
        // Store the dragging window.
        if (isDragDrop) {
            if (pointers.size() != 1) {
                ALOGW("The drag and drop cannot be started when there is no pointer or more than 1"
                      " pointer on the window.");
                return false;
            }
            // Track the pointer id for drag window and generate the drag state.
            const size_t id = pointers.begin()->id;
          //1.初始化mDragState 
            mDragState = std::make_unique<DragState>(toWindowHandle, deviceId, id);
        }
    //2.唤醒事件分发,进入下一个循环
     mLooper->wake();
}

 

4.2添加拖拽事件到队列

 

InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry) {
...
addDragEventLocked(entry)// 添加Drag事件到队列中
...
}

 

void InputDispatcher::addDragEventLocked(const MotionEntry& entry) {
  ...
  //添加拖拽事件到队列
 enqueueDragEventLocked(mDragState->dragHoverWindowHandle, /*isExiting=*/true, x,
                                       y)
  ....
}
void InputDispatcher::enqueueDragEventLocked(const sp<WindowInfoHandle>& windowHandle,
                                             bool isExiting, const int32_t rawX,
                                             const int32_t rawY) {
    const vec2 xy = windowHandle->getInfo()->transform.transform(vec2(rawX, rawY));
    //初始化事件
    std::unique_ptr<DragEntry> dragEntry =
            std::make_unique<DragEntry>(mIdGenerator.nextId(), now(), windowHandle->getToken(),
                                        isExiting, xy.x, xy.y);
    //添加拖拽事件到队列
    enqueueInboundEventLocked(std::move(dragEntry));
}

 

4.3 通过socket发送拖拽事件

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t& nextWakeupTime) {
  switch (mPendingEvent->type) {
     case EventEntry::Type::DRAG: {
            std::shared_ptr<const DragEntry> typedEntry =
                    std::static_pointer_cast<const DragEntry>(mPendingEvent);
            dispatchDragLocked(currentTime, typedEntry);
            done = true;
            break;
        }  
      ...
  }
}


void InputDispatcher::dispatchDragLocked(nsecs_t currentTime,
                                         std::shared_ptr<const DragEntry> entry) {
    std::shared_ptr<Connection> connection =
            mConnectionManager.getConnection(entry->connectionToken);
    if (connection == nullptr) {
        return; // Connection has gone away
    }
    entry->dispatchInProgress = true;
    dispatchEventLocked(currentTime, entry, {{connection}});
}

后续跟事件输入的流程基本一样了,dispatchEventLocked最终到startDispatchCycleLocked

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
                                               const std::shared_ptr<Connection>& connection) {
 switch (eventEntry.type) {
  case EventEntry::Type::DRAG: {
        const DragEntry& dragEntry = static_cast<const DragEntry&>(eventEntry);
        //调用socket发送事件
        status = connection->inputPublisher.publishDragEvent(dispatchEntry->seq,
                                                             dragEntry.id, dragEntry.x,
                                                             dragEntry.y,
                                                             dragEntry.isExiting);
        break;
      }
  }
}

后续流程可以参考 Android输入系统源码分析(上)  

 

五、应用层接受事件

转存失败,建议直接上传图片文件

5.1InputEventReceiver.consumeEvents接受事件后消费事件

frameworks/base/core/jni/android_view_InputEventReceiver.cpp
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
  InputEvent* inputEvent;
  //读取事件
  status_t status = mInputConsumer.consume(&mInputEventFactory,
          consumeBatches, frameTime, &seq, &inputEvent);
 switch (inputEvent->getType()) {
   case InputEventType::MOTION: {
     env->CallVoidMethod(receiverObj.get(),
            gInputEventReceiverClassInfo.dispatchInputEvent, seq,
            inputEventObj.get());
  
   }
    //拖拽事件处理
   case InputEventType::DRAG: {
      const DragEvent* dragEvent = static_cast<DragEvent*>(inputEvent);       
      env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.onDragEvent,
          jboolean(dragEvent->isExiting()), dragEvent->getX(),
          dragEvent->getY());
   }
...
 }
}

5.2ViewRootImpl$WindowInputEventReceiver.onDragEvent

frameworks/base/core/java/android/view/ViewRootImpl.java
final class WindowInputEventReceiver extends InputEventReceiver {
          public void onDragEvent(boolean isExiting, float x, float y) {
            // force DRAG_EXITED_EVENT if appropriate
            DragEvent event = DragEvent.obtain(
                    isExiting ? DragEvent.ACTION_DRAG_EXITED : DragEvent.ACTION_DRAG_LOCATION,
                    x, y, 0 /* offsetX */, 0 /* offsetY */, 0 /* flags */, null/* localState */,
                    null/* description */, null /* data */, null /* dragSurface */,
                    null /* dragAndDropPermissions */, false /* result */);
            dispatchDragEvent(event);
        }
}

dispatchDragEvent后续会调用到 handleDragEvent,然后调用到View.dispatchDragEvent

这样应用层的 通过 setOnDragListener 设置的listener

 

六、总结

6.1 核心流程

  1. 应用层启动: 调用 View.startDragAndDrop(DRAG_FLAG_GLOBAL)
  2. Framework 层: ViewRootImpl.performDrag() 通过 Binder 调用系统服务
  3. 系统服务层: WindowManagerService.performDrag() 创建 DragState,通知 InputDispatcher
  4. 输入系统: InputDispatcher 将触摸事件转换为拖放事件,分发到所有符合条件的窗口
  5. 应用层接收: Launcher 通过 IWindow.dispatchDragEvent()

 

6.2时序图

主要流程时序图

sequenceDiagram
    participant App as 应用层
    participant View as View/ViewRootImpl
    participant WMS as WindowManagerService
    participant InputDispatcher as InputDispatcher
    participant InputChannel as InputChannel

    Note over App,InputChannel: 拖拽启动流程
    
    App->>View: startDragAndDrop(flags=GLOBAL)
    View->>View: 创建DragSurface
    View->>WMS: Session.performDrag()
    WMS->>WMS: DragDropController.performDrag()
    WMS->>WMS: 创建DragState
    WMS->>InputDispatcher: registerInputChannel()
    InputDispatcher->>InputDispatcher: transferTouchGesture()
    InputDispatcher->>InputDispatcher: 初始化mDragState
    
    Note over App,InputChannel: 事件分发流程
    
    loop 每次触摸移动
        InputDispatcher->>InputDispatcher: 检测触摸事件
        InputDispatcher->>InputDispatcher: addDragEventLocked()
        InputDispatcher->>InputDispatcher: enqueueDragEventLocked()
        InputDispatcher->>InputChannel: publishDragEvent()
        InputChannel->>View: 发送拖拽事件
        View->>App: dispatchDragEvent()
        App->>App: OnDragListener.onDrag()
    end
    
    Note over App,InputChannel: 拖拽结束流程
    
    App->>View: 调用endDrag()
    View->>WMS: 结束拖拽
    WMS->>InputDispatcher: 清理DragState
    InputDispatcher->>InputDispatcher: 发送DRAG_EXITED事件

 

数据流转时序图

sequenceDiagram
    participant Launcher as App
    participant ClipData as ClipData
    participant Surface as DragSurface
    participant DragState as DragState
    participant DragEntry as DragEntry
    participant DragEvent as DragEvent

    Note over Launcher,DragEvent: 数据创建和传递流程
    
    Launcher->>ClipData: assembleClipData()<br/>创建ClipData对象<br/>包含卡片信息
    Launcher->>Surface: 创建SurfaceControl<br/>绘制拖拽阴影
    Launcher->>DragState: performDrag()<br/>传递ClipData和Surface
    
    DragState->>DragState: 保存mData=ClipData<br/>保存mSurface=Surface
    
    Note over Launcher,DragEvent: 事件生成流程
    
    InputDispatcher->>DragEntry: enqueueDragEventLocked()<br/>创建DragEntry<br/>包含x, y, isExiting
    DragEntry->>DragEntry: 坐标转换<br/>transform(rawX, rawY)
    
    Note over Launcher,DragEvent: 事件接收流程
    
    InputReceiver->>DragEvent: 解析Socket数据<br/>创建DragEvent对象
    DragEvent->>DragEvent: 设置ACTION类型<br/>ACTION_DRAG_LOCATION<br/>或ACTION_DRAG_EXITED
    DragEvent->>Launcher: dispatchDragEvent()<br/>传递到应用层
    Launcher->>Launcher: 从event.getClipData()<br/>获取原始数据

 

完整时序图

sequenceDiagram
    participant Launcher as App应用层
    participant View as View
    participant ViewRootImpl as ViewRootImpl
    participant Session as Session
    participant DragDropController as DragDropController
    participant WMS as WindowManagerService
    participant InputManager as InputManagerService
    participant InputDispatcher as InputDispatcher
    participant Socket as Socket通信
    participant InputReceiver as InputEventReceiver
    participant DragListener as OnDragListener

    Note over Launcher,DragListener: 一、启动拖拽阶段
    
    Launcher->>Launcher: assembleClipData<br/>组装卡片数据
    Launcher->>View: startDragAndDrop(data, builder, flags)
    
    View->>View: 检查DRAG_FLAG_ACCESSIBILITY_ACTION
    alt 无障碍模式
        View->>Session: performDrag(无需Surface)
    else 正常模式
        View->>View: 创建SurfaceControl
        View->>View: 创建Canvas并绘制阴影
        View->>ViewRootImpl: performDrag(surfaceControl, data)
    end
    
    ViewRootImpl->>Session: performDrag(window, flags, surface, data)
    Session->>DragDropController: performDrag(...)
    
    Note over DragDropController: 初始化DragState
    DragDropController->>DragDropController: 创建DragState对象
    DragDropController->>DragDropController: 设置mData, mToken等属性
    
    DragDropController->>WMS: registerInputChannel(DragState)
    WMS->>InputManager: startDragAndDrop(sourceToken, dragToken)
    InputManager->>InputDispatcher: transferTouchGesture(fromToken, toToken, isDragDrop=true)
    
    Note over InputDispatcher: 初始化拖拽状态
    InputDispatcher->>InputDispatcher: 创建mDragState
    InputDispatcher->>InputDispatcher: 唤醒事件分发循环
    
    Note over Launcher,DragListener: 二、事件分发阶段
    
    InputDispatcher->>InputDispatcher: findTouchedWindowTargetsLocked
    InputDispatcher->>InputDispatcher: addDragEventLocked
    InputDispatcher->>InputDispatcher: enqueueDragEventLocked<br/>创建DragEntry
    InputDispatcher->>InputDispatcher: enqueueInboundEventLocked<br/>添加到队列
    
    InputDispatcher->>InputDispatcher: dispatchOnceInnerLocked
    InputDispatcher->>InputDispatcher: dispatchDragLocked
    InputDispatcher->>InputDispatcher: startDispatchCycleLocked
    InputDispatcher->>Socket: publishDragEvent(seq, id, x, y, isExiting)
    
    Note over Launcher,DragListener: 三、应用层接收阶段
    
    Socket->>InputReceiver: 接收拖拽事件数据
    InputReceiver->>InputReceiver: consumeEvents
    InputReceiver->>InputReceiver: InputConsumer.consume<br/>解析DragEvent
    InputReceiver->>ViewRootImpl: onDragEvent(isExiting, x, y)
    
    ViewRootImpl->>ViewRootImpl: 创建DragEvent对象<br/>ACTION_DRAG_LOCATION<br/>或ACTION_DRAG_EXITED
    ViewRootImpl->>View: dispatchDragEvent(event)
    View->>DragListener: onDrag(v, event)
    DragListener->>Launcher: handleDragEvent(event)
    
    Note over Launcher: 处理拖拽事件<br/>更新UI状态