连续的输入事件可能会产生必定的手势操做。好比滑动手势和捏合手势。html
在Chromium中,网页的输入事件是在Browser进程中捕捉的。Browser进程捕获输入事件以后,会进行手势操做检測。检測出来的手势操做将会发送给Render进程处理,因为它们需要应用在网页之上。与此同一时候。Browser进程也会将原始的输入事件发送给Render进程处理。本文接下来就分析Browser进程处理网页输入事件的过程。java
老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!
android
《Android系统源码情景分析》一书正在进击的程序猿网(http://0xcc0xcd.com)中连载,点击进入。
web
接下来咱们将以Chromium自带的Content Shell APK为例,说明Chromium的Browser进程捕获网页输入事件以及检測手势操做的过程,如图1所看到的:app
图1 Browser进程处理网页输入事件的过程ide
从前面Chromium网页输入事件处理机制简要介绍和学习计划一文可以知道,Content Shell APK将网页渲染在一个SurfaceView控件上。函数
这个SurfaceView又是嵌入在一个ContentView控件里面的。学习
当用户在网页上触发了一个输入事件时,好比触发一个Touch事件时,这个Touch事件就会被系统分发给上述ContentView控件处理。表现为该ContentView控件的成员函数onTouchEvent被调用。ui
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进程处理。this
注意。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 ?这个函数定义在文件external/chromium_org/ui/events/gesture_detection/gesture_detector.cc中。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; }
前面咱们假设參数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_BEGIN、ET_GESTURE_SCROLL_UPDATE、ET_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进程发送一个ACK。Browser进程接收到这个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。