【Android 事件分发】事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 )

简介: 【Android 事件分发】事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 )

文章目录

Android 事件分发 系列文章目录

一、事件分发脉络

二、驱动层通过中断传递事件

三、WindowManagerService 向 View 传递事件





一、事件分发脉络


事件分发分析流程 :


① 驱动层 -> Framework 层 : 用户触摸 , 或按键 后 , 事件在硬件中产生 , 从 硬件驱动层 , 传递到 Framework 层 ;


② WMS -> View 层 : WindowManagerService ( 简称 WMS ) 将事件传递到 View 层 ;


③ View 层内部 : 事件在 View 的容器及下层容器 / 组件 之间传递 ;






二、驱动层通过中断传递事件


硬件产生事件后 , 驱动层通过中断传递事件 ;


中断在嵌入式 Linux 中经常使用 , 分为 外部中断 和 内部中断 ;


外部中断 : 由外部事件产生的中断 , 如这里的由硬件触摸 / 按键 产生的事件产生的中断 ;

内部中断 : 程序运行出现崩溃 , 异常 等情况 ;

中断是指在 CPU 正常执行指令时 , 内部或外部事件 / 事先设置好的中断操作 , 会引起 CPU 中断当前正在执行的指令 , 转而运行当前中断对应的相关指令 , 中断程序执行完毕后 , 继续执行后续中断前未执行完毕的代码 ;



中断有两种方式 : 一种是轮询的 , CPU 不断读取硬件的状态 ; 另一种是硬件产生事件会后 , 发送信息给 CPU , 让 CPU 暂停当前工作 , 执行中断任务 ;






三、WindowManagerService 向 View 传递事件


在 【Android 应用开发】UI绘制流程 ( 生命周期机制 | 布局加载机制 | UI 绘制流程 | 布局测量 | 布局摆放 | 组件绘制 | 瀑布流布局案例 ) 博客中 , 分析了 UI 布局绘制流程 , 从 ActivityThread 开始 , 逐步调用 , 绘制 UI 界面 , 调用链如下 :


ActivityThread | handleResumeActivity -> WindowManager | addView -> ViewRootImpl | setView ;

最后在 ViewRootImpl 的 performTraversals 方法中 , 完成 测量 , 布局 , 绘画 操作 ;


在 WindowManagerGlobal 中的 addView 方法的主要作用是添加 DecorView ;



各个窗口的层级如下 : 事件传递从 Activity 逐层向下传递的 View 组件上 ;

image.png




这里开始从 ViewRootImpl 的 setView 方法进行分析 ;


通过 new InputChannel() 直接创建输入通道 ;


还调用了 WindowSession 的 addToDisplay 方法 , mWindowSession 成员是 IWindowSession 类型 , 通过 mWindowSession = WindowManagerGlobal.getWindowSession() 获得 ;

WindowManagerGlobal 的 getWindowSession 方法中 , 最终 WindowSession 又调用回了 WMS 的 openSession , 创建了一个 WindowSession 对象 ;


在 ViewRootImpl 的 setView 方法中 , 注册了 mInputEventReceiver , 传入 InputChannel 和 Looper 参数 , InputChannel 就是事件传入的通道 , Looper 用于轮询事件是否发生 ;


ViewRootImpl 参考源码 :


public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
                requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                ...
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
    ...
                if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }
    }
}


/frameworks/base/core/java/android/view/ViewRootImpl.java



WindowManagerService 获取 WindowSession 方法 : 通过调用 WindowManagerGlobal 的 getWindowSession 获取 , 最终还是调用了 WindowManagerService 的 openSession 方法 ;


WindowManagerGlobal 参考源码 :


public final class WindowManagerGlobal {
    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            },
                            imm.getClient(), imm.getInputContext());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }
}


/frameworks/base/core/java/android/view/WindowManagerGlobal.java



在 WindowManagerService 的 addWindow 方法中 ,


初始化了 窗口的状态 WindowState ,

通过调用 WindowState 的 openInputChannel 方法 , 设置了 InputChannel , 就是将 ViewRootImpl 中 setView 方法中 new InputChannel() 创建的 InputChannel 传递进来 ;


WindowManagerService 参考源码 :


/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
    public int addWindow(Session session, IWindow client, int seq,
            LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
    // 初始化窗口状态 
            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);
            if  (openInputChannels) {
                win.openInputChannel(outInputChannel);
            }
  }
    @Override
    public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
            IInputContext inputContext) {
        if (client == null) throw new IllegalArgumentException("null client");
        if (inputContext == null) throw new IllegalArgumentException("null inputContext");
        Session session = new Session(this, callback, client, inputContext);
        return session;
    }
}


/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java



WindowState 的 openInputChannel 方法 , 其中调用了 InputChannel.openInputChannelPair(name) 静态方法 , 这是开启了 2 22 个 InputChannel , inputChannels[0] 放在服务端 , inputChannels[1] 放在了客户端 ;


服务端 与 客户端 需要进行通信 , 二者通过 Looper 进行通信 , 通信前需要进行注册 , 在 InputDispatcher.cpp 中进行的注册 ;


WindowState 参考源码 :


/** A window in the window manager. */
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState {
    void openInputChannel(InputChannel outInputChannel) {
        if (mInputChannel != null) {
            throw new IllegalStateException("Window already has an input channel.");
        }
        String name = getName();
        InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
        // 服务端
        mInputChannel = inputChannels[0];
        // 客户端 
        mClientChannel = inputChannels[1];
        mInputWindowHandle.inputChannel = inputChannels[0];
        if (outInputChannel != null) {
            mClientChannel.transferTo(outInputChannel);
            mClientChannel.dispose();
            mClientChannel = null;
        } else {
            // If the window died visible, we setup a dummy input channel, so that taps
            // can still detected by input monitor channel, and we can relaunch the app.
            // Create dummy event receiver that simply reports all events as handled.
            mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
        }
        mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
    }
}


/frameworks/base/services/core/java/com/android/server/wm/WindowState.java



下面分析在 InputDispatcher 中 , 注册 服务端 与 客户端 InputChannel 的过程 ;


在 registerInputChannel 方法中 , 创建了 Connection 连接 , 这就是两个 服务端 与 客户端 InputChannel 沟通的通道 , 每个 InputChannel 都有一个 fd , 通过 int fd = inputChannel->getFd() 获取 fd , 调用 mConnectionsByFd.add(fd, connection) 将 fd 与 Connection 对应起来 ;

最后将 fd 注册在 Looper 中 , mLooper->addFd ;


只要有任何事件输入 , 该 Looper 就会被唤醒 , 通过 InputChannel 传递到 Activity , 进而传递给各个层级的 View 组件 ;


status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
#if DEBUG_REGISTRATION
    ALOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().c_str(),
            toString(monitor));
#endif
    { // acquire lock
        AutoMutex _l(mLock);
        if (getConnectionIndexLocked(inputChannel) >= 0) {
            ALOGW("Attempted to register already registered input channel '%s'",
                    inputChannel->getName().c_str());
            return BAD_VALUE;
        }
        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
        int fd = inputChannel->getFd();
        mConnectionsByFd.add(fd, connection);
        if (monitor) {
            mMonitoringChannels.push(inputChannel);
        }
        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } // release lock
    // Wake the looper because some connections have changed.
    mLooper->wake();
    return OK;
}


/frameworks/native/services/inputflinger/InputDispatcher.cpp


目录
相关文章
|
5月前
|
Android开发 UED 计算机视觉
Android自定义view之线条等待动画(灵感来源:金铲铲之战)
本文介绍了一款受游戏“金铲铲之战”启发的Android自定义View——线条等待动画的实现过程。通过将布局分为10份,利用`onSizeChanged`测量最小长度,并借助画笔绘制动态线条,实现渐变伸缩效果。动画逻辑通过四个变量控制线条的增长与回退,最终形成流畅的等待动画。代码中详细展示了画笔初始化、线条绘制及动画更新的核心步骤,并提供完整源码供参考。此动画适用于加载场景,提升用户体验。
472 5
Android自定义view之线条等待动画(灵感来源:金铲铲之战)
|
5月前
|
Android开发
Android自定义view之利用PathEffect实现动态效果
本文介绍如何在Android自定义View中利用`PathEffect`实现动态效果。通过改变偏移量,结合`PathEffect`的子类(如`CornerPathEffect`、`DashPathEffect`、`PathDashPathEffect`等)实现路径绘制的动态变化。文章详细解析了各子类的功能与参数,并通过案例代码展示了如何使用`ComposePathEffect`组合效果,以及通过修改偏移量实现动画。最终效果为一个菱形图案沿路径运动,源码附于文末供参考。
|
5月前
|
XML Java Android开发
Android自定义view之网易云推荐歌单界面
本文详细介绍了如何通过自定义View实现网易云音乐推荐歌单界面的效果。首先,作者自定义了一个圆角图片控件`MellowImageView`,用于绘制圆角矩形图片。接着,通过将布局放入`HorizontalScrollView`中,实现了左右滑动功能,并使用`ViewFlipper`添加图片切换动画效果。文章提供了完整的代码示例,包括XML布局、动画文件和Java代码,最终展示了实现效果。此教程适合想了解自定义View和动画效果的开发者。
235 65
Android自定义view之网易云推荐歌单界面
|
5月前
|
XML 前端开发 Android开发
一篇文章带你走近Android自定义view
这是一篇关于Android自定义View的全面教程,涵盖从基础到进阶的知识点。文章首先讲解了自定义View的必要性及简单实现(如通过三个构造函数解决焦点问题),接着深入探讨Canvas绘图、自定义属性设置、动画实现等内容。还提供了具体案例,如跑马灯、折线图、太极图等。此外,文章详细解析了View绘制流程(measure、layout、draw)和事件分发机制。最后延伸至SurfaceView、GLSurfaceView、SVG动画等高级主题,并附带GitHub案例供实践。适合希望深入理解Android自定义View的开发者学习参考。
573 84
|
5月前
|
前端开发 Android开发 UED
讲讲Android为自定义view提供的SurfaceView
本文详细介绍了Android中自定义View时使用SurfaceView的必要性和实现方式。首先分析了在复杂绘制逻辑和高频界面更新场景下,传统View可能引发卡顿的问题,进而引出SurfaceView作为解决方案。文章通过Android官方Demo展示了SurfaceView的基本用法,包括实现`SurfaceHolder.Callback2`接口、与Activity生命周期绑定、子线程中使用`lockCanvas()`和`unlockCanvasAndPost()`方法完成绘图操作。
119 3
|
5月前
|
Android开发 开发者
Android自定义view之围棋动画(化繁为简)
本文介绍了Android自定义View的动画实现,通过两个案例拓展动态效果。第一个案例基于`drawArc`方法实现单次动画,借助布尔值控制动画流程。第二个案例以围棋动画为例,从简单的小球直线运动到双向变速运动,最终实现循环动画效果。代码结构清晰,逻辑简明,展示了如何化繁为简实现复杂动画,帮助读者拓展动态效果设计思路。文末提供完整源码,适合初学者和进阶开发者学习参考。
Android自定义view之围棋动画(化繁为简)
|
5月前
|
Java Android开发 开发者
Android自定义view之围棋动画
本文详细介绍了在Android中自定义View实现围棋动画的过程。从测量宽高、绘制棋盘背景,到创建固定棋子及动态棋子,最后通过属性动画实现棋子的移动效果。文章还讲解了如何通过自定义属性调整棋子和棋盘的颜色及动画时长,并优化视觉效果,如添加渐变色让白子更明显。最终效果既可作为围棋动画展示,也可用作加载等待动画。代码完整,适合进阶开发者学习参考。
113 0
|
Android开发
Android Touch事件分发(源码分析)
Android一文让你轻松搞定Touch事件分发 源码分析 Activity事件分发机制 Activity.dispatchTouchEvent()源码 Activity.onTouchEvent()源码 Activity源码总结 ViewGroup事件分发机制 ViewGroup.dispatchTouchEvent()源码 ViewGroup.onInterceptTouchEvent()源码 ViewGroup.onTouchEvent()源码 ViweGroup源码总结 View的事件分发机制 View.dispatchTouchEvent()源码
245 0
Android Touch事件分发(源码分析)
|
Java Android开发
【Android 事件分发】事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )
【Android 事件分发】事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )
310 0
【Android 事件分发】事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )
|
Java Android开发
【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 七 )(二)
【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 七 )(二)
201 0

热门文章

最新文章