Android 全局手势识别原理

本文是对全局手势识别进行分析,那什么是全局手势呢?简单来说就是在任何界面都需要识别的手势,比如:在任何界面从手机屏幕左侧滑动,当前的界面会退出 (类似 back 键)。

我们知道,在 Android 系统中一个 Activity 在显示时,当对屏幕触摸事件进行响应时,经过了许多逻辑处理,详细分析可以参考之前对 IMS 原理分析的一系列文章: Android知识体系导图 中的输入系统章节。

接下来对全局手势事件注册监听及处理进行分析。目前使用 12.0 代码分析

一 启动和注册 Native 监听

RootWindowContainer 的构造是在 WindowManagerService 中:

WindowManagerService.java 

    private WindowManagerService(Context context, InputManagerService inputManager,
            boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
            ActivityTaskManagerService atm, DisplayWindowSettingsProvider
            displayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory,
            Supplier<Surface> surfaceFactory,
            Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
            ……
            mRoot = new RootWindowContainer(this);
			……
    }

可见 RootWindowContainer 对象作为 WindowManagerService 的成员变量 mRoot 存在。在 SystemServer 类的 startOtherServices 中,会调用 ActivityManagerService 的 setWindowManager() 方法:

SystemServer.java

mActivityManagerService.setWindowManager(wm);
ActivityManagerService.java

public void setWindowManager(WindowManagerService wm) {
1846          synchronized (this) {
1847              mWindowManager = wm;
1848              mWmInternal = LocalServices.getService(WindowManagerInternal.class);
1849              mActivityTaskManager.setWindowManager(wm);
1850          }
1851      }

进而调用 ActivityTaskManagerService 的 setWindowManager

ActivityTaskManagerService.java

    public void setWindowManager(WindowManagerService wm) {
974          synchronized (mGlobalLock) {
975              mWindowManager = wm;
976              mRootWindowContainer = wm.mRoot;
977              mTempConfig.setToDefaults();
978              mTempConfig.setLocales(LocaleList.getDefault());
979              mConfigurationSeq = mTempConfig.seq = 1;
980              mRootWindowContainer.onConfigurationChanged(mTempConfig);
981              mLockTaskController.setWindowManager(wm);
982              mTaskSupervisor.setWindowManager(wm);
983              mRootWindowContainer.setWindowManager(wm);
984          }
985      }

ActivityTaskManagerService 的成员变量 mRootWindowContainer 也赋值为 RootWindowContainer 根对象。最后会调用RootWindowContainer的setWindowManager(wm)方法:

RootWindowContainer.java 

    void setWindowManager(WindowManagerService wm) {
1353          mWindowManager = wm;
1354          mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
1355          mDisplayManager.registerDisplayListener(this, mService.mUiHandler);
1356          mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
1357  
1358          final Display[] displays = mDisplayManager.getDisplays();
1359          for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
1360              final Display display = displays[displayNdx];
1361              final DisplayContent displayContent = new DisplayContent(display, this);
1362              addChild(displayContent, POSITION_BOTTOM);
1363              if (displayContent.mDisplayId == DEFAULT_DISPLAY) {
1364                  mDefaultDisplay = displayContent;
1365              }
1366          }
1367          calculateDefaultMinimalSizeOfResizeableTasks();
1368  
1369          final TaskDisplayArea defaultTaskDisplayArea = getDefaultTaskDisplayArea();
1370          defaultTaskDisplayArea.getOrCreateRootHomeTask(ON_TOP);
1371          positionChildAt(POSITION_TOP, defaultTaskDisplayArea.mDisplayContent,
1372                  false /* includingParents */);
1373      }

从上面看出,通过屏幕管理对象 mDisplayManager 得到所有的显示屏幕,然后构造 DisplayContent 对象,再通过 addChild(displayContent, POSITION_BOTTOM) 方法将 DisplayContent 对象添加到 RootWindowContainer 根对象的树状结构中。

默认显示屏幕的 mDisplayId 是 DEFAULT_DISPLAY,看下它的值(Display类 public static final int DEFAULT_DISPLAY = 0),mDisplayId 为 0 作为基本显示屏幕。我们接下来看 DisplayContent 的构造函数。
  
在 DisplayContent.java 的构造函数中进行注册,代码如下:

DisplayContent.java 

    private final PointerEventDispatcher mPointerEventDispatcher;

    DisplayContent(Display display, RootWindowContainer root) { // 构造函数中初始化     
            ......
            final InputChannel inputChannel = mWmService.mInputManager.monitorInput(
1028                  "PointerEventDispatcher" + mDisplayId, mDisplayId);
1029          mPointerEventDispatcher = new PointerEventDispatcher(inputChannel, this);
            ......
    }
}

我们可以看到在 DisplayContent 的构造函数中,生成了 InputChannel,并作为参数传入 PointerEventDispatcher 中。我们知道这个 InputChannel 就是一对负责通信的 socket 通道。至于构建过程我们后面分析,我们只要知道它是 socket 通信通道的建立即可。

接着上面的分析,在 DisplayContent 的构造方法内部,通过 mWmService.mInputManager.monitorInput() 的返回值 InputChannel,创建了PointerEventDispatcher对象,一起看一下PointerEventDispatcher的实现:

public class PointerEventDispatcher extends InputEventReceiver {
35      private final ArrayList<PointerEventListener> mListeners = new ArrayList<>();
36      private PointerEventListener[] mListenersArray = new PointerEventListener[0];
37  
38      private final DisplayContent mDisplayContent;
39      private final Point mTmpSize = new Point();
40  
41      public PointerEventDispatcher(InputChannel inputChannel, DisplayContent dc) {
42          super(inputChannel, UiThread.getHandler().getLooper());
43          mDisplayContent = dc;
44      }
45  
46      @Override
47      public void onInputEvent(InputEvent event) {
48          try {
49              if (event instanceof MotionEvent
50                      && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
51                  MotionEvent motionEvent = (MotionEvent) event;
52                  if (ENABLE_PER_WINDOW_INPUT_ROTATION) {
53                      final int rotation = mDisplayContent.getRotation();
54                      if (rotation != Surface.ROTATION_0) {
55                          mDisplayContent.getDisplay().getRealSize(mTmpSize);
56                          motionEvent = MotionEvent.obtain(motionEvent);
57                          motionEvent.transform(MotionEvent.createRotateMatrix(
58                                  rotation, mTmpSize.x, mTmpSize.y));
59                      }
60                  }
61                  PointerEventListener[] listeners;
62                  synchronized (mListeners) {
63                      if (mListenersArray == null) {
64                          mListenersArray = new PointerEventListener[mListeners.size()];
65                          mListeners.toArray(mListenersArray);
66                      }
67                      listeners = mListenersArray;
68                  }
69                  for (int i = 0; i < listeners.length; ++i) {
70                      listeners[i].onPointerEvent(motionEvent);
71                  }
72              }
73          } finally {
74              finishInputEvent(event, false);
75          }
76      }
77  
78     
82      public void registerInputEventListener(PointerEventListener listener) {
83          synchronized (mListeners) {
84              if (mListeners.contains(listener)) {
85                  throw new IllegalStateException("registerInputEventListener: trying to register" +
86                          listener + " twice.");
87              }
88              mListeners.add(listener);
89              mListenersArray = null;
90          }
91      }
92  
93      
97      public void unregisterInputEventListener(PointerEventListener listener) {
98          synchronized (mListeners) {
99              if (!mListeners.contains(listener)) {
100                  throw new IllegalStateException("registerInputEventListener: " + listener +
101                          " not registered.");
102              }
103              mListeners.remove(listener);
104              mListenersArray = null;
105          }
106      }
107  
108      ......
117  }

可以看到,PointerEventDispatcher 继承了 InputEventReceiver,即:触摸事件从 native 返回后会回调 InputEventReceiver 的dispatchInputEvent() 方法,接着调用其继承类即 PointerEventDispatcher 的 onInputEvent() 方法,在该方法内遍历了所有的 Listener 并回调 onPointerEvent() 方法,通过 registerInputEventListener() 来注册监听。

二 注册事件回调

上面分析了在 DisplayContent 构造方法内部建立了对 native 的监听,即当有触摸事件产生时,会回调到 onInputEvent() 方法,最终再回调所有 PointerEventListener 的 onPointerEvent(),那么 PointerEventListener 就是全局手势的接收者,onPointerEvent() 就是对全局手势的处理方法。

先看一下 PointerEventListener,定义在 WindowManagerPolicyConstants.java 里面:

WindowManagerPolicyConstants.java

interface PointerEventListener {
108          /**
109           * 1. onPointerEvent will be called on the service.UiThread.
110           * 2. motionEvent will be recycled after onPointerEvent returns so if it is needed later a
111           * copy() must be made and the copy must be recycled.
112           **/
113          void onPointerEvent(MotionEvent motionEvent);
114      }

PointerEventListener 是一个接口,来看一下具体实现类,实现类为 SystemGesturesPointerEventListener:

SystemGesturesPointerEventListener.java


class SystemGesturesPointerEventListener implements PointerEventListener {
41      private static final String TAG = "SystemGestures";
42      private static final boolean DEBUG = false;
43      private static final long SWIPE_TIMEOUT_MS = 500;
44      private static final int MAX_TRACKED_POINTERS = 32;  // max per input system
45      private static final int UNTRACKED_POINTER = -1;
46      private static final int MAX_FLING_TIME_MILLIS = 5000;
47  
48      private static final int SWIPE_NONE = 0;
49      private static final int SWIPE_FROM_TOP = 1;
50      private static final int SWIPE_FROM_BOTTOM = 2;
51      private static final int SWIPE_FROM_RIGHT = 3;
52      private static final int SWIPE_FROM_LEFT = 4;
53  
54      ......
64  
65      private GestureDetector mGestureDetector;
66  
67      ......
75  
76      SystemGesturesPointerEventListener(Context context, Handler handler, Callbacks callbacks) {
77          mContext = checkNull("context", context);
78          mHandler = handler;
79          mCallbacks = checkNull("callbacks", callbacks);
80          onConfigurationChanged();
81      }
82  
83      ......
120  
121      public void systemReady() {
131          mHandler.post(() -> {
132              final int displayId = mContext.getDisplayId();
133              final DisplayInfo info = DisplayManagerGlobal.getInstance().getDisplayInfo(displayId);
134              if (info == null) {
135                  // Display already removed, stop here.
136                  Slog.w(TAG, "Cannot create GestureDetector, display removed:" + displayId);
137                  return;
138              }
139              mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), mHandler) {
140              };
141          });
142      }
143  
144      @Override
145      public void onPointerEvent(MotionEvent event) {
146          if (mGestureDetector != null && event.isTouchEvent()) {
147              mGestureDetector.onTouchEvent(event);
148          }
149          switch (event.getActionMasked()) {
150              case MotionEvent.ACTION_DOWN:
151                  mSwipeFireable = true;
152                  mDebugFireable = true;
153                  mDownPointers = 0;
154                  captureDown(event, 0);
155                  if (mMouseHoveringAtEdge) {
156                      mMouseHoveringAtEdge = false;
157                      mCallbacks.onMouseLeaveFromEdge();
158                  }
159                  mCallbacks.onDown();
160                  break;
161              case MotionEvent.ACTION_POINTER_DOWN:
162                  captureDown(event, event.getActionIndex());
163                  if (mDebugFireable) {
164                      mDebugFireable = event.getPointerCount() < 5;
165                      if (!mDebugFireable) {
166                          if (DEBUG) Slog.d(TAG, "Firing debug");
167                          mCallbacks.onDebug();
168                      }
169                  }
170                  break;
171              case MotionEvent.ACTION_MOVE:
172                  if (mSwipeFireable) {
173                      final int swipe = detectSwipe(event);
174                      mSwipeFireable = swipe == SWIPE_NONE;
175                      if (swipe == SWIPE_FROM_TOP) {
176                          if (DEBUG) Slog.d(TAG, "Firing onSwipeFromTop");
177                          mCallbacks.onSwipeFromTop();
178                      } else if (swipe == SWIPE_FROM_BOTTOM) {
179                          if (DEBUG) Slog.d(TAG, "Firing onSwipeFromBottom");
180                          mCallbacks.onSwipeFromBottom();
181                      } else if (swipe == SWIPE_FROM_RIGHT) {
182                          if (DEBUG) Slog.d(TAG, "Firing onSwipeFromRight");
183                          mCallbacks.onSwipeFromRight();
184                      } else if (swipe == SWIPE_FROM_LEFT) {
185                          if (DEBUG) Slog.d(TAG, "Firing onSwipeFromLeft");
186                          mCallbacks.onSwipeFromLeft();
187                      }
188                  }
189                  break;
190              case MotionEvent.ACTION_HOVER_MOVE:
191                  if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
192                      if (!mMouseHoveringAtEdge && event.getY() == 0) {
193                          mCallbacks.onMouseHoverAtTop();
194                          mMouseHoveringAtEdge = true;
195                      } else if (!mMouseHoveringAtEdge && event.getY() >= screenHeight - 1) {
196                          mCallbacks.onMouseHoverAtBottom();
197                          mMouseHoveringAtEdge = true;
198                      } else if (mMouseHoveringAtEdge
199                              && (event.getY() > 0 && event.getY() < screenHeight - 1)) {
200                          mCallbacks.onMouseLeaveFromEdge();
201                          mMouseHoveringAtEdge = false;
202                      }
203                  }
204                  break;
205              case MotionEvent.ACTION_UP:
206              case MotionEvent.ACTION_CANCEL:
207                  mSwipeFireable = false;
208                  mDebugFireable = false;
209                  mCallbacks.onUpOrCancel();
210                  break;
211              default:
212                  if (DEBUG) Slog.d(TAG, "Ignoring " + event);
213          }
214      }
215  
216      private void captureDown(MotionEvent event, int pointerIndex) {
217          final int pointerId = event.getPointerId(pointerIndex);
218          final int i = findIndex(pointerId);
219          if (DEBUG) Slog.d(TAG, "pointer " + pointerId
220                  + " down pointerIndex=" + pointerIndex + " trackingIndex=" + i);
221          if (i != UNTRACKED_POINTER) {
222              mDownX[i] = event.getX(pointerIndex);
223              mDownY[i] = event.getY(pointerIndex);
224              mDownTime[i] = event.getEventTime();
225              if (DEBUG) Slog.d(TAG, "pointer " + pointerId
226                      + " down x=" + mDownX[i] + " y=" + mDownY[i]);
227          }
228      }
229  
230      protected boolean currentGestureStartedInRegion(Region r) {
231          return r.contains((int) mDownX[0], (int) mDownY[0]);
232      }
233  
234      private int findIndex(int pointerId) {
235          for (int i = 0; i < mDownPointers; i++) {
236              if (mDownPointerId[i] == pointerId) {
237                  return i;
238              }
239          }
240          if (mDownPointers == MAX_TRACKED_POINTERS || pointerId == MotionEvent.INVALID_POINTER_ID) {
241              return UNTRACKED_POINTER;
242          }
243          mDownPointerId[mDownPointers++] = pointerId;
244          return mDownPointers - 1;
245      }
246  
247      private int detectSwipe(MotionEvent move) {
248          final int historySize = move.getHistorySize();
249          final int pointerCount = move.getPointerCount();
250          for (int p = 0; p < pointerCount; p++) {
251              final int pointerId = move.getPointerId(p);
252              final int i = findIndex(pointerId);
253              if (i != UNTRACKED_POINTER) {
254                  for (int h = 0; h < historySize; h++) {
255                      final long time = move.getHistoricalEventTime(h);
256                      final float x = move.getHistoricalX(p, h);
257                      final float y = move.getHistoricalY(p,  h);
258                      final int swipe = detectSwipe(i, time, x, y);
259                      if (swipe != SWIPE_NONE) {
260                          return swipe;
261                      }
262                  }
263                  final int swipe = detectSwipe(i, move.getEventTime(), move.getX(p), move.getY(p));
264                  if (swipe != SWIPE_NONE) {
265                      return swipe;
266                  }
267              }
268          }
269          return SWIPE_NONE;
270      }
271  
272      private int detectSwipe(int i, long time, float x, float y) {
273          final float fromX = mDownX[i];
274          final float fromY = mDownY[i];
275          final long elapsed = time - mDownTime[i];
276          if (DEBUG) Slog.d(TAG, "pointer " + mDownPointerId[i]
277                  + " moved (" + fromX + "->" + x + "," + fromY + "->" + y + ") in " + elapsed);
278          if (fromY <= mSwipeStartThreshold
279                  && y > fromY + mSwipeDistanceThreshold
280                  && elapsed < SWIPE_TIMEOUT_MS) {
281              return SWIPE_FROM_TOP;
282          }
283          if (fromY >= screenHeight - mSwipeStartThreshold
284                  && y < fromY - mSwipeDistanceThreshold
285                  && elapsed < SWIPE_TIMEOUT_MS) {
286              return SWIPE_FROM_BOTTOM;
287          }
288          if (fromX >= screenWidth - mSwipeStartThreshold
289                  && x < fromX - mSwipeDistanceThreshold
290                  && elapsed < SWIPE_TIMEOUT_MS) {
291              return SWIPE_FROM_RIGHT;
292          }
293          if (fromX <= mSwipeStartThreshold
294                  && x > fromX + mSwipeDistanceThreshold
295                  && elapsed < SWIPE_TIMEOUT_MS) {
296              return SWIPE_FROM_LEFT;
297          }
298          return SWIPE_NONE;
299      }
300  
301      private final class FlingGestureDetector extends GestureDetector.SimpleOnGestureListener {
302  
303          private OverScroller mOverscroller;
304  
305          FlingGestureDetector() {
306              mOverscroller = new OverScroller(mContext);
307          }
308  
309          @Override
310          public boolean onSingleTapUp(MotionEvent e) {
311              if (!mOverscroller.isFinished()) {
312                  mOverscroller.forceFinished(true);
313              }
314              return true;
315          }
316          @Override
317          public boolean onFling(MotionEvent down, MotionEvent up,
318                  float velocityX, float velocityY) {
319              mOverscroller.computeScrollOffset();
320              long now = SystemClock.uptimeMillis();
321  
322              if (mLastFlingTime != 0 && now > mLastFlingTime + MAX_FLING_TIME_MILLIS) {
323                  mOverscroller.forceFinished(true);
324              }
325              mOverscroller.fling(0, 0, (int)velocityX, (int)velocityY,
326                      Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
327              int duration = mOverscroller.getDuration();
328              if (duration > MAX_FLING_TIME_MILLIS) {
329                  duration = MAX_FLING_TIME_MILLIS;
330              }
331              mLastFlingTime = now;
332              mCallbacks.onFling(duration);
333              return true;
334          }
335      }
336  
337      interface Callbacks {
338          void onSwipeFromTop();
339          void onSwipeFromBottom();
340          void onSwipeFromRight();
341          void onSwipeFromLeft();
342          void onFling(int durationMs);
343          void onDown();
344          void onUpOrCancel();
345          void onMouseHoverAtTop();
346          void onMouseHoverAtBottom();
347          void onMouseLeaveFromEdge();
348          void onDebug();
349      }
350  }
351  

通过代码逻辑可以看到,里面实现了左滑、右滑、上滑、下滑等等全局事件,当检测到对应的事件后,会执行回调方法,具体实现及处理是在 DisplayPolicy.java 里面,之前的版本在 PhoneWindowManager 里面:

DisplayPolicy.java

public class DisplayPolicy {
    private final SystemGesturesPointerEventListener mSystemGestures;
    ......
     DisplayPolicy(WindowManagerService service, DisplayContent displayContent) {
     mSystemGestures = new SystemGesturesPointerEventListener(mUiContext, mHandler,
452                  new SystemGesturesPointerEventListener.Callbacks() {
453                      @Override
454                      public void onSwipeFromTop() {
455                          synchronized (mLock) {
456                              if (mStatusBar != null) {
457                                  requestTransientBars(mStatusBar);
458                              }
459                              checkAltBarSwipeForTransientBars(ALT_BAR_TOP);
460                          }
461                      }
462  
463                      @Override
464                      public void onSwipeFromBottom() {
465                          synchronized (mLock) {
466                              if (mNavigationBar != null
467                                      && mNavigationBarPosition == NAV_BAR_BOTTOM) {
468                                  requestTransientBars(mNavigationBar);
469                              }
470                              checkAltBarSwipeForTransientBars(ALT_BAR_BOTTOM);
471                          }
472                      }
473  
474                      @Override
475                      public void onSwipeFromRight() {
476                          ......
489                      }
490  
491                      @Override
492                      public void onSwipeFromLeft() {
493                          ......
506                      }
507  
508                      @Override
509                      public void onFling(int duration) {
510                          if (mService.mPowerManagerInternal != null) {
511                              mService.mPowerManagerInternal.setPowerBoost(
512                                      Boost.INTERACTION, duration);
513                          }
514                      }
534                      ......
562                  });
             // 接口的注册
563          displayContent.registerPointerEventListener(mSystemGestures);


    }
}

可以看到,在 DisplayPolicy 的构造方法内部,会创建 SystemGesturesPointerEventListener,并通过 displayContent 的registerPointerEventListener() 进行注册,接下来看一下 DisplayPolicy 构造方法的调用入口在 DisplayContent 的构造函数中实现,:

DisplayContent.java

DisplayContent(Display display, RootWindowContainer root) {

    mDisplayPolicy = new DisplayPolicy(mWmService, this);


}

由此可见 DisplayContent 构造的时候创建了对全局手势的监听,然后调用 DisplayContent 的 registerPointerEventListener 注册这个监听,如下:

DisplayContent.java

void registerPointerEventListener(@NonNull PointerEventListener listener) {
4933          mPointerEventDispatcher.registerInputEventListener(listener);
4934      }

至此注册监听的流程完毕。

三 native 层处理

关于触摸事件从接收、传递、处理在前面的几篇文章里面已经详细分析了,全局手势事件也是大致相同的处理流程,本章仅将涉及全局手势事件处理相关的点来进行分析。

先从注册讲起,我们来看 InputChannel 的生成:

InputManagerService.java

public InputChannel monitorInput(String inputChannelName, int displayId) {
699          if (inputChannelName == null) {
700              throw new IllegalArgumentException("inputChannelName must not be null.");
701          }
702  
703          if (displayId < Display.DEFAULT_DISPLAY) {
704              throw new IllegalArgumentException("displayId must >= 0.");
705          }
706  
707          return nativeCreateInputMonitor(mPtr, displayId, false /* isGestureMonitor */,
708                  inputChannelName, Binder.getCallingPid());
709      }

调用 native 方法 nativeCreateInputMonitor

com_android_server_input_InputManagerService.cpp

static jobject nativeCreateInputMonitor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint displayId,
1604                                          jboolean isGestureMonitor, jstring nameObj, jint pid) {
1605      NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
1606  
1607      if (displayId == ADISPLAY_ID_NONE) {
1608          std::string message = "InputChannel used as a monitor must be associated with a display";
1609          jniThrowRuntimeException(env, message.c_str());
1610          return nullptr;
1611      }
1612  
1613      ......
1615  
1616      base::Result<std::unique_ptr<InputChannel>> inputChannel =
1617              im->createInputMonitor(env, displayId, isGestureMonitor, name, pid);
1618  
1619      ......
1625  
1626      jobject inputChannelObj =
1627              android_view_InputChannel_createJavaObject(env, std::move(*inputChannel));
1628      if (!inputChannelObj) {
1629          return nullptr;
1630      }
1631      return inputChannelObj;
1632  }

生成 java 层的 inputChannel 对象并返回。我们接下来看 native 层是怎么构建 inputChannel 的。

com_android_server_input_InputManagerService.cpp

base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputMonitor(
518          JNIEnv* /* env */, int32_t displayId, bool isGestureMonitor, const std::string& name,
519          int32_t pid) {
520      ATRACE_CALL();
         // 可以知道是通过 InputDispatcher 来生成 InputMonitor
521      return mInputManager->getDispatcher()->createInputMonitor(displayId, isGestureMonitor, name,
522                                                                pid);
523  }

可以知道是通过 InputDispatcher 来生成 InputMonitor

InputDispatcher.cpp


Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(int32_t displayId,
5226                                                                            bool isGestureMonitor,
5227                                                                            const std::string& name,
5228                                                                            int32_t pid) {
5229      std::shared_ptr<InputChannel> serverChannel;
5230      std::unique_ptr<InputChannel> clientChannel;
5231      status_t result = openInputChannelPair(name, serverChannel, clientChannel);
5232      if (result) {
5233          return base::Error(result) << "Failed to open input channel pair with name " << name;
5234      }
5235  
5236      { // acquire lock
5237          ......
5243  
5244          sp<Connection> connection = new Connection(serverChannel, true /*monitor*/, mIdGenerator);
5245          const sp<IBinder>& token = serverChannel->getConnectionToken();
5246          const int fd = serverChannel->getFd();
5247  
5248          if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) {
5249              ALOGE("Created a new connection, but the token %p is already known", token.get());
5250          }
5251          mConnectionsByToken.emplace(token, connection);
5252          std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,
5253                                                              this, std::placeholders::_1, token);
5254  
5255          auto& monitorsByDisplay =
5256                  isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay;
5257          monitorsByDisplay[displayId].emplace_back(serverChannel, pid);
5258  
5259          mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);
5260          ALOGI("Created monitor %s for display %" PRId32 ", gesture=%s, pid=%" PRId32, name.c_str(),
5261                displayId, toString(isGestureMonitor), pid);
5262      }
5263  
5264      // Wake the looper because some connections have changed.
5265      mLooper->wake();
5266      return clientChannel;
5267  }

主要干了四件事:

  • 首先使用 openInputChannelPair 创建了2个 InputChannel,一个 clientChannel 和一个 serverChannel
  • 将 serverChannel 封装成 connection,并放入成员变量 mConnectionsByToken 中管理,这样在事件到来的时候就可以使用 connection 向客户端发送事件了
  • 利用 Looper 持续监听 serverChannel,事件处理的回调消息会就到 InputDispatcher::handleReceiveCallback 回调,最后把 clientChannel 返回给客户端,也就是最初在 WMS 中得到的 InputChannel,这样在 WMS 就也能通过 clientChannel 来获取事件了
  • 生成了 LooperEventCallback 并添加了 callback 监听serverChannel 的 fd,这样有事件发生时,就会回调给 InputDispatcher::handleReceiveCallback 方法。
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android全局Dialog是一种可以在应用程序的任何界面中显示的对话框。它可以覆盖在当前界面上方,并且不会阻止用户与其他部分进行交互。可以使用全局Dialog来显示一些重要的信息、警告、确认对话框等。 要创建一个全局Dialog,可以使用Android的Dialog类。首先,需要在应用程序的主题中设置一个全局样式,该样式将应用于所有的Dialog。可以在styles.xml文件中定义这个样式,例如: ```xml <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- 全局Dialog样式 --> <item name="android:dialogTheme">@style/CustomDialogTheme</item> </style> <style name="CustomDialogTheme" parent="Theme.AppCompat.Dialog"> <!-- 自定义全局Dialog样式 --> <item name="android:windowIsFloating">true</item> <!-- 其他自定义属性 --> </style> ``` 然后,在代码中创建Dialog实例并显示出来。可以在任何需要显示全局Dialog的地方调用以下代码: ```java Dialog dialog = new Dialog(context, R.style.CustomDialogTheme); dialog.setContentView(R.layout.dialog_layout); // 设置其他Dialog属性和内容 dialog.show(); ``` 这里的`R.layout.dialog_layout`是自定义的对话框布局文件,可以根据需要进行修改。 需要注意的是,全局Dialog并不是一种推荐的UI设计方式,因为它可能会打断用户的操作流程,并且可能会给用户带来困扰。在使用全局Dialog时,应该谨慎考虑,并确保它的使用是合理的,不会对用户体验造成负面影响。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值