通过标题可以看到,本文是对全局手势识别进行分析,那什么是全局手势呢?简单来说就是在任何界面都需要识别的手势,比如:在任何界面从手机屏幕左侧滑动,当前的界面会退出(类似back键);
我们知道,在Android系统中一个Activity在显示时,当对屏幕触摸事件进行响应时,经过了许多逻辑处理,详细分析可以参考之前对IMS原理分析的一系列文章:
Android IMS原理解析
Android IMS原理解析之InputReader
Android IMS原理解析之InputDispatcher
Android IMS原理解析之InputChannel
Android IMS原理解析之processEvent
Android IMS原理解析之dispatchEvent
接下来对全局事件注册监听及处理进行分析:
一.注册Native监听
在WMS启动时就进行注册了,具体的逻辑是在构造方法内完成的,代码如下:
private final PointerEventDispatcher mPointerEventDispatcher
private WindowManagerService(Context context, InputManagerService inputManager,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
WindowManagerPolicy policy) {
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
if(mInputManager != null) {
final InputChannel inputChannel = mInputManager.monitorInput(TAG_WM);
mPointerEventDispatcher = inputChannel != null ? new PointerEventDispatcher(inputChannel) : null;
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
}
根据调用关系来看一下InputManagerService中monitorInput()方法:
public InputChannel monitorInput(String inputChannelName) {
InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
nativeRegisterInputChannel(mPtr, inputChannels[0], null, true);
inputChannels[0].dispose(); // don't need to retain the Java object reference
return inputChannels[1];
}
在该方法内部是通过InputChannel的openInputChannelPair()来创建一对InputChannel,将inputChannels[0]进行native注册,inputChannels[1]作为接收者,可以看到,此处跟普通的Window接收事件的处理是一致的,不同点在nativeRegisterInputChannel()传递的参数,主要有两处:
1.第三个参数InputWindowHandle传入的为null,因为是全局手势识别,所以不能指定特定的InputWindowHandle;
2.第四个参数monitor传入的为true,普通window传入的是false,通过名称可以看到,是监测,即接收所有事件;
接着上面的分析,在WindowManagerService的构造方法内部,通过mInputManager.monitorInput()的返回值InputChannel,创建了PointerEventDispatcher对象,一起看一下PointerEventDispatcher的实现:
public class PointerEventDispatcher extends InputEventReceiver {
ArrayList<PointerEventListener> mListeners = new ArrayList<PointerEventListener>();
PointerEventListener[] mListenersArray = new PointerEventListener[0];
public PointerEventDispatcher(InputChannel inputChannel) {
super(inputChannel, UiThread.getHandler().getLooper());
}
@Override
public void onInputEvent(InputEvent event, int displayId) {
try {
if (event instanceof MotionEvent
&& (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
final MotionEvent motionEvent = (MotionEvent) event;
PointerEventListener[] listeners;
synchronized (mListeners) {
if (mListenersArray == null) {
mListenersArray = new PointerEventListener[mListeners.size()];
mListeners.toArray(mListenersArray);
}
listeners = mListenersArray;
}
for (int i = 0; i < listeners.length; ++i) {
listeners[i].onPointerEvent(motionEvent, displayId);
}
}
} finally {
finishInputEvent(event, false);
}
}
public void registerInputEventListener(PointerEventListener listener) {
synchronized (mListeners) {
if (mListeners.contains(listener)) {
throw new IllegalStateException("registerInputEventListener: trying to register" +
listener + " twice.");
}
mListeners.add(listener);
mListenersArray = null;
}
}
public void unregisterInputEventListener(PointerEventListener listener) {
synchronized (mListeners) {
if (!mListeners.contains(listener)) {
throw new IllegalStateException("registerInputEventListener: " + listener +
" not registered.");
}
mListeners.remove(listener);
mListenersArray = null;
}
}
}
可以看到,PointerEventDispatcher继承了InputEventReceiver,即:触摸事件从native返回后会回调InputEventReceiver的dispatchInputEvent()方法,接着调用其继承类即PointerEventDispatcher的onInputEvent()方法,在该方法内遍历了所有的Listener并回调onPointerEvent()方法,通过registerInputEventListener()来注册监听;
二.注册事件回调
上面分析了在WMS构造方法内部建立了对native的监听,即当有触摸事件产生时,会回调到onInputEvent()方法,最终再回调所有PointerEventListener的onPointerEvent(),那么PointerEventListener就是全局手势的接收者,onPointerEvent()就是对全局手势的处理方法;
先看一下PointerEventListener,定义在WindowManagerPolicy.java里面:
public interface PointerEventListener {
void onPointerEvent(MotionEvent motionEvent);
default void onPointerEvent(MotionEvent motionEvent, int displayId) {
if (displayId == DEFAULT_DISPLAY) {
onPointerEvent(motionEvent);
}
}
}
PointerEventListener是一个接口,来看一下具体实现类,实现类为SystemGesturesPointerEventListener:
public class SystemGesturesPointerEventListener implements PointerEventListener {
;;;;;;;;;;;;;;;;;;;;;;;;;
public SystemGesturesPointerEventListener(Context context, Callbacks callbacks) {
mContext = context;
mCallbacks = checkNull("callbacks", callbacks);
mSwipeStartThreshold = checkNull("context", context).getResources()
.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
mSwipeDistanceThreshold = mSwipeStartThreshold;
if (DEBUG) Slog.d(TAG, "mSwipeStartThreshold=" + mSwipeStartThreshold
+ " mSwipeDistanceThreshold=" + mSwipeDistanceThreshold);
}
private static <T> T checkNull(String name, T arg) {
if (arg == null) {
throw new IllegalArgumentException(name + " must not be null");
}
return arg;
}
public void systemReady() {
Handler h = new Handler(Looper.myLooper());
mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), h);
mOverscroller = new OverScroller(mContext);
}
@Override
public void onPointerEvent(MotionEvent event) {
if (mGestureDetector != null && event.isTouchEvent()) {
mGestureDetector.onTouchEvent(event);
}
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
;;;;;;;;;;;;;;;;;;;;;
mCallbacks.onDown();
break;
case MotionEvent.ACTION_POINTER_DOWN:
;;;;;;;;;;;;;;;;;;;;;;
break;
case MotionEvent.ACTION_MOVE:
if (mSwipeFireable) {
final int swipe = detectSwipe(event);
mSwipeFireable = swipe == SWIPE_NONE;
if (swipe == SWIPE_FROM_TOP) {
mCallbacks.onSwipeFromTop();
} else if (swipe == SWIPE_FROM_BOTTOM) {
mCallbacks.onSwipeFromBottom();
} else if (swipe == SWIPE_FROM_RIGHT) {
mCallbacks.onSwipeFromRight();
} else if (swipe == SWIPE_FROM_LEFT) {
mCallbacks.onSwipeFromLeft();
}
}
break;
case MotionEvent.ACTION_HOVER_MOVE:
if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
if (!mMouseHoveringAtEdge && event.getY() == 0) {
mCallbacks.onMouseHoverAtTop();
mMouseHoveringAtEdge = true;
} else if (!mMouseHoveringAtEdge && event.getY() >= screenHeight - 1) {
mCallbacks.onMouseHoverAtBottom();
mMouseHoveringAtEdge = true;
} else if (mMouseHoveringAtEdge
&& (event.getY() > 0 && event.getY() < screenHeight - 1)) {
mCallbacks.onMouseLeaveFromEdge();
mMouseHoveringAtEdge = false;
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mSwipeFireable = false;
mDebugFireable = false;
if (mScrollFired)
mCallbacks.onScroll(false);
mScrollFired = false;
mCallbacks.onUpOrCancel();
break;
default:
if (DEBUG) Slog.d(TAG, "Ignoring " + event);
}
}
private void captureDown(MotionEvent event, int pointerIndex) {
final int pointerId = event.getPointerId(pointerIndex);
final int i = findIndex(pointerId);
if (i != UNTRACKED_POINTER) {
mDownX[i] = event.getX(pointerIndex);
mDownY[i] = event.getY(pointerIndex);
mDownTime[i] = event.getEventTime();
if (DEBUG) Slog.d(TAG, "pointer " + pointerId +
" down x=" + mDownX[i] + " y=" + mDownY[i]);
}
}
private int findIndex(int pointerId) {
for (int i = 0; i < mDownPointers; i++) {
if (mDownPointerId[i] == pointerId) {
return i;
}
}
if (mDownPointers == MAX_TRACKED_POINTERS || pointerId == MotionEvent.INVALID_POINTER_ID) {
return UNTRACKED_POINTER;
}
mDownPointerId[mDownPointers++] = pointerId;
return mDownPointers - 1;
}
private int detectSwipe(MotionEvent move) {
final int historySize = move.getHistorySize();
final int pointerCount = move.getPointerCount();
for (int p = 0; p < pointerCount; p++) {
final int pointerId = move.getPointerId(p);
final int i = findIndex(pointerId);
if (i != UNTRACKED_POINTER) {
for (int h = 0; h < historySize; h++) {
final long time = move.getHistoricalEventTime(h);
final float x = move.getHistoricalX(p, h);
final float y = move.getHistoricalY(p, h);
final int swipe = detectSwipe(i, time, x, y);
if (swipe != SWIPE_NONE) {
return swipe;
}
}
final int swipe = detectSwipe(i, move.getEventTime(), move.getX(p), move.getY(p));
if (swipe != SWIPE_NONE) {
return swipe;
}
}
}
return SWIPE_NONE;
}
private int detectSwipe(int i, long time, float x, float y) {
final float fromX = mDownX[i];
final float fromY = mDownY[i];
final long elapsed = time - mDownTime[i];
if (DEBUG) Slog.d(TAG, "pointer " + mDownPointerId[i]
+ " moved (" + fromX + "->" + x + "," + fromY + "->" + y + ") in " + elapsed);
if (fromY <= mSwipeStartThreshold
&& y > fromY + mSwipeDistanceThreshold
&& elapsed < SWIPE_TIMEOUT_MS) {
return SWIPE_FROM_TOP;
}
if (fromY >= screenHeight - mSwipeStartThreshold
&& y < fromY - mSwipeDistanceThreshold
&& elapsed < SWIPE_TIMEOUT_MS) {
return SWIPE_FROM_BOTTOM;
}
if (fromX >= screenWidth - mSwipeStartThreshold
&& x < fromX - mSwipeDistanceThreshold
&& elapsed < SWIPE_TIMEOUT_MS) {
return SWIPE_FROM_RIGHT;
}
if (fromX <= mSwipeStartThreshold
&& x > fromX + mSwipeDistanceThreshold
&& elapsed < SWIPE_TIMEOUT_MS) {
return SWIPE_FROM_LEFT;
}
return SWIPE_NONE;
}
private final class FlingGestureDetector extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent e) {
if (!mOverscroller.isFinished()) {
mOverscroller.forceFinished(true);
}
return true;
}
@Override
public boolean onFling(MotionEvent down, MotionEvent up,
float velocityX, float velocityY) {
mOverscroller.computeScrollOffset();
long now = SystemClock.uptimeMillis();
if (mLastFlingTime != 0 && now > mLastFlingTime + MAX_FLING_TIME_MILLIS) {
mOverscroller.forceFinished(true);
}
mOverscroller.fling(0, 0, (int)velocityX, (int)velocityY,
Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
int duration = mOverscroller.getDuration();
if (duration > MAX_FLING_TIME_MILLIS) {
duration = MAX_FLING_TIME_MILLIS;
}
mLastFlingTime = now;
mCallbacks.onFling(duration);
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
if (!mScrollFired) {
mCallbacks.onScroll(true);
mScrollFired = true;
}
return true;
}
}
interface Callbacks {
void onSwipeFromTop();
void onSwipeFromBottom();
void onSwipeFromRight();
void onSwipeFromLeft();
void onFling(int durationMs);
void onScroll(boolean started);
void onDown();
void onUpOrCancel();
void onMouseHoverAtTop();
void onMouseHoverAtBottom();
void onMouseLeaveFromEdge();
void onDebug();
}
}
通过代码逻辑可以看到,里面实现了左滑、右滑、上滑、下滑等等全局事件,当检测到对应的事件后,会执行回调方法,具体实现及处理是在PhoneWindowManager里面:
WindowManagerFuncs mWindowManagerFuncs;
private SystemGesturesPointerEventListener mSystemGestures;
public void init(Context context, IWindowManager windowManager,
WindowManagerFuncs windowManagerFuncs) {
mWindowManagerFuncs = windowManagerFuncs;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
// monitor for system gestures
mSystemGestures = new SystemGesturesPointerEventListener(context,
new SystemGesturesPointerEventListener.Callbacks() {
@Override
public void onSwipeFromTop() {
}
@Override
public void onSwipeFromBottom() {
}
@Override
public void onSwipeFromRight() {
}
@Override
public void onSwipeFromLeft() {
}
;;;;;;;;;;;;;;;;;;;
});
//通过mWindowManagerFuncs进行注册
mWindowManagerFuncs.registerPointerEventListener(mSystemGestures);
}
可以看到,在PhoneWindowManager的init()方法内部,会创建SystemGesturesPointerEventListener,并通过WindowManagerFuncs的registerPointerEventListener()进行注册,接下来看一下init()方法的调用入口和WindowManagerFuncs的实现,通过源码发现,这两者都跟WindowManagerService有关系,WindowManagerService是在SystemServer里面的startOtherServices()里面启动的:
//SystemServer.java
private void startOtherServices() {
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore, new PhoneWindowManager());
}
//WindowManagerService.java
public static WindowManagerService main(final Context context, final InputManagerService im,
final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
WindowManagerPolicy policy) {
sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
onlyCore, policy), 0);
return sInstance;
}
private WindowManagerService(Context context, InputManagerService inputManager,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
WindowManagerPolicy policy) {
mPolicy = policy;
;;;;;;;;;;;;;;;;;;;;
initPolicy();
}
private void initPolicy() {
mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);
}
WindowManagerPolicy是一个接口,PhoneWindowManager是WindowManagerPolicy的实现者,所以PhoneWindowManager的init()方法是在WindowManagerService里面调用的,通过传入的参数可以看到,第二和第三参数传的都是WindowManagerService本身,说明WindowManagerService实现了WindowManagerFuncs,一起看一下:
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@Override
public void registerPointerEventListener(PointerEventListener listener) {
mPointerEventDispatcher.registerInputEventListener(listener);
}
@Override
public void unregisterPointerEventListener(PointerEventListener listener) {
mPointerEventDispatcher.unregisterInputEventListener(listener);
}
}
}
当在PhoneWindowManager的init()方法内部通过WindowManagerFuncs的registerPointerEventListener()进行注册时,会调用到WMS内部的实现方法,然后调用mPointerEventDispatcher的对应方法;
根据第一章分析,mPointerEventDispatcher是在WMS的构造方法内部创建的,此时就建立了监听,当native层的手势事件传上来时,会通过mPointerEventDispatcher来回调所有的PointerEventListener实现者;
三.Native层处理
关于触摸事件从接收、传递、处理在前面的几篇文章里面已经详细分析了,全局手势事件也是大致相同的处理流程,本章仅将涉及全局手势事件处理相关的点来进行分析;
先从注册讲起,在第一章里面讲到,在执行nativeRegisterInputChannel()时,传入的最后参数Monitor是为true,看一下对应的实现路径:framework/services/core/jni/com_android_server_input_InputManagerService.cpp
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
sp<InputWindowHandle> inputWindowHandle =
android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);
status_t status = im->registerInputChannel(
env, inputChannel, inputWindowHandle, monitor);
}
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
return mInputManager->getDispatcher()->registerInputChannel(
inputChannel, inputWindowHandle, monitor);
}
根据调用关系,接着看一下InputDispatcher.cpp,对应的实现路径为:frameworks/native/services/inputflinger/InputDispatcher.cpp
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
{ // acquire lock
AutoMutex _l(mLock);
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;
}
可以看到,在registerInputChannel()里面会进行判断,如果monitor为true,会向mMonitoringChannels队列里面push inputChannel,普通的窗口传入的monitor都是false,该mMonitoringChannels有什么作用呢?直接看代码:
void InputDispatcher::addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets) {
for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
inputTargets.push();
InputTarget& target = inputTargets.editTop();
target.inputChannel = mMonitoringChannels[i];
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
target.xOffset = 0;
target.yOffset = 0;
target.pointerIds.clear();
target.scaleFactor = 1.0f;
}
}
在addMonitoringTargetsLocked()里面会遍历mMonitoringChannels来添加到inputTargets队列里面,看一下addMonitoringTargetsLocked()的调用处理,以dispatchMotionLocked()为例:
bool InputDispatcher::dispatchMotionLocked(
nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
;;;;;;;;;;;;;;;;;;;;;;;;;;
bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
// Identify targets.
Vector<InputTarget> inputTargets;
bool conflictingPointerActions = false;
int32_t injectionResult;
if (isPointerEvent) {
// Pointer event. (eg. touchscreen)
injectionResult = findTouchedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
} else {
// Non touch event. (eg. trackball)
injectionResult = findFocusedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime);
}
if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
return false;
}
setInjectionResultLocked(entry, injectionResult);
;;;;;;;;;;;;;;;;;;;;;;;;;
addMonitoringTargetsLocked(inputTargets);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}
通过前面的文章分析,InputDispatcher负责事件派发,在dispatchMotionLocked()里面来确定需要发送的inputTargets,在该方法内部执行了addMonitoringTargetsLocked(inputTargets),即在发现需要派发的inputTarget后,又添加了monitor inputTarget;
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
pokeUserActivityLocked(eventEntry);
for (size_t i = 0; i < inputTargets.size(); i++) {
const InputTarget& inputTarget = inputTargets.itemAt(i);
ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
} else {
}
}
}
在dispatchEventLocked()内部会遍历inputTargets进行发送,也就是说,当我们在触摸屏幕后事件在派发时,不仅会派发到当前focus窗口,也会发送到全局手势监听者,比如:屏幕左侧滑动退出应用,如果以模拟发送back键来实现时,在退出应用前,会看到应用内部也会响应左滑事件;
四.总结
全局手势处理跟普通窗口处理流程大致是相同的,全局手势在任何界面下都会接收到,可以根据需求本地实现PointerEventListener来实现不同的全局手势,以上层实现流程图来结束本文章: