连续的输入事件可能会产生一定的手势操作比如滑动手势和捏合手势
在 Chromium 中, 网页的输入事件是在 Browser 进程中捕捉的 Browser 进程捕获输入事件之后, 会进行手势操作检測检測出来的手势操作将会发送给 Render 进程处理, 由于它们须要应用在网页之上与此同一时候 Browser 进程也会将原始的输入事件发送给 Render 进程处理本文接下来就分析 Browser 进程处理网页输入事件的过程
老罗的新浪微博: http://weibo.com/shengyangluo, 欢迎关注!
Android 系统源码情景分析一书正在进击的程序猿网 (http://0xcc0xcd.com) 中连载, 点击进入
接下来我们将以 Chromium 自带的 Content Shell APK 为例, 说明 Chromium 的 Browser 进程捕获网页输入事件以及检測手势操作的过程, 如图 1 所看到的:
图 1 Browser 进程处理网页输入事件的过程
从前面 Chromium 网页输入事件处理机制简要介绍和学习计划一文能够知道, Content Shell APK 将网页渲染在一个 SurfaceView 控件上
这个 SurfaceView 又是嵌入在一个 ContentView 控件里面的
当用户在网页上触发了一个输入事件时, 比如触发一个 Touch 事件时, 这个 Touch 事件就会被系统分发给上述 ContentView 控件处理表现为该 ContentView 控件的成员函数 onTouchEvent 被调用
ContentView 控件得到 Touch 事件之后, 会将它传递到 Chromium 的 C++ 层去处理 Java 层的每个 ContentView 控件在 C++ 层都相应一个 ContentViewCore 对象 C++ 层的 ContentViewCore 对象得到 Touch 事件之后就会通过一个 Gesture Dector 和一个 Scale Gesture Detector 进行滑动 (Scroll) 和捏合 (Pinch) 手势检測检測出来的滑动和捏合手势将会统一保存在一个 Gestrue Packet 中这个 Gestrue Packet 接下来会被一个 Input Router 封装在一个类型为 InputMsg_HandleInputEvent 的 IPC 消息中, 发送给 Render 进程处理
注意 Touch 事件经过手势检測之后它本身也会被上述 Input Router 通过另外一个 InputMsg_HandleInputEvent 消息发送给 Render 进程处理这意味着在这样的情况下, Render 进程将收到两个 InputMsg_HandleInputEvent 消息
接下来, 我们就从 ContentView 类的成员函数 onTouchEvent 開始, 分析 Browser 进程处理网页输入事件的过程, 例如以下所看到的:
- public class ContentView extends FrameLayout
- implements ContentViewCore.InternalAccessDelegate, SmartClipProvider {
- ......
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- return mContentViewCore.onTouchEvent(event);
- }
- ......
- }
这个函数定义在文件 external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ContentView.java 中
參数 event 指向的 MotionEvent 对象描写叙述的就是当前发生的 Touch 事件 ContentView 类的成员变量 mContentViewCore 指向的是一个 ContentViewCore 对象, ContentView 类的成员函数 onTouchEvent 调用这个 ContentViewCore 对象的成员函数 onTouchEvent 处理參数 event 所描写叙述的 Touch 事件
ContentViewCore 类的成员函数 onTouchEvent 的实现例如以下所看到的:
- public class ContentViewCore
- implements NavigationClient, AccessibilityStateChangeListener, ScreenOrientationObserver {
- ......
- public boolean onTouchEvent(MotionEvent event) {
- TraceEvent.begin("onTouchEvent");
- try {
- ......
- final int pointerCount = event.getPointerCount();
- final boolean consumed = nativeOnTouchEvent(mNativeContentViewCore, event,
- event.getEventTime(), eventAction,
- pointerCount, event.getHistorySize(), event.getActionIndex(),
- event.getX(), event.getY(),
- pointerCount > 1 ? event.getX(1) : 0,
- pointerCount > 1 ?
- event.getY(1) : 0,
- event.getPointerId(0), pointerCount > 1 ?
- event.getPointerId(1) : -1,
- event.getTouchMajor(), pointerCount > 1 ? event.getTouchMajor(1) : 0,
- event.getRawX(), event.getRawY(),
- event.getToolType(0),
- pointerCount > 1 ?
- event.getToolType(1) : MotionEvent.TOOL_TYPE_UNKNOWN,
- event.getButtonState());
- ......
- return consumed;
- } finally {
- TraceEvent.end("onTouchEvent");
- }
- }
- ......
- }
这个函数定义在文件 external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java 中
ContentViewCore 类的成员函数 onTouchEvent 主要是调用另外一个成员函数 nativeOnTouchEvent 处理參数 event 描写叙述的 Touch 事件
ContentViewCore 类的成员函数 nativeOnTouchEvent 是一个 JNI 函数, 它由 C++ 层的函数 Java_com_android_org_chromium_content_browser_ContentViewCore_nativeOnTouchEvent 实现例如以下所看到的:
- __attribute__((visibility("default")))
- jboolean
- Java_com_android_org_chromium_content_browser_ContentViewCore_nativeOnTouchEvent(JNIEnv*
- env,
- jobject jcaller,
- jlong nativeContentViewCoreImpl,
- jobject event,
- jlong timeMs,
- jint action,
- jint pointerCount,
- jint historySize,
- jint actionIndex,
- jfloat x0,
- jfloat y0,
- jfloat x1,
- jfloat y1,
- jint pointerId0,
- jint pointerId1,
- jfloat touchMajor0,
- jfloat touchMajor1,
- jfloat rawX,
- jfloat rawY,
- jint androidToolType0,
- jint androidToolType1,
- jint androidButtonState) {
- ContentViewCoreImpl* native =
- reinterpret_cast<ContentViewCoreImpl*>(nativeContentViewCoreImpl);
- CHECK_NATIVE_PTR(env, jcaller, native, "OnTouchEvent", false);
- return native->OnTouchEvent(env, jcaller, event, timeMs, action, pointerCount,
- historySize, actionIndex, x0, y0, x1, y1, pointerId0, pointerId1,
- touchMajor0, touchMajor1, rawX, rawY, androidToolType0, androidToolType1,
- androidButtonState);
- }
这个函数定义在文件 out/target/product/generic/obj/GYP/shared_intermediates/content/jni/ContentViewCore_jni.h 中
參数 nativeContentViewCoreImpl 描写叙述的是 C++ 层的一个 ContentViewCoreImpl 对象函数 Java_com_android_org_chromium_content_browser_ContentViewCore_nativeOnTouchEvent 调用这个 ContentViewCoreImpl 对象的成员函数 OnTouchEvent 处理其他參数所描写叙述的 Touch 事件
ContentViewCoreImpl 类的成员函数 OnTouchEvent 的实现例如以下所看到的:
- jboolean ContentViewCoreImpl::OnTouchEvent(JNIEnv* env,
- jobject obj,
- jobject motion_event,
- jlong time_ms,
- jint android_action,
- jint pointer_count,
- jint history_size,
- jint action_index,
- jfloat pos_x_0,
- jfloat pos_y_0,
- jfloat pos_x_1,
- jfloat pos_y_1,
- jint pointer_id_0,
- jint pointer_id_1,
- jfloat touch_major_0,
- jfloat touch_major_1,
- jfloat raw_pos_x,
- jfloat raw_pos_y,
- jint android_tool_type_0,
- jint android_tool_type_1,
- jint android_button_state) {
- RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid();
- ......
- MotionEventAndroid event(1.f / dpi_scale(),
- env,
- motion_event,
- time_ms,
- android_action,
- pointer_count,
- history_size,
- action_index,
- pos_x_0,
- pos_y_0,
- pos_x_1,
- pos_y_1,
- pointer_id_0,
- pointer_id_1,
- touch_major_0,
- touch_major_1,
- raw_pos_x,
- raw_pos_y,
- android_tool_type_0,
- android_tool_type_1,
- android_button_state);
- return rwhv->OnTouchEvent(event);
- }
这个函数定义在文件 external/chromium_org/content/browser/android/content_view_core_impl.cc 中
ContentViewCoreImpl 类的成员函数 OnTouchEvent 首先调用成员函数 GetRenderWidgetHostViewAndroid 获得一个 RenderWidgetHostViewAndroid 对象
这个 RenderWidgetHostViewAndroid 对象用来在 C++ 层描写叙述载入网页的控件, 它的创建过程能够參考前面 Chromium 硬件加速渲染的 OpenGL 上下文画图表面创建过程分析一文
ContentViewCoreImpl 类的成员函数 OnTouchEvent 接下来又将參数描写叙述的 Touch 事件封装在一个 MotionEventAndroid 对象中然后将该 MotionEventAndroid 对象传递给前面获得的 RenderWidgetHostViewAndroid 对象的成员函数 OnTouchEvent 处理
RenderWidgetHostViewAndroid 对象的成员函数 OnTouchEvent 的实现例如以下所看到的:
- bool RenderWidgetHostViewAndroid::OnTouchEvent(
- const ui::MotionEvent& event) {
- ......
- if (!gesture_provider_.OnTouchEvent(event))
- return false;
- ......
- // Short-circuit touch forwarding if no touch handlers exist.
- if (!host_->ShouldForwardTouchEvent()) {
- const bool event_consumed = false;
- gesture_provider_.OnTouchEventAck(event_consumed);
- return true;
- }
- SendTouchEvent(CreateWebTouchEventFromMotionEvent(event));
- return true;
- }
这个函数定义在文件 external/chromium_org/content/browser/renderer_host/render_widget_host_view_android.cc 中
RenderWidgetHostViewAndroid 类的成员变量 gesture_provider_描写叙述的是一个 FilteredGestureProvider 对象 RenderWidgetHostViewAndroid 类的成员函数 OnTouchEvent 首先调用这个 FilteredGestureProvider 对象的成员函数 OnTouchEvent 检測參数 event 描写叙述的 Touch 事件是否产生了手势操作假设有发生, 那么就会将它们发送给 Render 进程处理
RenderWidgetHostViewAndroid 类的成员变量 host_指向的是一个 RenderWidgetHostImpl 对象
这个 RenderWidgetHostImpl 对象也是用来在 C++ 层描写叙述载入网页的控件的, 它的创建过程能够參考前面 Chromium 硬件加速渲染的 OpenGL 上下文画图表面创建过程分析一文
RenderWidgetHostViewAndroid 类的成员函数 OnTouchEvent 接下来调用这个 RenderWidgetHostImpl 对象的成员函数 ShouldForwardTouchEvent 检查 Render 进程是否注冊了处理 Touch 事件的 Handler 假设没有注冊的话, 那么就不须要将參数 event 描写叙述的 Touch 事件发送给它处理了
我们假设 Render 进程注冊了处理 Touch 事件的 Handler
在这样的情况下 RenderWidgetHostViewAndroid 类的成员函数 OnTouchEvent 就会调用函数 CreateWebTouchEventFromMotionEvent 将參数 event 描写叙述的 Touch 事件封装成一个 blink::WebTouchEvent 对象, 而且调用另外一个成员函数 SendTouchEvent 将该 blink::WebTouchEvent 对象发送给 Render 进程处理注意, 这个 blink::WebTouchEvent 对象描写叙述的是原始的 Touch 事件, 它不是一个手势操作
接下来, 我们先分析 FilteredGestureProvider 类的成员函数 OnTouchEvent 检測手势操作的过程, 接着再分析函数 CreateWebTouchEventFromMotionEvent 创建 blink::WebTouchEvent 对象的过程, 以及 RenderWidgetHostViewAndroid 类的成员函数 SendTouchEvent 向 Render 进程发送 Touch 事件的过程
FilteredGestureProvider 类的成员函数 OnTouchEvent 的实现例如以下所看到的:
- bool FilteredGestureProvider::OnTouchEvent(const MotionEvent& event) {
- DCHECK(!handling_event_);
- base::AutoReset<bool> handling_event(&handling_event_, true);
- pending_gesture_packet_ = GestureEventDataPacket::FromTouch(event);
- if (!gesture_provider_.OnTouchEvent(event))
- return false;
- TouchDispositionGestureFilter::PacketResult result =
- gesture_filter_.OnGesturePacket(pending_gesture_packet_);
- if (result != TouchDispositionGestureFilter::SUCCESS) {
- NOTREACHED() << "Invalid touch gesture sequence detected.";
- return false;
- }
- return true;
- }
这个函数定义在文件 external/chromium_org/ui/events/gesture_detection/filtered_gesture_provider.cc 中
FilteredGestureProvider 类的成员函数 OnTouchEvent 首先将成员变量 handling_event_的值设置为 true, 表示当前正处于收集手势操作的过程中不要将正在收集的手势操作发送给 Render 进程处理而是等到全部收集完毕再一起发送给 Render 进程处理
注意, 当 FilteredGestureProvider 类的成员函数 OnTouchEvent 的调用结束后, FilteredGestureProvider 类的成员变量 handling_event 的值将自己主动恢复为 false
FilteredGestureProvider 类的成员函数 OnTouchEvent 接下来调用 GestureEventDataPacket 类的静态成员函数 FromTouch 创建一个用来保存手势操作的 Gesture Event Data Packet 例如以下所看到的:
- GestureEventDataPacket GestureEventDataPacket::FromTouch(
- const ui::MotionEvent& touch) {
- return GestureEventDataPacket(touch.GetEventTime(),
- ToGestureSource(touch),
- gfx::PointF(touch.GetX(), touch.GetY()),
- gfx::PointF(touch.GetRawX(), touch.GetRawY()));
- }
这个函数定义在文件 external/chromium_org/ui/events/gesture_detection/gesture_event_data_packet.cc 中
GestureEventDataPacket 类的静态成员函数 FromTouch 首先调用函数 ToGestureSource 获得接下来要创建的 Gesture Event Data Packet 的类型, 接着创建一个该类型的 Gesture Event Data Packet 返回给调用者
函数 ToGestureSource 的实现例如以下所看到的:
- GestureEventDataPacket::GestureSource ToGestureSource(
- const ui::MotionEvent& event) {
- switch (event.GetAction()) {
- case ui::MotionEvent::ACTION_DOWN:
- return GestureEventDataPacket::TOUCH_SEQUENCE_START;
- case ui::MotionEvent::ACTION_UP:
- return GestureEventDataPacket::TOUCH_SEQUENCE_END;
- case ui::MotionEvent::ACTION_MOVE:
- return GestureEventDataPacket::TOUCH_MOVE;
- case ui::MotionEvent::ACTION_CANCEL:
- return GestureEventDataPacket::TOUCH_SEQUENCE_CANCEL;
- case ui::MotionEvent::ACTION_POINTER_DOWN:
- return GestureEventDataPacket::TOUCH_START;
- case ui::MotionEvent::ACTION_POINTER_UP:
- return GestureEventDataPacket::TOUCH_END;
- };
- NOTREACHED() << "Invalid ui::MotionEvent action:" << event.GetAction();
- return GestureEventDataPacket::INVALID;
- }
这个函数定义在文件 external/chromium_org/ui/events/gesture_detection/gesture_event_data_packet.cc 中
函数 ToGestureSource 返回的 Gesture Event Data Packet 的类型与參数 evnet 描写叙述的 Touch 事件的类型有关比如假设 event 描写叙述的是一个 ACTION_MOVE 类型的 Touch 事件, 那么函数 ToGestureSource 返回的 Gesture Event Data Packet 的类型就为 GestureEventDataPacket::TOUCH_MOVE
在接下来的分析中, 我们就假设当前要处理的是一个 ACTION_MOVE 类型的 Touch 事件, 这意味着 FilteredGestureProvider 类的成员函数 OnTouchEvent 调用 GestureEventDataPacket 类的静态成员函数 FromTouch 获得的是一个类型为 GestureEventDataPacket::TOUCH_MOVE 的 Gesture Event Data Packet
这个 Gesture Event Data Packet 保存在 FilteredGestureProvider 类的成员变量 pending_gesture_packet_中
回到 FilteredGestureProvider 类的成员函数 OnTouchEvent 中它接下来调用成员变量 gesture_provider_描写叙述的一个 GestureProvider 对象的成员函数 OnTouchEvent 检查參数 event 描写叙述的 Touch 事件是否产生了手势操作
假设产生了, 那么就会将它们保存在成员变量 pending_gesture_packet_描写叙述的 Gesture Event Data Packet 中
FilteredGestureProvider 类的成员变量 gesture_filter_描写叙述的是一个 TouchDispositionGestureFilter 对象, FilteredGestureProvider 类的成员函数 OnTouchEvent 最后调用这个 TouchDispositionGestureFilter 对象的成员函数 OnGesturePacket 将成员变量 pending_gesture_packet_描写叙述的 Gesture Event Data Packet 发送给 Render 进程处理, 也就是将前面检測到的手势操作发送给 Render 进程处理
接下来我们先分析 GestureProvider 对象的成员函数 OnTouchEvent 检測手势操作的过程, 接下来再分析 TouchDispositionGestureFilter 类的成员函数 OnGesturePacket 发送手势操作给 Render 进程的过程
GestureProvider 类的成员函数 OnTouchEvent 的实现例如以下所看到的:
- bool GestureProvider::OnTouchEvent(const MotionEvent& event) {
- ......
- gesture_listener_->OnTouchEvent(event, in_scale_gesture);
- scale_gesture_listener_->OnTouchEvent(event);
- ......
- return true;
- }
这个函数定义在文件 external/chromium_org/ui/events/gesture_detection/gesture_provider.cc 中
GestureProvider 类的成员变量 gesture_listener_指向的是一个 GestureListenerImpl 对象
这个 GestureListenerImpl 对象负责检測參数 event 描写叙述的 Touch 事件是否产生滑动手势操作这是通过调用它的成员函数 OnTouchEvent 实现的
GestureProvider 类的成员变量 scale_gesture_listener_指向的是一个 ScaleGestureListenerImpl 对象
这个 ScaleGestureListenerImpl 对象负责检測參数 event 描写叙述的 Touch 事件是否产生捏合手势操作
这是通过调用它的成员函数 OnTouchEvent 实现的
接下来, 我们就分别分析 GestureListenerImpl 类和 ScaleGestureListenerImpl 类的成员函数 OnTouchEvent 的实现, 以便了解滑动和捏合手势操作的检測过程
GestureListenerImpl 类的成员函数 OnTouchEvent 的实现例如以下所看到的:
- class GestureProvider::GestureListenerImpl
- : public GestureDetector::GestureListener,
- public GestureDetector::DoubleTapListener {
- public:
- ......
- bool OnTouchEvent(const MotionEvent& e,
- bool is_scale_gesture_detection_in_progress) {
- ......
- return gesture_detector_.OnTouchEvent(e);
- }
- private:
- ......
- GestureDetector gesture_detector_;
- ......
- };
这个函数定义在文件 external/chromium_org/ui/events/gesture_detection/gesture_provider.cc 中
GestureListenerImpl 类的成员函数 OnTouchEvent 通过调用成员变量 gesture_detector_描写叙述的一个 GestureDetector 对象的成员函数 OnTouchEvent 检測測參数 e 描写叙述的 Touch 事件是否产生了滑动手势操作
GestureDetector 类的成员函数 OnTouchEvent 的实现例如以下所看到的:
- bool GestureDetector::OnTouchEvent(const MotionEvent& ev) {
- const MotionEvent::Action action = ev.GetAction();
- ......
- const bool pointer_up = action == MotionEvent::ACTION_POINTER_UP;
- const int skip_index = pointer_up ?
- ev.GetActionIndex() : -1;
- // Determine focal point.
- float sum_x = 0, sum_y = 0;
- const int count = static_cast<int>(ev.GetPointerCount());
- for (int i = 0; i < count; i++) {
- if (skip_index == i)
- continue;
- sum_x += ev.GetX(i);
- sum_y += ev.GetY(i);
- }
- const int div = pointer_up ? count - 1 : count;
- const float focus_x = sum_x / div;
- const float focus_y = sum_y / div;
- bool handled = false;
- switch (action) {
- ......
- case MotionEvent::ACTION_MOVE:
- {
- const float scroll_x = last_focus_x_ - focus_x;
- const float scroll_y = last_focus_y_ - focus_y;
- if (is_double_tapping_) {
- // Give the move events of the double-tap.
- DCHECK(double_tap_listener_);
- handled |= double_tap_listener_->OnDoubleTapEvent(ev);
- } else if (always_in_tap_region_) {
- const float delta_x = focus_x - down_focus_x_;
- const float delta_y = focus_y - down_focus_y_;
- const float distance_square = delta_x * delta_x + delta_y * delta_y;
- if (distance_square > touch_slop_square_) {
- handled = listener_->OnScroll(
- *current_down_event_, ev, scroll_x, scroll_y);
- last_focus_x_ = focus_x;
- last_focus_y_ = focus_y;
- always_in_tap_region_ = false;
- ......
- }
- ......
- } else if (std::abs(scroll_x) > kScrollEpsilon ||
- std::abs(scroll_y) > kScrollEpsilon) {
- handled =
- listener_->OnScroll(*current_down_event_, ev, scroll_x, scroll_y);
- last_focus_x_ = focus_x;
- last_focus_y_ = focus_y;
- }
- ......
- break;
- ......
- }
- return handled;
- }
这个函数定义在文件 external/chromium_org/ui/events/gesture_detection/gesture_detector.cc 中
前面我们假设參数 ev 描写叙述的是一个 ACTION_MOVE 类型的 Touch 事件
GestureDetector 类的成员函数 OnTouchEvent 首先会计算这个 Touch 事件的位置 (focus_x, focus_y) 注意, 这个 Touch 事件可能包括了多个触摸点因此在计算它的位置时, 通过将全部的触摸点进行算术平均得到
GestureDetector 类的成员变量 last_focus_x_和 last_focus_y_记录的是上一个类型为 ACTION_MOVE 的 Touch 事件的位置 (last_focus_x_, last_focus_y_)GestureDetector 类的成员函数 OnTouchEvent 通过比較(last_focus_x_, last_focus_y_) 和(focus_x, focus_y)的值, 得到连续两个类型为 ACTION_MOVE 的 Touch 事件在网页的 X 轴和 Y 轴上所产生的滑动量 scroll_x 和 scroll_y
GestureDetector 类的成员变量 is_double_tapping_是一个布尔变量当它的值等于 true 的时候表示用户在规定的时间和空间内连续点击了两次网页
这样的情况称为 Double Tap, 这时候 GestureDetector 类的成员函数 OnTouchEvent 会将參数 ev 描写叙述的类型为 ACTION_MOVE 的 Touch 事件交给成员变量 double_tap_listener_指向的一个 DoubleTapListener 对象的成员函数 OnDoubleTapEvent 处理
也就是说, Double Tap 之后的类型为 ACTION_MOVE 的 Touch 事件将不会产生滑动手势操作
GestureDetector 类的成员变量 always_in_tap_region_也是一个布尔变量当它的值等于 true 的时候, 表示用户之前触发了一个类型为 ACTION_DOWN 的 Touch 事件在这样的情况下, GestureDetector 类的成员函数 OnTouchEvent 须要计算当前发生的类型为 ACTION_MOVE 的 Touch 事件与之前触发的类型为 ACTION_DOWN 的 Touch 事件的位置距离当这个距离大于预设的值之时, GestureDetector 类的成员变量 always_in_tap_region_会被重置为 false, 表示后面触发类型为 ACTION_UP 的 Touch 事件时, 不要产生一个 Single Tap 事件与此同一时候, 须要产生一个滑动手势操作这个滑动手势操作通过调用 GestureDetector 类的成员变量 listener_描写叙述的一个 GestureListener 对象的成员函数 OnScroll 进行处理
从前面的分析我们能够看出, Single Tap 事件与滑动手势操作是相互排斥的一个 Single Tap 事件指的是指在规定时间和空间内先后发生了一个类型为 ACTION_DOWN 的 Touch 事件和一个类型为 ACTION_UP 的 Touch 事件在这两个 Touch 事件之间发生的类型为 ACTION_MOVE 的 Touch 事件将不会产生手势操作
当 GestureDetector 类的成员变量 is_double_tapping_和 always_in_tap_region_ 的值都等于 false 的时候 GestureDetector 类的成员函数 OnTouchEvent 检查连续两个类型为 ACTION_MOVE 的 Touch 事件在网页的 X 轴和 Y 轴上所产生的滑动量 scroll_x 和 scroll_y 是否超过了预设的阀值假设超过了, 那么就觉得产生了一个滑动手势操作
这个滑动手势操作也是通过调用 GestureDetector 类的成员变量 listener_描写叙述的一个 GestureListener 对象的成员函数 OnScroll 进行处理
接下来我们主要关注滑动手势操作的处理过程 GestureDetector 类的成员变量 listener_指向的实际上是一个 GestureListenerImpl 对象
这个 GestureListenerImpl 对象就是前面提到的 GestureProvider 类的成员变量 gesture_listener_所指向的 GestureListenerImpl 对象这意味着 GestureDetector 类的成员函数 OnTouchEvent 检測到的滑动手势操作将由这个 GestureListenerImpl 对象的成员函数 OnScroll 进行处理
GestureListenerImpl 类的成员函数 OnScroll 的实现例如以下所看到的:
- class GestureProvider::GestureListenerImpl
- : public GestureDetector::GestureListener,
- public GestureDetector::DoubleTapListener {
- public:
- ......
- virtual bool OnScroll(const MotionEvent& e1,
- const MotionEvent& e2,
- float raw_distance_x,
- float raw_distance_y) OVERRIDE {
- float distance_x = raw_distance_x;
- float distance_y = raw_distance_y;
- ......
- if (!provider_->IsScrollInProgress()) {
- // Note that scroll start hints are in distance traveled, where
- // scroll deltas are in the opposite direction.
- GestureEventDetails scroll_details(
- ET_GESTURE_SCROLL_BEGIN, -raw_distance_x, -raw_distance_y);
- // Use the co-ordinates from the touch down, as these co-ordinates are
- // used to determine which layer the scroll should affect.
- provider_->Send(CreateGesture(scroll_details,
- e2.GetId(),
- e2.GetEventTime(),
- e1.GetX(),
- e1.GetY(),
- e1.GetRawX(),
- e1.GetRawY(),
- e2.GetPointerCount(),
- GetBoundingBox(e2)));
- }
- if (distance_x || distance_y) {
- const gfx::RectF bounding_box = GetBoundingBox(e2);
- const gfx::PointF center = bounding_box.CenterPoint();
- const gfx::PointF raw_center =
- center + gfx::Vector2dF(e2.GetRawOffsetX(), e2.GetRawOffsetY());
- GestureEventDetails scroll_details(
- ET_GESTURE_SCROLL_UPDATE, -distance_x, -distance_y);
- provider_->Send(CreateGesture(scroll_details,
- e2.GetId(),
- e2.GetEventTime(),
- center.x(),
- center.y(),
- raw_center.x(),
- raw_center.y(),
- e2.GetPointerCount(),
- bounding_box));
- }
- return true;
- }
- ......
- };
这个函数定义在文件 external/chromium_org/ui/events/gesture_detection/gesture_provider.cc 中
GestureListenerImpl 类的成员函数 OnScroll 首先调用成员变量 provider_指向的一个 GestureProvider 对象的成员函数 IsScrollInProgress 推断网页当前是否正在滑动过程中假设不是的话, 那么就说明如今要開始对网页进行滑动
这时候 Browser 进程会先发送一个类型为 ET_GESTURE_SCROLL_BEGIN 的手势操作给 Render 进程
在网页有滑动的情况下也就是网页至少在 X 轴和 Y 轴之中的一个有偏移时 GestureListenerImpl 类的成员函数 OnScroll 接下来还会向 Render 进程发送一个类型为 ET_GESTURE_SCROLL_UPDATE 的手势操作
无论是类型为 ET_GESTURE_SCROLL_BEGIN 的手势操作, 还是类型为 ET_GESTURE_SCROLL_UPDATE 的手势操作, 它们都会通过函数 CreateGesture 封装为一个 GestureEventData 对象这两个 GestureEventData 对象都是通过调用 GestureListenerImpl 类的成员变量 provider_指向的 GestureProvider 对象的成员函数 Send 发送给 Render 进程的例如以下所看到的:
- void GestureProvider::Send(GestureEventData gesture) {
- ......
- client_->OnGestureEvent(gesture);
- }
这个函数定义在文件 external/chromium_org/ui/events/gesture_detection/gesture_provider.cc 中
GestureProvider 类的成员变量 client_指向的是一个 FilteredGestureProvider 对象这个 FilteredGestureProvider 对象就是前面分析的 RenderWidgetHostViewAndroid 类的成员变量 gesture_provider_所指向的 FilteredGestureProvider 对象
GestureProvider 类的成员函数 Send 主要是调用上述 FilteredGestureProvider 对象的成员函数 OnGestureEvent 将參数 gesture 描写叙述的手势操作发送给 Render 进程处理例如以下所看到的:
- void FilteredGestureProvider::OnGestureEvent(const GestureEventData& event) {
- if (handling_event_) {
- pending_gesture_packet_.Push(event);
- return;
- }
- gesture_filter_.OnGesturePacket(
- GestureEventDataPacket::FromTouchTimeout(event));
- }
这个函数定义在文件 external/chromium_org/ui/events/gesture_detection/filtered_gesture_provider.cc 中
从前面的分析能够知道, 当前正在处理的 FilteredGestureProvider 对象的成员变量 handling_event_已经被设置为 true, 表示此时只收集參数 event 描写叙述的手势操作而不要将它发送给 Render 进程
參数 event 描写叙述的手势操作被收集在 FilteredGestureProvider 类的成员变量 pending_gesture_packet_描写叙述的一个 Gesture Event Data Packet 中从前面的分析能够知道, 这个 Gesture Event Data Packet 的类型为 GestureEventDataPacket::TOUCH_MOVE
假设当前正在处理的 FilteredGestureProvider 对象的成员变量 handling_event_的值不等于 true, 那么 FilteredGestureProvider 类的成员函数 OnGestureEvent 将会直接将參数 event 描写叙述的手势操作发送给 Render 进程, 这是通过调用另外一个成员变量 gesture_filter_描写叙述的一个 TouchDispositionGestureFilter 对象的成员函数 OnGesturePacket 实现的后面我们再分析这个发送过程
这一步运行完毕后 Browser 进程就对当前发生的 Touch 事件进行了滑动手势检測, 而且检測到的滑动手势操作已经保存在一个 Gesture Event Data Packet 中回到前面分析的 GestureProvider 类的成员函数 OnTouchEvent 中, 接下来它会继续调用另外一个成员变量 scale_gesture_listener_指向的是 ScaleGestureListenerImpl 对象的成员函数 OnTouchEvent 检測当前发生的 Touch 事件是否产生了捏合手势操作
假设产生了, 那么相同将它收集在上述的 Gesture Event Data Packet 中
接下来我们就继续分析捏合手势操作的检測过程, 也就是 ScaleGestureListenerImpl 类的成员函数 OnTouchEvent 的实现, 例如以下所看到的:
- class GestureProvider::ScaleGestureListenerImpl
- : public ScaleGestureDetector::ScaleGestureListener {
- public:
- ......
- bool OnTouchEvent(const MotionEvent& event) {
- ......
- bool handled = scale_gesture_detector_.OnTouchEvent(event);
- ......
- return handled;
- }
- ......
- private:
- ......
- ScaleGestureDetector scale_gesture_detector_;
- ......
- };
这个函数定义在文件 external/chromium_org/ui/events/gesture_detection/gesture_provider.cc 中
ScaleGestureListenerImpl 类的成员函数 OnTouchEvent 主要是调用成员变量 scale_gesture_detector_描写叙述的一个 ScaleGestureDetector 对象的成员函数 OnTouchEvent 检測參数 event 描写叙述的 Touch 事件是否产生捏合手势操作
ScaleGestureDetector 类的成员函数 OnTouchEvent 的实现例如以下所看到的:
- bool ScaleGestureDetector::OnTouchEvent(const MotionEvent& event) {
- ......
- const int action = event.GetAction();
- ......
- // Span is the average distance between touch points through the focal point;
- // i.e. the diameter of the circle with a radius of the average deviation from
- // the focal point.
- const float span_x = dev_x * 2;
- const float span_y = dev_y * 2;
- float span;
- if (InDoubleTapMode()) {
- span = span_y;
- } else {
- span = std::sqrt(span_x * span_x + span_y * span_y);
- }
- ......
- const float min_span = InDoubleTapMode() ? span_slop_ : min_span_;
- if (!in_progress_ && span >= min_span && (InDoubleTapMode() || count > 1) &&
- (was_in_progress || std::abs(span - initial_span_) > span_slop_)) {
- ......
- in_progress_ = listener_->OnScaleBegin(*this, event);
- }
- // Handle motion; focal point and span/scale factor are changing.
- if (action == MotionEvent::ACTION_MOVE) {
- ......
- if (in_progress_) {
- update_prev = listener_->OnScale(*this, event);
- }
- ......
- }
- return true;
- }
这个函数定义在文件 external/chromium_org/ui/events/gesture_detection/scale_gesture_detector.cc 中
ScaleGestureDetector 类的成员函数 OnTouchEvent 首先计算网页被捏合的大小 span 这是依据网页在 X 轴和 Y 轴上的捏合大小 span_x 和 span_y 计算得到的
ScaleGestureDetector 类的成员函数 OnTouchEvent 接下来推断网页被捏合的大小 span 是否大于等于预设的阀值
假设大于等于, 而且网页是刚開始被捏合, 那么就会调用成员变量 listener_指向的一个 ScaleGestureListenerImpl 对象的成员函数 OnScaleBegin 用来询问是否同意产生一个捏合手势操作假设同意的话, ScaleGestureDetector 类的成员变量 in_progress_就会被设置为 true
上述 ScaleGestureListenerImpl 对象就是前面分析的 GestureProvider 类的成员变量 scale_gesture_listener_所指向的 ScaleGestureListenerImpl 对象它的成员函数 OnScaleBegin 的实现例如以下所看到的:
- class GestureProvider::ScaleGestureListenerImpl
- : public ScaleGestureDetector::ScaleGestureListener {
- public:
- ......
- virtual bool OnScaleBegin(const ScaleGestureDetector& detector,
- const MotionEvent& e) OVERRIDE {
- if (ignore_multitouch_events_ && !detector.InDoubleTapMode())
- return false;
- ......
- return true;
- }
- ......
- };
这个函数定义在文件 external/chromium_org/ui/events/gesture_detection/gesture_provider.cc 中
ScaleGestureListenerImpl 类的成员函数 OnScaleBegin 在两种情况下的返回值为 true 第一种情况是当它的成员变量 ignore_multitouch_events_的值等于 false 时
这表示当网页被多点触摸时, 有可能须要对网页进行缩放, 也就是要产生一个捏合手势操作
另外一种情况是网页被 Double Tap 时
回到 ScaleGestureDetector 类的成员函数 OnTouchEvent 中综合起来, 我们就能够知道, 当网页被多点触摸移动或者 Double Tap 后移动, 而且移动的距离或者两次 Tap 的距离大于等于预设值时那么就会产生捏合手势操作
这个捏合手势操作将会交给 ScaleGestureDetector 类的成员变量 listener_指向的 ScaleGestureListenerImpl 对象的成员函数 OnScale 处理例如以下所看到的:
- class GestureProvider::ScaleGestureListenerImpl
- : public ScaleGestureDetector::ScaleGestureListener {
- public:
- ......
- virtual bool OnScale(const ScaleGestureDetector& detector,
- const MotionEvent& e) OVERRIDE {
- ......
- if (!pinch_event_sent_) {
- pinch_event_sent_ = true;
- provider_->Send(CreateGesture(ET_GESTURE_PINCH_BEGIN,
- e.GetId(),
- detector.GetEventTime(),
- detector.GetFocusX(),
- detector.GetFocusY(),
- detector.GetFocusX() + e.GetRawOffsetX(),
- detector.GetFocusY() + e.GetRawOffsetY(),
- e.GetPointerCount(),
- GetBoundingBox(e)));
- }
- ......
- float scale = detector.GetScaleFactor();
- ......
- GestureEventDetails pinch_details(ET_GESTURE_PINCH_UPDATE, scale, 0);
- provider_->Send(CreateGesture(pinch_details,
- e.GetId(),
- detector.GetEventTime(),
- detector.GetFocusX(),
- detector.GetFocusY(),
- detector.GetFocusX() + e.GetRawOffsetX(),
- detector.GetFocusY() + e.GetRawOffsetY(),
- e.GetPointerCount(),
- GetBoundingBox(e)));
- return true;
- }
- ......
- };
这个函数定义在文件 external/chromium_org/ui/events/gesture_detection/gesture_provider.cc 中
ScaleGestureListenerImpl 类的成员函数 OnScale 首先检查网页是否刚刚開始被捏合假设是的话, ScaleGestureListenerImpl 类的成员变量 pinch_event_sent_的值就会等于 false 在这样的情况下, Browser 进程会先发送一个类型为 ET_GESTURE_PINCH_BEGIN 的手势操作给 Render 进程
ScaleGestureListenerImpl 类的成员函数 OnScale 接下来又通过调用參数 detector 描写叙述的 ScaleGestureDetector 对象的成员函数 GetScaleFactor 得到捏合手势操作所产生的缩放因子, 然后将这个缩放因子封装在一个类型为 ET_GESTURE_PINCH_UPDATE 的手势操作中发送给 Render 进程
与前面提到的类型为 ET_GESTURE_SCROLL_BEGIN 和 ET_GESTURE_SCROLL_UPDATE 的手势操作一样, 类型为 ET_GESTURE_PINCH_BEGIN 和 ET_GESTURE_PINCH_UPDATE 的手势操作也是通过 GestureProvider 类的成员函数 Send 发送给 Render 进程的可是在我们这个情景中 GestureProvider 类的成员函数 Send 并没有将这些手势操作发送给 Render 进程, 而只是将它们收集在一个 Gesture Event Data Packet 中
这一步运行完毕之后 Browser 进程就对当前发生的 Touch 事件进行了滑动手势和捏合手势检測, 而且检測出来的手势操作 (ET_GESTURE_SCROLL_BEGINET_GESTURE_SCROLL_UPDATEET_GESTURE_PINCH_BEGIN 和 ET_GESTURE_PINCH_UPDATE) 都保存了 FilteredGestureProvider 类的成员变量 pending_gesture_packet_描写叙述的一个类型为 GestureEventDataPacket::TOUCH_MOVE 的 Gesture Event Data Packet
回到 FilteredGestureProvider 类的成员函数 OnTouchEvent 中, 它接下来要做的工作就将保存在成员变量 pending_gesture_packet_描写叙述的 Gesture Event Data Packet 中的手势操作发送给 Render 进程处理这是通过调用另外一个成员变量 gesture_filter_描写叙述的一个 TouchDispositionGestureFilter 对象的成员函数 OnGesturePacket 实现的, 例如以下所看到的:
- TouchDispositionGestureFilter::PacketResult
- TouchDispositionGestureFilter::OnGesturePacket(
- const GestureEventDataPacket& packet) {
- ......
- if (packet.gesture_source() == GestureEventDataPacket::TOUCH_TIMEOUT &&
- Tail().empty()) {
- // Handle the timeout packet immediately if the packet preceding the timeout
- // has already been dispatched.
- FilterAndSendPacket(packet);
- return SUCCESS;
- }
- Tail().push(packet);
- return SUCCESS;
- }
这个函数定义在文件 external/chromium_org/ui/events/gesture_detection/touch_disposition_gesture_filter.cc 中
TouchDispositionGestureFilter 类的成员函数 OnGesturePacket 首先推断參数 packet 描写叙述的 Gesture Event Data Packet 的类型是否等于 GestureEventDataPacket::TOUCH_TIMEOUT 假设等于而且当前的 Gesture Event Data Packet 队列为空那么參数 packet 描写叙述的 Gesture Event Data Packet 就会立即被发送给 Render 进程这个发送过程是通过调用 TouchDispositionGestureFilter 类的成员函数 FilterAndSendPacket 进行的
从前面的分析能够知道參数 packet 描写叙述的 Gesture Event Data Packet 的类型为 GestureEventDataPacket::TOUCH_MOVE, 因此它将不会立即被发送给 Render 进程, 而是被保存在一个 Gesture Event Data Packet 队列中那么, 这个队列中的 Gesture Event Data Packet 什么会被发送给 Render 进程呢? 当 Render 进程处理完毕 Browser 进程上一次发送给它的 Gesture Event Data Packet 之后它就会给 Browser 进程发送一个 ACKBrowser 进程接收到这个 ACK 之后, 就会从队列中取出下一个 Gesture Event Data Packet 发送给 Render 进程处理
这个发送过程相同也是通过调用 TouchDispositionGestureFilter 类的成员函数 FilterAndSendPacket 进行的因此接下来我们就继续分析 TouchDispositionGestureFilter 类的成员函数 FilterAndSendPacket 的实现, 例如以下所看到的:
- void TouchDispositionGestureFilter::FilterAndSendPacket(
- const GestureEventDataPacket& packet) {
- ......
- for (size_t i = 0; i < packet.gesture_count(); ++i) {
- const GestureEventData& gesture = packet.gesture(i);
- ......
- SendGesture(gesture, packet);
- }
- ......
- }
这个函数定义在文件 external/chromium_org/ui/events/gesture_detection/touch_disposition_gesture_filter.cc 中
TouchDispositionGestureFilter 类的成员函数 FilterAndSendPacket 遍历保存在參数 packet 描写叙述的 Gesture Event Data Packet 中的每个手势操作, 而且调用另外一个成员函数 SendGesture 分别将这些手势操作发送给 Render 进程例如以下所看到的:
- void TouchDispositionGestureFilter::SendGesture(
- const GestureEventData& event,
- const GestureEventDataPacket& packet_being_sent) {
- ......
- client_->ForwardGestureEvent(event);
- }
这个函数定义在文件 external/chromium_org/ui/events/gesture_detection/touch_disposition_gesture_filter.cc 中
TouchDispositionGestureFilter 类的成员变量 client_指向的是一个 FilteredGestureProvider 对象
这个 FilteredGestureProvider 对象就是前面分析的 RenderWidgetHostViewAndroid 类的成员变量 gesture_provider_所描写叙述的 FilteredGestureProvider 对象 TouchDispositionGestureFilter 类的成员函数 SendGesture 通过调用这个 FilteredGestureProvider 对象的成员函数 ForwardGestureEvent 将參数 event 描写叙述的手势操作发送给 Render 进程
FilteredGestureProvider 类的成员函数 ForwardGestureEvent 的实现例如以下所看到的:
- void FilteredGestureProvider::ForwardGestureEvent(
- const GestureEventData& event) {
- client_->OnGestureEvent(event);
- }
这个函数定义在文件 external/chromium_org/ui/events/gesture_detection/filtered_gesture_provider.cc 中
FilteredGestureProvider 类的成员变量 client_指向的是一个 RenderWidgetHostViewAndroid 对象这个 RenderWidgetHostViewAndroid 对象就前面描写叙述的在 Browser 进程中用来载入网页的控件 FilteredGestureProvider 类的成员函数 ForwardGestureEvent 通过调用这个 RenderWidgetHostViewAndroid 对象的成员函数 OnGestureEvent 将參数 event 描写叙述的手势操作发送给 Render 进程
RenderWidgetHostViewAndroid 类的成员函数 OnGestureEvent 的实现例如以下所看到的:
- void RenderWidgetHostViewAndroid::OnGestureEvent(
- const ui::GestureEventData& gesture) {
- ......
- SendGestureEvent(CreateWebGestureEventFromGestureEventData(gesture));
- }
这个函数定义在文件 external/chromium_org/content/browser/renderer_host/render_widget_host_view_android.cc 中
RenderWidgetHostViewAndroid 类的成员函数 OnGestureEvent 首先调用函数 CreateWebGestureEventFromGestureEventData 将參数 gesture 描写叙述的手势操作封装在一个 WebGestureEvent 对象中, 例如以下所看到的:
- WebGestureEvent CreateWebGestureEventFromGestureEventData(const ui: :GestureEventData & data) {
- WebGestureEvent gesture;
- gesture.x = data.x;
- gesture.y = data.y;
- gesture.globalX = data.raw_x;
- gesture.globalY = data.raw_y;
- gesture.timeStampSeconds = (data.time - base: :TimeTicks()).InSecondsF();
- gesture.sourceDevice = blink: :WebGestureDeviceTouchscreen;
- switch (data.type()) {......
- case ui:
- :
- ET_GESTURE_SCROLL_UPDATE:
- gesture.type = WebInputEvent: :GestureScrollUpdate;
- gesture.data.scrollUpdate.deltaX = data.details.scroll_x();
- gesture.data.scrollUpdate.deltaY = data.details.scroll_y();
- break;......
- case ui:
- :
- ET_GESTURE_PINCH_UPDATE:
- gesture.type = WebInputEvent: :GesturePinchUpdate;
- gesture.data.pinchUpdate.scale = data.details.scale();
- break;......
- }
- return gesture;
- }
这个函数定义在文件 external/chromium_org/content/browser/renderer_host/input/web_input_event_util.cc 中
函数 CreateWebGestureEventFromGestureEventData 会将不同类型的手势操作封装在不同类型的 WebGestureEvent 对象中比如, ui::ET_GESTURE_SCROLL_UPDATE 类的手势操作, 即滑动手势操作, 会封装在一个类型为 WebInputEvent::GestureScrollUpdate 的 WebGestureEvent 对象中又如, ui::ET_GESTURE_PINCH_UPDATE 类型的手势操作, 即捏合手势操作, 会封装在一个类型为 WebInputEvent::GesturePinchUpdate 的 WebGestureEvent 对象中
回到 RenderWidgetHostViewAndroid 类的成员函数 OnGestureEvent 中它将手势操作封装在一个 WebGestureEvent 对象之后, 再调用另外一个成员函数 SendGestureEvent 将这个 WebGestureEvent 对象发送给 Render 进程例如以下所看到的:
- void RenderWidgetHostViewAndroid::SendGestureEvent(
- const blink::WebGestureEvent& event) {
- ......
- if (host_)
- host_->ForwardGestureEventWithLatencyInfo(event, CreateLatencyInfo(event));
- }
这个函数定义在文件 external/chromium_org/content/browser/renderer_host/render_widget_host_view_android.cc 中
RenderWidgetHostViewAndroid 类的成员变量 host_指向的是一个 RenderWidgetHostImpl 对象这个 RenderWidgetHostImpl 对象描写叙述的是载入当前正在发生输入事件的网页的 Render 进程 RenderWidgetHostViewAndroid 类的成员函数 SendGestureEvent 调用这个 RenderWidgetHostImpl 对象的成员函数 ForwardGestureEventWithLatencyInfo 将參数 event 描写叙述的手势操作发送给它所描写叙述的 Render 进程
RenderWidgetHostImpl 类的成员函数 ForwardGestureEventWithLatencyInfo 的实现例如以下所看到的:
- void RenderWidgetHostImpl::ForwardGestureEventWithLatencyInfo(
- const blink::WebGestureEvent& gesture_event,
- const ui::LatencyInfo& ui_latency) {
- ......
- GestureEventWithLatencyInfo gesture_with_latency(gesture_event, latency_info);
- input_router_->SendGestureEvent(gesture_with_latency);
- }
这个函数定义在文件 external/chromium_org/content/browser/renderer_host/render_widget_host_impl.cc 中
RenderWidgetHostImpl 类的成员函数 ForwardGestureEventWithLatencyInfo 首先将參数 gesture_event 描写叙述的手势操作封装在一个 GestureEventWithLatencyInfo 对象中
RenderWidgetHostImpl 类的成员变量 input_router_指向的是一个 InputRouterImpl 对象这个 InputRouterImpl 负责将输入事件发送给 Render 进程
因此, RenderWidgetHostImpl 类的成员函数 SendGestureEvent 就通过调用这个 InputRouterImpl 对象的成员函数 SendGestureEvent 将上述封装了手势操作的 GestureEventWithLatencyInfo 对象发送给 Render 进程
InputRouterImpl 类的成员函数 SendGestureEvent 的实现例如以下所看到的:
- void InputRouterImpl::SendGestureEvent(
- const GestureEventWithLatencyInfo& original_gesture_event) {
- ......
- GestureEventWithLatencyInfo gesture_event(original_gesture_event);
- ......
- SendGestureEventImmediately(gesture_event);
- }
这个函数定义在文件 external/chromium_org/content/browser/renderer_host/input/input_router_impl.cc 中
InputRouterImpl 类的成员函数 SendGestureEvent 主要是调用另外一个成员函数 SendGestureEventImmediately 将參数 original_gesture_event 描写叙述的手势操作发送给 Render 进程, 例如以下所看到的:
- void InputRouterImpl::SendGestureEventImmediately(
- const GestureEventWithLatencyInfo& gesture_event) {
- ......
- FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false);
- }
这个函数定义在文件 external/chromium_org/content/browser/renderer_host/input/input_router_impl.cc 中
InputRouterImpl 类的成员函数 SendGestureEventImmediately 又主要是调用另外一个成员函数 FilterAndSendWebInputEvent 将參数 gesture_event 描写叙述的手势操作发送给 Render 进程例如以下所看到的:
- void InputRouterImpl::FilterAndSendWebInputEvent(
- const WebInputEvent& input_event,
- const ui::LatencyInfo& latency_info,
- bool is_keyboard_shortcut) {
- ......
- OfferToHandlers(input_event, latency_info, is_keyboard_shortcut);
- }
这个函数定义在文件 external/chromium_org/content/browser/renderer_host/input/input_router_impl.cc 中
InputRouterImpl 类的成员函数 FilterAndSendWebInputEvent 又主要是调用另外一个成员函数 OfferToHandlers 将參数 input_event 描写叙述的手势操作发送给 Render 进程例如以下所看到的:
- void InputRouterImpl::OfferToHandlers(const WebInputEvent& input_event,
- const ui::LatencyInfo& latency_info,
- bool is_keyboard_shortcut) {
- ......
- if (OfferToClient(input_event, latency_info))
- return;
- OfferToRenderer(input_event, latency_info, is_keyboard_shortcut);
- ......
- }
这个函数定义在文件 external/chromium_org/content/browser/renderer_host/input/input_router_impl.cc 中
InputRouterImpl 类的成员函数 OfferToHandlers 首先调用成员函数 OfferToClient 询问 Browser 进程是否要过滤參数 input_event 描写叙述的手势操作
假设过滤的话, 那么 InputRouterImpl 类的成员函数 OfferToHandlers 就不会将它发送给 Render 进程否则的话, 就会调用另外一个成员函数 OfferToRenderer 进行发送
我们假设 Browser 进程只是滤參数 input_event 描写叙述的手势操作, 因此接下来这个手势就会通过 InputRouterImpl 类的成员函数 OfferToRenderer 发送给 Render 进程例如以下所看到的:
- bool InputRouterImpl::OfferToRenderer(const WebInputEvent& input_event,
- const ui::LatencyInfo& latency_info,
- bool is_keyboard_shortcut) {
- if (Send(new InputMsg_HandleInputEvent(
- routing_id(), &input_event, latency_info, is_keyboard_shortcut))) {
- ......
- return true;
- }
- return false;
- }
这个函数定义在文件 external/chromium_org/content/browser/renderer_host/input/input_router_impl.cc 中
从这里就能够看到, InputRouterImpl 类的成员函数 OfferToRenderer 会将參数 input_event 描写叙述的手势操作封装在一个类型为 InputMsg_HandleInputEvent 的 IPC 消息中, 然后再将这个消息发送给 Render 进程处理这个处理过程我们在接下来的一篇文章中再具体分析
这一步运行完毕后, Browser 进程就将检測到的手势操作发送给 Render 进程了回到前面分析的 RenderWidgetHostViewAndroid 类的成员函数 OnTouchEvent 中, 它接下来再调用函数 CreateWebTouchEventFromMotionEvent 将原始的 Touch 事件封装在一个 blink::WebTouchEvent 对象中例如以下所看到的:
- blink::WebTouchEvent CreateWebTouchEventFromMotionEvent(
- const ui::MotionEvent& event) {
- blink::WebTouchEvent result;
- WebTouchEventTraits::ResetType(
- ToWebInputEventType(event.GetAction()),
- (event.GetEventTime() - base::TimeTicks()).InSecondsF(),
- &result);
- result.touchesLength =
- std::min(event.GetPointerCount(),
- static_cast<size_t>(WebTouchEvent::touchesLengthCap));
- DCHECK_GT(result.touchesLength, 0U);
- for (size_t i = 0; i < result.touchesLength; ++i)
- result.touches[i] = CreateWebTouchPoint(event, i);
- return result;
- }
这个函数定义在文件 external/chromium_org/content/browser/renderer_host/input/web_input_event_util.cc 中
函数 CreateWebTouchEventFromMotionEvent 首先调用函数 ToWebInputEventType 获得接下来要创建的 blink::WebTouchEvent 对象的类型, 例如以下所看到的:
- WebInputEvent::Type ToWebInputEventType(MotionEvent::Action action) {
- switch (action) {
- case MotionEvent::ACTION_DOWN:
- return WebInputEvent::TouchStart;
- case MotionEvent::ACTION_MOVE:
- return WebInputEvent::TouchMove;
- case MotionEvent::ACTION_UP:
- return WebInputEvent::TouchEnd;
- case MotionEvent::ACTION_CANCEL:
- return WebInputEvent::TouchCancel;
- case MotionEvent::ACTION_POINTER_DOWN:
- return WebInputEvent::TouchStart;
- case MotionEvent::ACTION_POINTER_UP:
- return WebInputEvent::TouchEnd;
- }
- NOTREACHED() << "Invalid MotionEvent::Action.";
- return WebInputEvent::Undefined;
- }
这个函数定义在文件 external/chromium_org/content/browser/renderer_host/input/web_input_event_util.cc 中
參数 action 表示要封装的 Touch 事件的类型
函数 ToWebInputEventType 会依据不同的 Touch 事件类型返回不同的 blink::WebTouchEvent 对象类型
比如, 对于类型为 MotionEvent::ACTION_MOVE 的 Touch 事件函数 ToWebInputEventType 返回的 blink::WebTouchEvent 对象类型为 WebInputEvent::TouchMove
回到函数 CreateWebTouchEventFromMotionEvent 中它获得了接下来要创建的 blink::WebTouchEvent 对象的类型之后, 就会创建这个 blink::WebTouchEvent 对象而且会将 event 描写叙述的 Touch 事件的全部信息比如触摸点位置, 保存在创建出来的 blink::WebTouchEvent 对象中
这一步运行完毕之后再回到前面分析的 RenderWidgetHostViewAndroid 类的成员函数 OnTouchEvent 中, 它接下来就会将前面创建的 blink::WebTouchEvent 对象发送给 Render 进程处理这是通过调用另外一个成员函数 SendTouchEvent 实现的, 例如以下所看到的:
- void RenderWidgetHostViewAndroid::SendTouchEvent(
- const blink::WebTouchEvent& event) {
- if (host_)
- host_->ForwardTouchEventWithLatencyInfo(event, CreateLatencyInfo(event));
- ......
- }
这个函数定义在文件 external/chromium_org/content/browser/renderer_host/render_widget_host_view_android.cc 中
前面提到, RenderWidgetHostViewAndroid 类的成员变量 host_指向的是一个 RenderWidgetHostImpl 对象, RenderWidgetHostViewAndroid 类的成员函数 SendTouchEvent 调用这个 RenderWidgetHostImpl 对象的成员函数 ForwardTouchEventWithLatencyInfo 将參数 event 描写叙述的 Touch 事件发送给 Render 进程
RenderWidgetHostImpl 类的成员函数 ForwardTouchEventWithLatencyInfo 的实现例如以下所看到的:
- void RenderWidgetHostImpl::ForwardTouchEventWithLatencyInfo(
- const blink::WebTouchEvent& touch_event,
- const ui::LatencyInfo& ui_latency) {
- ......
- ui::LatencyInfo latency_info =
- CreateRWHLatencyInfoIfNotExist(&ui_latency, touch_event.type);
- TouchEventWithLatencyInfo touch_with_latency(touch_event, latency_info);
- input_router_->SendTouchEvent(touch_with_latency);
- }
这个函数定义在文件 external/chromium_org/content/browser/renderer_host/render_widget_host_impl.cc 中
RenderWidgetHostImpl 类的成员函数 ForwardTouchEventWithLatencyInfo 首先将參数 touch_event 描写叙述的 Touch 事件封装在一个 TouchEventWithLatencyInfo 对象中, 然后再调用成员变量 input_router_指向的一个 InputRouterImpl 对象的成员函数 SendTouchEvent 将这个 TouchEventWithLatencyInfo 对象发送给 Render 进程
InputRouterImpl 类的成员函数 SendTouchEvent 的实现例如以下所看到的:
- void InputRouterImpl::SendTouchEvent(
- const TouchEventWithLatencyInfo& touch_event) {
- ......
- touch_event_queue_.QueueEvent(touch_event);
- }
这个函数定义在文件 external/chromium_org/content/browser/renderer_host/input/input_router_impl.cc 中
InputRouterImpl 类的成员变量 touch_event_queue_描写叙述的是一个 TouchEventQueue 对象, InputRouterImpl 类的成员函数 SendTouchEvent 调用这个 TouchEventQueue 对象的成员函数 QueueEvent 将參数 touch_event 描写叙述的 Touch 事件发送给 Render 进程
TouchEventQueue 类的成员函数 QueueEvent 的实现例如以下所看到的:
- void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) {
- ......
- // If the queueing of |event| was triggered by an ack dispatch, defer
- // processing the event until the dispatch has finished.
- if (touch_queue_.empty() && !dispatching_touch_ack_) {
- ......
- // There is no touch event in the queue. Forward it to the renderer
- // immediately.
- touch_queue_.push_back(new CoalescedWebTouchEvent(event, false));
- ForwardNextEventToRenderer();
- return;
- }
- // If the last queued touch-event was a touch-move, and the current event is
- // also a touch-move, then the events can be coalesced into a single event.
- if (touch_queue_.size() > 1) {
- CoalescedWebTouchEvent* last_event = touch_queue_.back();
- if (last_event->CoalesceEventIfPossible(event))
- return;
- }
- touch_queue_.push_back(new CoalescedWebTouchEvent(event, false));
- }
这个函数定义在文件 external/chromium_org/content/browser/renderer_host/input/touch_event_queue.cc 中
TouchEventQueue 类的成员变量 touch_queue_描写叙述的是一个 Touch 事件队列这个队列用来暂存即将要发送给 Render 进程的 Touch 事件
一个即将要发送的 Touch 事件在两种情况下须要暂存在队列中:
1. 在它之前的 Touch 事件还未发送给 Render 进程, 即 Touch 事件队列不为空
2. Render 进程正在发送一个 ACK 事件给 Browser 进程, 而 Browser 进程正在分发这个 ACK 事件这个 ACK 事件分发完毕之后 Browser 进程才干够将下一个 Touch 事件发送给 Render 进程处理这时候 TouchEventQueue 类的成员变量 dispatching_touch_ack_的值就不等于 NULL, 它指向正在分发的 ACK 事件
TouchEventQueue 类的成员函数 QueueEvent 所做的事情就是推断參数 event 描写叙述的 Touch 事件能否够立即发送假设能立即发送, 那么就会将它保存在 Touch 事件队列中, 然后再调用另外一个成员函数 ForwardNextEventToRenderer 将它从 Touch 事件队列读取出来, 而且发送给 Render 进程假设不能立即发送, 那么相同会将它保存在 Touch 事件队列中, 只是要等到上一个发送给 Render 进程的 Touch 事件被 ACK 之后, 才干继续将它发送给 Render 进程这相同是通过调用 TouchEventQueue 类的成员函数 ForwardNextEventToRenderer 进行发送的
我们注意到, 在将參数 event 描写叙述的 Touch 事件保存在 Touch 事件队列之前, 假设队列不为空, 那么 TouchEventQueue 类的成员函数 QueueEvent 会推断參数 event 描写叙述的 Touch 事件与队列中最后一个 Touch 事件是否是相同的, 也就是它们所包括的触摸点都是一样的假设相同, 那么就能够合并为一个 Touch 事件发送给 Render 进程
合并后的 Touch 事件使用一个 CoalescedWebTouchEvent 对象描写叙述
这样能够避免反复向 Render 进程发送相同的 Touch 事件
我们假设參数 event 描写叙述的 Touch 事件能够立即发送给 Render 进程, 因此接下来我们就继续分析 TouchEventQueue 类的成员函数 ForwardNextEventToRenderer 的实现, 例如以下所看到的:
- void TouchEventQueue::ForwardNextEventToRenderer() {
- ......
- TouchEventWithLatencyInfo touch = touch_queue_.front()->coalesced_event();
- ......
- // A synchronous ack will reset |dispatching_touch_|, in which case
- // the touch timeout should not be started.
- base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true);
- SendTouchEventImmediately(touch);
- ......
- }
这个函数定义在文件 external/chromium_org/content/browser/renderer_host/input/touch_event_queue.cc 中
TouchEventQueue 类的成员函数 ForwardNextEventToRenderer 首先从 Touch 事件队列中取出第一个 Touch 事件, 然后调用另外一个成员函数 SendTouchEventImmediately 将该 Touch 事件发送给 Render 进程
在发送的过程中, TouchEventQueue 类的成员变量 dispatching_touch_会被设置为 true 而且会在发送结束后 (也就是 TouchEventQueue 类的成员函数 ForwardNextEventToRenderer 调用结束) 恢复为 false
TouchEventQueue 类的成员函数 SendTouchEventImmediately 的实现例如以下所看到的:
- void TouchEventQueue::SendTouchEventImmediately(
- const TouchEventWithLatencyInfo& touch) {
- ......
- client_->SendTouchEventImmediately(touch);
- }
这个函数定义在文件 external/chromium_org/content/browser/renderer_host/input/touch_event_queue.cc 中
TouchEventQueue 类的成员变量 client_指向的是一个 InputRouterImpl 对象这个 InputRouterImpl 对象就前面分析的 RenderWidgetHostImpl 类的成员变量 input_router_所指向的 InputRouterImpl 对象
TouchEventQueue 类的成员函数 SendTouchEventImmediately 调用这个 InputRouterImpl 对象的成员函数 SendTouchEventImmediately 将參数 touch 描写叙述的 Touch 事件发送给 Render 进程
InputRouterImpl 类的成员函数 SendTouchEventImmediately 的实现例如以下所看到的:
- void InputRouterImpl::SendTouchEventImmediately(
- const TouchEventWithLatencyInfo& touch_event) {
- ......
- FilterAndSendWebInputEvent(touch_event.event, touch_event.latency, false);
- }
这个函数定义在文件 external/chromium_org/content/browser/renderer_host/input/input_router_impl.cc 中
从这里能够看到, InputRouterImpl 类的成员函数 SendTouchEventImmediately 是调用我们前面已经分析过的另外一个成员函数 FilterAndSendWebInputEvent 将參数 touch_event 描写叙述的 Touch 事件发送给 Render 进程的从前面的分析能够知道, 这个 Touch 事件封装在一个类型为 WebInputEvent::TouchMove 的 WebInputEvent 对象中, 它的发送过程与前面分析的滑动手势操作和捏合手势操作的发送过程是一样的, 只只是后两者分别封装在类型为 WebInputEvent::GestureScrollUpdate 和 WebInputEvent::GesturePinchUpdate 的 WebInputEvent 对象中
至此, 我们就以 Touch 事件为例分析完毕了 Browser 进程捕捉网页输入事件, 以及从中检測手势操作的过程这些网页输入事件和手势操作都是通过类型为 InputMsg_HandleInputEvent 的 IPC 消息发送给 Render 进程处理的在接下来的两篇文章中我们就具体分析 Render 进程处理网页输入事件和手势操作的过程也就是 Render 进程处理类型为 InputMsg_HandleInputEvent 的 IPC 消息的过程, 敬请关注!
很多其他的信息也能够关注老罗的新浪微博: http://weibo.com/shengyangluo
来源: http://www.bubuko.com/infodetail-2499364.html