Android R PointerEventDispatcher 触摸事件监听帮助类
Posted pecuyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android R PointerEventDispatcher 触摸事件监听帮助类相关的知识,希望对你有一定的参考价值。
文章托管在gitee上 Android Notes , 同步csdn
PointerEventDispatcher 介绍
PointerEventDispatcher类的功能如同名字描述的那样,它是一个触摸事件分发类. 使用者通过registerInputEventListener(PointerEventListener listener) 方法向它注册事件监听,当有事件到来时,它的onInputEvent(InputEvent event)方法会被调用到, 然后将事件分发给所有的监听者. 当不需要监听Pointer事件,调用
unregisterInputEventListener(PointerEventListener listener).
原理分析
PointerEventDispatcher 继承自InputEventReceiver, 因此它可以使用传进来的inputChannel构建一个与inputManagerService传递事件的通道. 当对端派发事件时,本地native端的InputChannel会收到相关事件, 并向上传递此事件, 回调它的onInputEvent(InputEvent event)方法.
public class PointerEventDispatcher extends InputEventReceiver {
// 注册的Pointer监听列表
private final ArrayList<PointerEventListener> mListeners = new ArrayList<>();
private PointerEventListener[] mListenersArray = new PointerEventListener[0];
public PointerEventDispatcher(InputChannel inputChannel) {
super(inputChannel, UiThread.getHandler().getLooper()); // 注意这个关键的super, 参数是inputChannel
}
InputEventReceiver 构造函数创建事件通道
/**
* Creates an input event receiver bound to the specified input channel.
*
* @param inputChannel The input channel.
* @param looper The looper to use when invoking callbacks.
*/
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null");
}
if (looper == null) {
throw new IllegalArgumentException("looper must not be null");
}
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
// 初始化native的InputChannel
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);
mCloseGuard.open("dispose");
}
注册监听
/**
* Add the specified listener to the list.
* @param listener The listener to add.
*/
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;
}
}
事件分发
@Override
public void onInputEvent(InputEvent event) {
try {
if (event instanceof MotionEvent
&& (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
final MotionEvent motionEvent = (MotionEvent) event;
PointerEventListener[] listeners;
// 将mListeners拷贝到mListenersArray
// 这一步的作用应该是创建一个固定长度的数组,不被修改 减少同步,
// 不然整个分发过程都需要加锁mListeners
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); // 分发事件
}
}
} finally {
// 发送事件派发结束的反馈, 注意第二个参数为false, 表示没有被处理
finishInputEvent(event, /*handled*/ false);
}
}
取消注册
/**
* Remove the specified listener from the list.
* @param listener The listener to remove.
*/
public void unregisterInputEventListener(PointerEventListener listener) {
synchronized (mListeners) {
if (!mListeners.contains(listener)) {
throw new IllegalStateException("registerInputEventListener: " + listener +
" not registered.");
}
mListeners.remove(listener);
mListenersArray = null;
}
}
清理
/** Dispose the associated input channel and clean up the listeners. */
@Override
public void dispose() {
super.dispose();
synchronized (mListeners) {
mListeners.clear();
mListenersArray = null;
}
}
应用
在DisplayContent的构造方法中创建了PointerEventDispatcher, 并注册了一些监听
// DisplayContent#<init>
...
// app transition相关
mAppTransition = new AppTransition(mWmService.mContext, mWmService, this);
mAppTransition.registerListenerLocked(mWmService.mActivityManagerAppTransitionNotifier);
mAppTransition.registerListenerLocked(mFixedRotationTransitionListener);
mAppTransitionController = new AppTransitionController(mWmService, this);
mUnknownAppVisibilityController = new UnknownAppVisibilityController(mWmService, this);
// 关键: 创建inputChannel, 使用 nativeRegisterInputMonitor 注册monitor
final InputChannel inputChannel = mWmService.mInputManager.monitorInput(
"PointerEventDispatcher" + mDisplayId, mDisplayId);
// 创建PointerEventDispatcher
mPointerEventDispatcher = new PointerEventDispatcher(inputChannel);
// Tap Listeners are supported for:
// 1. All physical displays (multi-display).
// 2. VirtualDisplays on VR, AA (and everything else).
mTapDetector = new TaskTapPointerEventListener(mWmService, this);
registerPointerEventListener(mTapDetector); // 注册tap事件到PointerEventDispatcher
registerPointerEventListener(mWmService.mMousePositionTracker);
if (mWmService.mAtmService.getRecentTasks() != null) {
registerPointerEventListener(
mWmService.mAtmService.getRecentTasks().getInputListener());
}
看一下 DisplayConent#registerPointerEventListener
// 向mPointerEventDispatcher注册监听
void registerPointerEventListener(@NonNull PointerEventListener listener) {
mPointerEventDispatcher.registerInputEventListener(listener);
}
PointerLocationView
另外一个例子是开发者选项的 PointerLocationView 的显示, 这个实现在 DisplayPolicy.
路径是frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java
enablePointerLocation
开启PointerLocationView 的显示
private void enablePointerLocation() {
if (mPointerLocationView != null) {
return;
}
mPointerLocationView = new PointerLocationView(mContext);
mPointerLocationView.setPrintCoords(false);
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT);
lp.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
lp.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
lp.setFitInsetsTypes(0);
lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
if (ActivityManager.isHighEndGfx()) {
lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
lp.privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED;
}
lp.format = PixelFormat.TRANSLUCENT;
lp.setTitle("PointerLocation - display " + getDisplayId());
lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
final WindowManager wm = mContext.getSystemService(WindowManager.class);
wm.addView(mPointerLocationView, lp); // 添加窗口显示
mDisplayContent.registerPointerEventListener(mPointerLocationView); // 最终还是注册到了 mPointerEventDispatcher
}
disablePointerLocation
关闭 PointerLocationView 的显示
private void disablePointerLocation() {
if (mPointerLocationView == null) {
return;
}
mDisplayContent.unregisterPointerEventListener(mPointerLocationView); // 移除监听
final WindowManager wm = mContext.getSystemService(WindowManager.class);
wm.removeView(mPointerLocationView); // 移除窗口
mPointerLocationView = null;
}
InputManagerService#monitorInput
下面看一个比较关键的部分, 就是InputManagerService#monitorInput的实现,这个方法会创建一对InputChannel,native层的实现是socket pair.
然后将一端注册到native的InputDispatcher, 另一端返回给 PointerEventDispatcher来创建自身, 从而建立了两者事件派发的通道.
/// @frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
/**
* Creates an input channel that will receive all input from the input dispatcher.
* @param inputChannelName The input channel name.
* @param displayId Target display id.
* @return The input channel.
*/
public InputChannel monitorInput(String inputChannelName, int displayId) {
if (inputChannelName == null) {
throw new IllegalArgumentException("inputChannelName must not be null.");
}
if (displayId < Display.DEFAULT_DISPLAY) {
throw new IllegalArgumentException("displayId must >= 0.");
}
// 创建InputChannel对
InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
// 注册inputChannels[0]到 InputDispatcher
nativeRegisterInputMonitor(mPtr, inputChannels[0], displayId, false /*isGestureMonitor*/);
// 这个地方的作用是释放native层的NativeInputChannel的对象, 置空java层InputChannel的mPtr
inputChannels[0].dispose(); // don't need to retain the Java object reference
return inputChannels[1]; // 返回另一端
}
nativeRegisterInputMonitor
注意这个方法与nativeRegisterInputChannel的区别 , 它没有调用 android_view_InputChannel_setDisposeCallback 设置DisposeCallback , 这个对上面的 inputChannels[0].dispose() 操作有较大影响.
/// @frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static void nativeRegisterInputMonitor(JNIEnv* env, jclass /* clazz */,
jlong ptr, jobject inputChannelObj, jint displayId, jboolean isGestureMonitor) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
if (inputChannel == nullptr) {
throwInputChannelNotInitialized(env);
return;
}
if (displayId == ADISPLAY_ID_NONE) {
std::string message = "InputChannel used as a monitor must be associated with a display";
jniThrowRuntimeException(env, message.c_str());
return;
}
// 通过 NativeInputManager 注册
status_t status = im->registerInputMonitor(env, inputChannel, displayId, isGestureMonitor);
if (status) {
std::string message = StringPrintf("Failed to register input channel. status=%d", status);
jniThrowRuntimeException(env, message.c_str());
return;
}
}
NativeInputManager::registerInputMonitor
status_t NativeInputManager::registerInputMonitor(JNIEnv* /* env */,
const sp<InputChannel>& inputChannel, int32_t displayId, bool isGestureMonitor) {
ATRACE_CALL();
return mInputManager->getDispatcher()->registerInputMonitor(
inputChannel, displayId, isGestureMonitor);
}
InputDispatcher::registerInputMonitor
/// @frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
status_t InputDispatcher::registerInputMonitor(const sp<InputChannel>& inputChannel,
int32_t displayId, bool isGestureMonitor) {
{ // acquire lock
std::scoped_lock _l(mLock);
if (displayId < 0) {
ALOGW("Attempted to register input monitor without a specified display.");
return BAD_VALUE;
}
if (inputChannel->getConnectionToken() == nullptr) {
ALOGW("Attempted to register input monitor without an identifying token.");
return BAD_VALUE;
}
// 创建 Connection
sp<Connection> connection = new Connection(inputChannel, true /*monitor*/, mIdGenerator);
// 保存各种信息
const int fd = inputChannel->getFd();
mConnectionsByFd[fd] = connection;
mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;
auto& monitorsByDisplay =
isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay;
monitorsByDisplay[displayId].emplace_back(inputChannel);
// 监听此InputChannel的fd,接收Input事件处理的反馈
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
}
// Wake the looper because some connections have changed.
mLooper->wake();
return OK;
}
InputChannel#dispose
/**
* Disposes the input channel.
* Explicitly releases the reference this object is holding on the input channel.
* When all references are released, the input channel will be closed.
*/
public void dispose() {
nativeDispose(false); // 调用native方法处理
}
对应的jni方法如下
static void android_view_InputChannel_nativeDispose(JNIEnv* env, jobject obj, jboolean finalized) {
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, obj);
if (nativeInputChannel) {
if (finalized) {
ALOGW("Input channel object '%s' was finalized without being disposed!",
nativeInputChannel->getInputChannel()->getName().c_str());
}
nativeInputChannel->invokeAndRemoveDisposeCallback(env, obj);
// java层InputChannel的mPtr置为nullprt
android_view_InputChannel_setNativeInputChannel(env, obj, nullptr);
// 释放NativeInputChannel
delete nativeInputChannel;
}
}
NativeInputChannel#invokeAndRemoveDisposeCallback
由上面可知,nativeRegisterInputMonitor没有设置DisposeCallback, 因此此函数实际上就没有执行操作.
void NativeInputChannel::invokeAndRemoveDisposeCallback(JNIEnv* env, jobject obj) {
if (mDisposeCallback) {
mDisposeCallback(env, obj, mInputChannel, mDisposeData);
mDisposeCallback = nullptr;
mDisposeData = nullptr;
}
}
由上可知 InputChannel#dispose()方法释放了NativeInputChannel. 另外,java层InputChannels[0] 由于没有任何引用,也会很快被JVM回收.
以上是关于Android R PointerEventDispatcher 触摸事件监听帮助类的主要内容,如果未能解决你的问题,请参考以下文章
为啥 Android Studio 将我所有的引用从 R 更改为 android.R?
Android:android.R.id.content 是做啥用的?
Android - 如何获取 android.R.anim.slide_in_right
同时导入 R(android.R 和 <my_package>.R)