API 28-32如何将InputQueue转换为AInputQueue?
问题:API 28-32 下 InputQueue 转 AInputQueue 的兼容方案
背景
自定义实现MyNativeActivity时需重写onInputQueueCreated()方法,原生NativeActivity的实现是调用InputQueue.getNativePtr()获取原生指针后传给onInputQueueCreatedNative():
public void onInputQueueCreated(InputQueue queue) { if (!mDestroyed) { mCurInputQueue = queue; onInputQueueCreatedNative(mNativeHandle, queue.getNativePtr()); } }
但存在版本兼容性问题:
- API 28 及以下:
getNativePtr()可通过JNI调用,但属于隐藏API - API 33 及以上:可使用NDK提供的
AInputQueue_fromJava()直接转换 - API 28-32:
getNativePtr()被加入暗灰名单,调用返回null;反射调用该方法同样无效
可行解决方案
在API 28-32版本中,可通过反射直接读取InputQueue内部的mNativePtr字段(而非调用方法)获取原生指针:
反射获取原生指针代码
import java.lang.reflect.Field; public static long getIQNativePtr(InputQueue queue) { try { // 反射读取InputQueue内部的mNativePtr字段 Field field = InputQueue.class.getDeclaredField("mNativePtr"); field.setAccessible(true); return (long) field.get(queue); } catch (Exception e) { e.printStackTrace(); return 0; } }
原理说明
API 28之后,InputQueue的getNativePtr()方法内部实际是返回mNativePtr字段,但该方法被标记为隐藏且加入暗灰名单,直接或反射调用都会被系统拦截;而直接反射读取mNativePtr字段,在多数设备上可绕过该限制(注意:字段名属于内部实现,部分定制ROM可能存在差异)。
全版本兼容处理
在onInputQueueCreated()中加入版本判断,覆盖所有API等级:
public void onInputQueueCreated(InputQueue queue) { if (!mDestroyed) { mCurInputQueue = queue; long nativePtr = 0; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { // API 33+ 使用NDK官方转换方法 nativePtr = AInputQueue_fromJava(queue); } else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { // API 28及以下 直接调用getNativePtr() nativePtr = queue.getNativePtr(); } else { // API 29-32 反射读取mNativePtr字段 nativePtr = getIQNativePtr(queue); } if (nativePtr != 0) { onInputQueueCreatedNative(mNativeHandle, nativePtr); } } }
注意事项
- 反射访问内部字段存在兼容性风险,若厂商修改
InputQueue内部实现,可能导致获取失败 - 针对API 28-32,也可直接实现
AInputQueue的JNI绑定逻辑,但复杂度较高,反射字段是更简便的临时方案
内容的提问来源于stack exchange,提问作者dawnmenu
相关产品推荐
相关产品推荐

