首先,先贴一张全局手势的架构图,对整个手势结构有一个大概了解,后面再结合源码一步步分析。
架构图如下:
从整个架构图看到,主要是SystemUI和IMS(InputManagerService)打交道,因为全局手势功能是在SystemUI中的,所以我们上层就是SystemUI。
看完架构图,我们再从源码再进一步分析:
NavigationBarView是初始化手势的入口,所以先从NavigationBarView开始分析:
在底部导航View构造函数中,注入了EdgeBackGestureHandler,这个类是手势中核心管理类
NavigationBarView(){
...
mEdgeBackGestureHandler = Dependency.get(EdgeBackGestureHandler.Factory.class)
.create(mContext);
...
}
NavigationBarView第一次添加到window的时候会调用EdgeBackGestureHandler的onNavBarAttached做一些初始化工作
NOTE:目前v4车机版系统不带原生settings,无法切换导航模式,需在代码中默认开启手势导航
//NavigationBarView
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
...
mEdgeBackGestureHandler.onNavBarAttached();
...
}
//EdgeBackGestureHandler
public void onNavBarAttached() {
...
updateIsEnabled();
...
}
在EdgeBackGestureHandler的updateIsEnabled方法中,监听手势相关事件
//EdgeBackGestureHandler
private void updateIsEnabled() {
boolean isEnabled = mIsAttached && mIsGesturalModeEnabled;
if (!mIsEnabled) {
...
//注销手势排除区域监听
try {
mWindowManagerService.unregisterSystemGestureExclusionListener(
mGestureExclusionListener, mDisplayId);
}
} else {
// 监听手势排除区域
try {
mWindowManagerService.registerSystemGestureExclusionListener(
mGestureExclusionListener, mDisplayId);
}
//注册输入事件监听
mInputMonitor = InputManager.getInstance().monitorGestureInput(
"edge-swipe", mDisplayId);
mInputEventReceiver = new InputChannelCompat.InputEventReceiver(
mInputMonitor.getInputChannel(), Looper.getMainLooper(),
Choreographer.getInstance(), this::onInputEvent);
}
}
InputManager 经过 Binder 将 monitorGestureInput() 的调用传递到 InputManagerService,
IMS 通过jni 向NativeInputManager发出调用,并将其创建的 Client 端 InputChannel 实例转为 Java 实例返回。
//InputManager
public InputMonitor monitorGestureInput(String name, int displayId) {
try {
return mIm.monitorGestureInput(name, displayId);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
//InputManagerService
Override // Binder call
public InputMonitor monitorGestureInput(String inputChannelName, int displayId) {
...
try {
InputChannel inputChannel = nativeCreateInputMonitor(
mPtr, displayId, true /*isGestureMonitor*/, inputChannelName, pid);
InputMonitorHost host = new InputMonitorHost(inputChannel.getToken());
return new InputMonitor(inputChannel, host);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
//InputManagerService.cpp
static jobject nativeCreateInputMonitor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint displayId,
jboolean isGestureMonitor, jstring nameObj, jint pid) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
...
base::Result<std::unique_ptr<InputChannel>> inputChannel =
im->createInputMonitor(env, displayId, isGestureMonitor, name, pid);
...
jobject inputChannelObj =
android_view_InputChannel_createJavaObject(env, std::move(*inputChannel));
if (!inputChannelObj) {
return nullptr;
}
return inputChannelObj;
}
InputMonitor 创建完毕之后,创建InputChannelCompat.InputEventReceiver,构造函数中是创建了BatchedInputEventReceiver,BatchedInputEventReceiver类继承自系统InputEventReceiver类,覆写了onInputEvent(InputEvent event)方法监听回调
//InputChannelCompat
public InputEventReceiver(InputChannel inputChannel, Looper looper, Choreographer choreographer, final InputEventListener listener) {
this.mReceiver = new BatchedInputEventReceiver(inputChannel, looper, choreographer) {
public void onInputEvent(InputEvent event) {
listener.onInputEvent(event);
this.finishInputEvent(event, true);
}
};
}
在InputDispacher分发事件中,方法dispatchEventLocked就通过遍历InputTargets,拿到InputTarget.getInutChannel,然后通过InputChannel的fd在集合mConnectionsByFd中获取到Connection对象.并调用prepareDispatchCycleLocked进行处理。prepareDispatchCycleLocked方法内部调用了enqueueDispatchEntriesLocked方法
void InputDispatcher::enqueueDispatchEntriesLocked(connection,..){
// Enqueue dispatch entries for the requested modes.
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,...);//1
...
// If the outbound queue was previously empty, start the dispatch cycle going.
if (wasEmpty && !connection->outboundQueue.isEmpty()) {//2
startDispatchCycleLocked(currentTime, connection);//3
}
}
void InputDispatcher::enqueueDispatchEntryLocked(
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
int32_t dispatchMode) {
...
DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry,
inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
inputTarget->scaleFactor);
switch (eventEntry->type) {
case EventEntry::TYPE_KEY: {
KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
dispatchEntry->resolvedAction = keyEntry->action;
dispatchEntry->resolvedFlags = keyEntry->flags;
...
break;
}
...
}
...
connection->outboundQueue.enqueueAtTail(dispatchEntry);
...
}
在1处enqueueDispatchEntryLocked方法中会将输入事件重新封装为一个DispatchEntry并压入connection的outboundQueue队列中。
然后在2处判断如果事件不为空,则调用startDispatchCycleLocked循环发送输入事件。
//InputDispatcher.cpp
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection) {
while (connection->status == Connection::STATUS_NORMAL
&& !connection->outboundQueue.isEmpty()) {
DispatchEntry* dispatchEntry = connection->outboundQueue.head;
...
EventEntry* eventEntry = dispatchEntry->eventEntry;
switch (eventEntry->type) {
case EventEntry::TYPE_KEY: {
KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
// Publish the key event.
status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
keyEntry->deviceId, keyEntry->source,
dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
keyEntry->keyCode, keyEntry->scanCode,
keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
keyEntry->eventTime);
break;
}
...
connection->outboundQueue.dequeue(dispatchEntry);
connection->waitQueue.enqueueAtTail(dispatchEntry)
}
...
}
startDispatchCycleLocked方法中调用publishKeyEvent,监听 Socket FD 写入的消费端 Looper 将触发 LooperCallback,进而从 Client 端 Socket 读取事件,最后通过 InputEventReceiver 回调。
在发送完毕后会将事件移出connection->outboundQueue队列,并放入到waitQueue等待队列中,等待事件处理完毕后再移出。
创建NavigationBarEdgePanel手势视图实例,并缓存起来;
//EdgeBackGestureHandler
private void updateIsEnabled() {
...
// Add a nav bar panel window
setEdgeBackPlugin(new NavigationBarEdgePanel(mContext));
...
}
private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) {
if (mEdgeBackPlugin != null) {
mEdgeBackPlugin.onDestroy();
}
mEdgeBackPlugin = edgeBackPlugin;
mEdgeBackPlugin.setBackCallback(mBackCallback);
mEdgeBackPlugin.setLayoutParams(createLayoutParams());
updateDisplaySize();
}
NavigationBarEdgePanel构造函数中负责视图相关的初始化工作。
// NavigationBarEdgePanel.java
public NavigationBarEdgePanel(Context context) {
super(context);
mWindowManager = context.getSystemService(WindowManager.class);
mVibratorHelper = Dependency.get(VibratorHelper.class);
...
mPaint.setStrokeWidth(mArrowThickness);
mPaint.setStrokeCap(Paint.Cap.ROUND);
...
mArrowColorAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
mArrowColorAnimator.setDuration(COLOR_ANIMATION_DURATION_MS);
mArrowColorAnimator.addUpdateListener(animation -> {
int newColor = ColorUtils.blendARGB(
mArrowStartColor, mArrowColor, animation.getAnimatedFraction());
setCurrentArrowColor(newColor);
});
mArrowDisappearAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
mArrowDisappearAnimation.setDuration(DISAPPEAR_ARROW_ANIMATION_DURATION_MS);
mArrowDisappearAnimation.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
mArrowDisappearAnimation.addUpdateListener(animation -> {
mDisappearAmount = (float) animation.getAnimatedValue();
invalidate();
});
mAngleAnimation =
new SpringAnimation(this, CURRENT_ANGLE);
mAngleAppearForce = new SpringForce()
.setStiffness(500)
.setDampingRatio(0.5f);
...
mSwipeThreshold = context.getResources()
.getDimension(R.dimen.navigation_edge_action_drag_threshold);
setVisibility(GONE);
...
}
onInputEvent() 作为 SystemUI 监视到系统 Input 事件回调的入口,将进行整个返回手势的判断、视图处理。
private void onInputEvent(InputEvent ev) {
if (!(ev instanceof MotionEvent)) return;
...
onMotionEvent(event);
}
onMotionEvent() 将先进行手势有效性判断和停用区域检查,通过后交给返回手势视图即 EdgeBackPlugin 进一步处理。
// EdgeBackGestureHandler.java
private void onMotionEvent(MotionEvent ev) {
int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_DOWN) {
mInputEventReceiver.setBatchingEnabled(false);
mIsOnLeftEdge = ev.getX() <= mEdgeWidthLeft + mLeftInset;
mMLResults = 0;
mLogGesture = false;
mInRejectedExclusion = false;
boolean isWithinInsets = isWithinInsets((int) ev.getX(), (int) ev.getY());
// 根据返回手势是否有效、
// 点击区域是否是停用区域等条件
// 得到当前是否允许视图处理该手势
mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed && isWithinInsets
&& !mGestureBlockingActivityRunning
&& !QuickStepContract.isBackGestureDisabled(mSysUiFlags)
&& isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
if (mAllowGesture) {
// 更新当前是屏幕左侧还是右侧
mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge);
// 发射事件给视图
mEdgeBackPlugin.onMotionEvent(ev);
}
} else if (mAllowGesture || mLogGesture) {
if (!mThresholdCrossed) {
mEndPoint.x = (int) ev.getX();
mEndPoint.y = (int) ev.getY();
// 多个手指按下的话取消事件处理
if (action == MotionEvent.ACTION_POINTER_DOWN) {
if (mAllowGesture) {
logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_MULTI_TOUCH);
cancelGesture(ev);
}
mLogGesture = false;
return;
} else if (action == MotionEvent.ACTION_MOVE) {
// 手指移动超过长按阈值的话
// 也要取消事件处理
if ((ev.getEventTime() - ev.getDownTime()) > mLongPressTimeout) {
if (mAllowGesture) {
logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_LONG_PRESS);
cancelGesture(ev);
}
mLogGesture = false;
return;
}
...
}
}
// 通过上述检查的话
// 将 MOVE、UP 交给视图
if (mAllowGesture) {
mEdgeBackPlugin.onMotionEvent(ev);
}
}
mProtoTracer.scheduleFrameUpdate();
}
DOWN 的时候先让视图变为可见 VISIBLE
MOVE 的处理通过 handleMoveEvent() 判断距离,决定是否要更新赋予 mTriggerBack
UP 的时候将检查该变量决定是否触发返回动作即 triggerBack()
//NavigationBarEdgePanel
@Override
public void onMotionEvent(MotionEvent event) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mDragSlopPassed = false;
resetOnDown();
mStartX = event.getX();
mStartY = event.getY();
setVisibility(VISIBLE);
updatePosition(event.getY());
mRegionSamplingHelper.start(mSamplingRect);
mWindowManager.updateViewLayout(this, mLayoutParams);
break;
case MotionEvent.ACTION_MOVE:
handleMoveEvent(event);
break;
case MotionEvent.ACTION_UP:
if (mTriggerBack) {
triggerBack();
} else {
cancelBack();
}
...
}
}
隐藏动画执行,隐藏手势视图
//NavigationBarEdgePanel
private void triggerBack() {
mBackCallback.triggerBack();
// Finally, after the translation, animate back and disappear the arrow
Runnable translationEnd = () -> {
...
setDesiredTranslation(mDesiredTranslation - dp(32), true /* animated */);
animate().alpha(0f).setDuration(DISAPPEAR_FADE_ANIMATION_DURATION_MS)
.withEndAction(() -> setVisibility(GONE));
...
};
}
给IMS和launcher发送back事件消息
//EdgeBackGestureHandler
private final NavigationEdgeBackPlugin.BackCallback mBackCallback =
new NavigationEdgeBackPlugin.BackCallback() {
@Override
public void triggerBack() {
//给系统发送back键事件
boolean sendDown = sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
boolean sendUp = sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
//通知Launcher
mOverviewProxyService.notifyBackAction(true, (int) mDownPoint.x,
(int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);
...
};
sendEvent实际上就是通知到WMS返回事件,实际也是通过IMS再通知到WMS,这里就不详细说明了,有兴趣的可以阅读源码。
手势的时序图: