1 静态注册
流程如下
- 1 java类编写native方法 make编译生成.class文件
- 2 在.class文件生成的父级目录debug目录,执行javah -jni 包名.类名,生成jni的头文件
- 3 根据头文件的方法声明,编写cpp文件实现
- 4 编写mk文件,在java文件中通过system.loadlibrary来加载编译的jni库
具体实例可以参考
https://www.jianshu.com/p/b4431ac22ec2
2 动态注册 以源码为例
这里主要分析InputManagerService中涉及的JNI
提到动态注册,先要知道为什么要有动态注册,他对比静态注册的优点都有哪些
(分析完静态注册在写上)
-1 首先静态注册每次修改jni方法,都需要本地编译生成对应jni声明文件,然后替换,这很麻烦
- 2 静态注册建立映射关系的函数名,过于长且复杂,方法名称不能修改
原因是需要按照编译生成的jni头文件建立java方法和jni方法的映射关系
如何实现动态注册
首先要了解动态注册的加载逻辑
- 1 java侧如果需要JNI使用native方法需要通知AndroidRuntime加载JNI编译的so库
system.loadLibrary(xxxx)
- 2 AndroidRuntime在加载编译JNI生成的so库后,主动查找执行该库的JNI_Onload方法
以系统源码举例
路径 frameworks/base/services/core/jni/onload.cpp
#include <android/graphics/jni_runtime.h>
#include <nativehelper/JNIHelp.h>
#include "jni.h"
#include "utils/Log.h"
#include "utils/misc.h"
#include "BroadcastRadio/BroadcastRadioService.h"
#include "BroadcastRadio/Tuner.h"
namespace android {
int register_android_server_BatteryStatsService(JNIEnv* env);
int register_android_server_ConsumerIrService(JNIEnv *env);
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
int register_android_server_PowerStatsService(JNIEnv* env);
int register_android_server_HintManagerService(JNIEnv* env);
int register_android_server_storage_AppFuse(JNIEnv* env);
int register_android_server_SerialService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_UsbAlsaJackDetector(JNIEnv* env);
int register_android_server_UsbDeviceManager(JNIEnv* env);
int register_android_server_UsbMidiDevice(JNIEnv* env);
int register_android_server_UsbHostManager(JNIEnv* env);
int register_android_server_vr_VrManagerService(JNIEnv* env);
int register_android_server_vibrator_VibratorController(JavaVM* vm, JNIEnv* env);
int register_android_server_vibrator_VibratorManagerService(JavaVM* vm, JNIEnv* env);
int register_android_server_location_GnssLocationProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_devicepolicy_CryptoTestHelper(JNIEnv*);
int register_android_server_tv_TvUinputBridge(JNIEnv* env);
int register_android_server_tv_TvInputHal(JNIEnv* env);
int register_android_server_PersistentDataBlockService(JNIEnv* env);
int register_android_server_Watchdog(JNIEnv* env);
int register_android_server_HardwarePropertiesManagerService(JNIEnv* env);
int register_android_server_SyntheticPasswordManager(JNIEnv* env);
int register_android_hardware_display_DisplayViewport(JNIEnv* env);
int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
int register_android_server_am_LowMemDetector(JNIEnv* env);
int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(JNIEnv* env);
int register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker(JNIEnv* env);
int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(JNIEnv* env);
int register_android_server_AdbDebuggingManager(JNIEnv* env);
int register_android_server_FaceService(JNIEnv* env);
int register_android_server_GpuService(JNIEnv* env);
int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env);
int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env);
int register_android_server_companion_virtual_InputController(JNIEnv* env);
int register_android_server_app_GameManagerService(JNIEnv* env);
int register_com_android_server_wm_TaskFpsCallbackController(JNIEnv* env);
};
using namespace android;
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("GetEnv failed!");
return result;
}
ALOG_ASSERT(env, "Could not retrieve the env!");
register_android_server_broadcastradio_BroadcastRadioService(env);
register_android_server_broadcastradio_Tuner(vm, env);
register_android_server_PowerManagerService(env);
register_android_server_PowerStatsService(env);
register_android_server_HintManagerService(env);
register_android_server_SerialService(env);
register_android_server_InputManager(env);
register_android_server_LightsService(env);
register_android_server_UsbDeviceManager(env);
register_android_server_UsbMidiDevice(env);
register_android_server_UsbAlsaJackDetector(env);
register_android_server_UsbHostManager(env);
register_android_server_vr_VrManagerService(env);
register_android_server_vibrator_VibratorController(vm, env);
register_android_server_vibrator_VibratorManagerService(vm, env);
register_android_server_SystemServer(env);
register_android_server_location_GnssLocationProvider(env);
register_android_server_connectivity_Vpn(env);
register_android_server_devicepolicy_CryptoTestHelper(env);
register_android_server_ConsumerIrService(env);
register_android_server_BatteryStatsService(env);
register_android_server_tv_TvUinputBridge(env);
register_android_server_tv_TvInputHal(env);
register_android_server_PersistentDataBlockService(env);
register_android_server_HardwarePropertiesManagerService(env);
register_android_server_storage_AppFuse(env);
register_android_server_SyntheticPasswordManager(env);
register_android_hardware_display_DisplayViewport(env);
register_android_server_am_CachedAppOptimizer(env);
register_android_server_am_LowMemDetector(env);
register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(env);
register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker(env);
register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(env);
register_android_server_AdbDebuggingManager(env);
register_android_server_FaceService(env);
register_android_server_GpuService(env);
register_android_server_stats_pull_StatsPullAtomService(env);
register_android_server_sensor_SensorService(vm, env);
register_android_server_companion_virtual_InputController(env);
register_android_server_app_GameManagerService(env);
register_com_android_server_wm_TaskFpsCallbackController(env);
return JNI_VERSION_1_4;
}
在JNI_Onload方法中首先我们通过jvm获取JNIEnv 对象
这个JNIEnv很重要,它代表当前线程的java执行环境,通过它我们才可以在native层调用java方法,创建java对象等等
可以看到系统要注册的jni方法还是蛮多的,这里我们着重看register_android_server_InputManager(env)方法
那么这个方法由做了什么呢
- 3 建立java方法与jni方法的方法映射关系
路径:frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
int register_android_server_InputManager(JNIEnv* env) {
int res = jniRegisterNativeMethods(env,
"com/android/server/input/"
"NativeInputManagerService$NativeImpl",
gInputManagerMethods, NELEM(gInputManagerMethods));
(void)res; // Faked use when LOG_NDEBUG.
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
FIND_CLASS(gNativeInputManagerServiceImpl.clazz,
"com/android/server/input/"
"NativeInputManagerService$NativeImpl");
gNativeInputManagerServiceImpl.clazz =
jclass(env->NewGlobalRef(gNativeInputManagerServiceImpl.clazz));
gNativeInputManagerServiceImpl.mPtr =
env->GetFieldID(gNativeInputManagerServiceImpl.clazz, "mPtr", "J");
// Callbacks
jclass clazz;
FIND_CLASS(clazz, "com/android/server/input/InputManagerService");
gServiceClassInfo.clazz = reinterpret_cast<jclass>(env->NewGlobalRef(clazz));
GET_METHOD_ID(gServiceClassInfo.notifyConfigurationChanged, clazz,
"notifyConfigurationChanged", "(J)V");
GET_METHOD_ID(gServiceClassInfo.notifyInputDevicesChanged, clazz,
"notifyInputDevicesChanged", "([Landroid/view/InputDevice;)V");
GET_METHOD_ID(gServiceClassInfo.notifySwitch, clazz,
"notifySwitch", "(JII)V");
GET_METHOD_ID(gServiceClassInfo.notifyInputChannelBroken, clazz,
"notifyInputChannelBroken", "(Landroid/os/IBinder;)V");
GET_METHOD_ID(gServiceClassInfo.notifyFocusChanged, clazz,
"notifyFocusChanged", "(Landroid/os/IBinder;Landroid/os/IBinder;)V");
GET_METHOD_ID(gServiceClassInfo.notifyDropWindow, clazz, "notifyDropWindow",
"(Landroid/os/IBinder;FF)V");
GET_METHOD_ID(gServiceClassInfo.notifySensorEvent, clazz, "notifySensorEvent", "(IIIJ[F)V");
GET_METHOD_ID(gServiceClassInfo.notifySensorAccuracy, clazz, "notifySensorAccuracy", "(III)V");
GET_METHOD_ID(gServiceClassInfo.notifyVibratorState, clazz, "notifyVibratorState", "(IZ)V");
GET_METHOD_ID(gServiceClassInfo.notifyUntrustedTouch, clazz, "notifyUntrustedTouch",
"(Ljava/lang/String;)V");
GET_METHOD_ID(gServiceClassInfo.notifyNoFocusedWindowAnr, clazz, "notifyNoFocusedWindowAnr",
"(Landroid/view/InputApplicationHandle;)V");
GET_METHOD_ID(gServiceClassInfo.notifyWindowUnresponsive, clazz, "notifyWindowUnresponsive",
"(Landroid/os/IBinder;IZLjava/lang/String;)V");
GET_METHOD_ID(gServiceClassInfo.notifyWindowResponsive, clazz, "notifyWindowResponsive",
"(Landroid/os/IBinder;IZ)V");
GET_METHOD_ID(gServiceClassInfo.filterInputEvent, clazz,
"filterInputEvent", "(Landroid/view/InputEvent;I)Z");
GET_METHOD_ID(gServiceClassInfo.interceptKeyBeforeQueueing, clazz,
"interceptKeyBeforeQueueing", "(Landroid/view/KeyEvent;I)I");
GET_METHOD_ID(gServiceClassInfo.interceptMotionBeforeQueueingNonInteractive, clazz,
"interceptMotionBeforeQueueingNonInteractive", "(IJI)I");
GET_METHOD_ID(gServiceClassInfo.interceptKeyBeforeDispatching, clazz,
"interceptKeyBeforeDispatching",
"(Landroid/os/IBinder;Landroid/view/KeyEvent;I)J");
GET_METHOD_ID(gServiceClassInfo.dispatchUnhandledKey, clazz,
"dispatchUnhandledKey",
"(Landroid/os/IBinder;Landroid/view/KeyEvent;I)Landroid/view/KeyEvent;");
GET_METHOD_ID(gServiceClassInfo.onPointerDisplayIdChanged, clazz, "onPointerDisplayIdChanged",
"(IFF)V");
GET_METHOD_ID(gServiceClassInfo.onPointerDownOutsideFocus, clazz,
"onPointerDownOutsideFocus", "(Landroid/os/IBinder;)V");
GET_METHOD_ID(gServiceClassInfo.getVirtualKeyQuietTimeMillis, clazz,
"getVirtualKeyQuietTimeMillis", "()I");
GET_STATIC_METHOD_ID(gServiceClassInfo.getExcludedDeviceNames, clazz,
"getExcludedDeviceNames", "()[Ljava/lang/String;");
GET_METHOD_ID(gServiceClassInfo.getInputPortAssociations, clazz,
"getInputPortAssociations", "()[Ljava/lang/String;");
GET_METHOD_ID(gServiceClassInfo.getInputUniqueIdAssociations, clazz,
"getInputUniqueIdAssociations", "()[Ljava/lang/String;");
GET_METHOD_ID(gServiceClassInfo.getKeyRepeatTimeout, clazz,
"getKeyRepeatTimeout", "()I");
GET_METHOD_ID(gServiceClassInfo.getKeyRepeatDelay, clazz,
"getKeyRepeatDelay", "()I");
GET_METHOD_ID(gServiceClassInfo.getHoverTapTimeout, clazz,
"getHoverTapTimeout", "()I");
GET_METHOD_ID(gServiceClassInfo.getHoverTapSlop, clazz,
"getHoverTapSlop", "()I");
GET_METHOD_ID(gServiceClassInfo.getDoubleTapTimeout, clazz,
"getDoubleTapTimeout", "()I");
GET_METHOD_ID(gServiceClassInfo.getLongPressTimeout, clazz,
"getLongPressTimeout", "()I");
GET_METHOD_ID(gServiceClassInfo.getPointerLayer, clazz,
"getPointerLayer", "()I");
GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz,
"getPointerIcon", "(I)Landroid/view/PointerIcon;");
GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz,
"getKeyboardLayoutOverlay",
"(Landroid/hardware/input/InputDeviceIdentifier;)[Ljava/lang/String;");
GET_METHOD_ID(gServiceClassInfo.getDeviceAlias, clazz,
"getDeviceAlias", "(Ljava/lang/String;)Ljava/lang/String;");
GET_METHOD_ID(gServiceClassInfo.getTouchCalibrationForInputDevice, clazz,
"getTouchCalibrationForInputDevice",
"(Ljava/lang/String;I)Landroid/hardware/input/TouchCalibration;");
GET_METHOD_ID(gServiceClassInfo.getContextForDisplay, clazz, "getContextForDisplay",
"(I)Landroid/content/Context;");
GET_METHOD_ID(gServiceClassInfo.getParentSurfaceForPointers, clazz,
"getParentSurfaceForPointers", "(I)J");
// InputDevice
FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice");
gInputDeviceClassInfo.clazz = jclass(env->NewGlobalRef(gInputDeviceClassInfo.clazz));
// KeyEvent
FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent");
gKeyEventClassInfo.clazz = jclass(env->NewGlobalRef(gKeyEventClassInfo.clazz));
// MotionEvent
FIND_CLASS(gMotionEventClassInfo.clazz, "android/view/MotionEvent");
gMotionEventClassInfo.clazz = jclass(env->NewGlobalRef(gMotionEventClassInfo.clazz));
// InputDeviceIdentifier
FIND_CLASS(gInputDeviceIdentifierInfo.clazz, "android/hardware/input/InputDeviceIdentifier");
gInputDeviceIdentifierInfo.clazz = jclass(env->NewGlobalRef(gInputDeviceIdentifierInfo.clazz));
GET_METHOD_ID(gInputDeviceIdentifierInfo.constructor, gInputDeviceIdentifierInfo.clazz,
"<init>", "(Ljava/lang/String;II)V");
// TouchCalibration
FIND_CLASS(gTouchCalibrationClassInfo.clazz, "android/hardware/input/TouchCalibration");
gTouchCalibrationClassInfo.clazz = jclass(env->NewGlobalRef(gTouchCalibrationClassInfo.clazz));
GET_METHOD_ID(gTouchCalibrationClassInfo.getAffineTransform, gTouchCalibrationClassInfo.clazz,
"getAffineTransform", "()[F");
// Light
FIND_CLASS(gLightClassInfo.clazz, "android/hardware/lights/Light");
gLightClassInfo.clazz = jclass(env->NewGlobalRef(gLightClassInfo.clazz));
GET_METHOD_ID(gLightClassInfo.constructor, gLightClassInfo.clazz, "<init>",
"(ILjava/lang/String;III)V");
gLightClassInfo.clazz = jclass(env->NewGlobalRef(gLightClassInfo.clazz));
gLightClassInfo.lightTypeInput =
env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_INPUT", "I");
gLightClassInfo.lightTypePlayerId =
env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_PLAYER_ID", "I");
gLightClassInfo.lightCapabilityBrightness =
env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_CAPABILITY_BRIGHTNESS", "I");
gLightClassInfo.lightCapabilityRgb =
env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_CAPABILITY_RGB", "I");
// ArrayList
FIND_CLASS(gArrayListClassInfo.clazz, "java/util/ArrayList");
gArrayListClassInfo.clazz = jclass(env->NewGlobalRef(gArrayListClassInfo.clazz));
GET_METHOD_ID(gArrayListClassInfo.constructor, gArrayListClassInfo.clazz, "<init>", "()V");
GET_METHOD_ID(gArrayListClassInfo.add, gArrayListClassInfo.clazz, "add",
"(Ljava/lang/Object;)Z");
// SparseArray
FIND_CLASS(gSparseArrayClassInfo.clazz, "android/util/SparseArray");
gSparseArrayClassInfo.clazz = jclass(env->NewGlobalRef(gSparseArrayClassInfo.clazz));
GET_METHOD_ID(gSparseArrayClassInfo.constructor, gSparseArrayClassInfo.clazz, "<init>", "()V");
GET_METHOD_ID(gSparseArrayClassInfo.keyAt, gSparseArrayClassInfo.clazz, "keyAt", "(I)I");
GET_METHOD_ID(gSparseArrayClassInfo.valueAt, gSparseArrayClassInfo.clazz, "valueAt",
"(I)Ljava/lang/Object;");
GET_METHOD_ID(gSparseArrayClassInfo.size, gSparseArrayClassInfo.clazz, "size", "()I");
// InputSensorInfo
// android.hardware.input.InputDeviceSensorInfo
FIND_CLASS(clazz, "android/hardware/input/InputSensorInfo");
gInputSensorInfo.clazz = reinterpret_cast<jclass>(env->NewGlobalRef(clazz));
GET_FIELD_ID(gInputSensorInfo.name, gInputSensorInfo.clazz, "mName", "Ljava/lang/String;");
GET_FIELD_ID(gInputSensorInfo.vendor, gInputSensorInfo.clazz, "mVendor", "Ljava/lang/String;");
GET_FIELD_ID(gInputSensorInfo.version, gInputSensorInfo.clazz, "mVersion", "I");
GET_FIELD_ID(gInputSensorInfo.handle, gInputSensorInfo.clazz, "mHandle", "I");
GET_FIELD_ID(gInputSensorInfo.maxRange, gInputSensorInfo.clazz, "mMaxRange", "F");
GET_FIELD_ID(gInputSensorInfo.resolution, gInputSensorInfo.clazz, "mResolution", "F");
GET_FIELD_ID(gInputSensorInfo.power, gInputSensorInfo.clazz, "mPower", "F");
GET_FIELD_ID(gInputSensorInfo.minDelay, gInputSensorInfo.clazz, "mMinDelay", "I");
GET_FIELD_ID(gInputSensorInfo.fifoReservedEventCount, gInputSensorInfo.clazz,
"mFifoReservedEventCount", "I");
GET_FIELD_ID(gInputSensorInfo.fifoMaxEventCount, gInputSensorInfo.clazz, "mFifoMaxEventCount",
"I");
GET_FIELD_ID(gInputSensorInfo.stringType, gInputSensorInfo.clazz, "mStringType",
"Ljava/lang/String;");
GET_FIELD_ID(gInputSensorInfo.requiredPermission, gInputSensorInfo.clazz, "mRequiredPermission",
"Ljava/lang/String;");
GET_FIELD_ID(gInputSensorInfo.maxDelay, gInputSensorInfo.clazz, "mMaxDelay", "I");
GET_FIELD_ID(gInputSensorInfo.flags, gInputSensorInfo.clazz, "mFlags", "I");
GET_FIELD_ID(gInputSensorInfo.type, gInputSensorInfo.clazz, "mType", "I");
GET_FIELD_ID(gInputSensorInfo.id, gInputSensorInfo.clazz, "mId", "I");
GET_METHOD_ID(gInputSensorInfo.init, gInputSensorInfo.clazz, "<init>", "()V");
return 0;
}
上述为系统的jni方法注册,涉及代码过多,但我们只关注jniRegisterNativeMethods方法,看下它的参数
- 1 env 当前线程的java执行环境
- 2 "com/android/server/input/" 映射的java方法的类路径
- 3 "NativeInputManagerService$NativeImpl" 映射的java方法的类
- 4 gInputManagerMethods 需要映射的方法组
- 5 NELEM(gInputManagerMethods) 需要映射的方法组数量
通过该方法可以将java和jni之间方法的映射关系保存在jni环境的内部结构,后续java方法要调用jni方法,就可以通过jni环境的内部结构找到
到这里关于动态注册加载的逻辑就结束了
下面的主要是通过宏来获取CLASS和METHOD保存到jni里定义的结构体中,这个在下面会单独列出这种写法的好处
- 4 java方法和jni方法如何建立的映射关系
这里主要分析gInputManagerMethods
static const JNINativeMethod gInputManagerMethods[] = {
/* name, signature, funcPtr */
{"init",
"(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/"
"MessageQueue;)J",
(void*)nativeInit},
{"start", "()V", (void*)nativeStart},
{"setDisplayViewports", "([Landroid/hardware/display/DisplayViewport;)V",
(void*)nativeSetDisplayViewports},
{"getScanCodeState", "(III)I", (void*)nativeGetScanCodeState},
{"getKeyCodeState", "(III)I", (void*)nativeGetKeyCodeState},
{"getSwitchState", "(III)I", (void*)nativeGetSwitchState},
{"hasKeys", "(II[I[Z)Z", (void*)nativeHasKeys},
{"getKeyCodeForKeyLocation", "(II)I", (void*)nativeGetKeyCodeForKeyLocation},
{"createInputChannel", "(Ljava/lang/String;)Landroid/view/InputChannel;",
(void*)nativeCreateInputChannel},
{"createInputMonitor", "(ILjava/lang/String;I)Landroid/view/InputChannel;",
(void*)nativeCreateInputMonitor},
{"removeInputChannel", "(Landroid/os/IBinder;)V", (void*)nativeRemoveInputChannel},
{"pilferPointers", "(Landroid/os/IBinder;)V", (void*)nativePilferPointers},
{"setInputFilterEnabled", "(Z)V", (void*)nativeSetInputFilterEnabled},
{"setInTouchMode", "(ZIIZ)Z", (void*)nativeSetInTouchMode},
{"setMaximumObscuringOpacityForTouch", "(F)V",
(void*)nativeSetMaximumObscuringOpacityForTouch},
{"setBlockUntrustedTouchesMode", "(I)V", (void*)nativeSetBlockUntrustedTouchesMode},
{"injectInputEvent", "(Landroid/view/InputEvent;ZIIII)I", (void*)nativeInjectInputEvent},
{"verifyInputEvent", "(Landroid/view/InputEvent;)Landroid/view/VerifiedInputEvent;",
(void*)nativeVerifyInputEvent},
{"toggleCapsLock", "(I)V", (void*)nativeToggleCapsLock},
{"displayRemoved", "(I)V", (void*)nativeDisplayRemoved},
{"setFocusedApplication", "(ILandroid/view/InputApplicationHandle;)V",
(void*)nativeSetFocusedApplication},
{"setFocusedDisplay", "(I)V", (void*)nativeSetFocusedDisplay},
{"requestPointerCapture", "(Landroid/os/IBinder;Z)V", (void*)nativeRequestPointerCapture},
{"setInputDispatchMode", "(ZZ)V", (void*)nativeSetInputDispatchMode},
{"setSystemUiLightsOut", "(Z)V", (void*)nativeSetSystemUiLightsOut},
{"transferTouchFocus", "(Landroid/os/IBinder;Landroid/os/IBinder;Z)Z",
(void*)nativeTransferTouchFocus},
{"transferTouch", "(Landroid/os/IBinder;I)Z", (void*)nativeTransferTouch},
{"setPointerSpeed", "(I)V", (void*)nativeSetPointerSpeed},
{"setPointerAcceleration", "(F)V", (void*)nativeSetPointerAcceleration},
{"setShowTouches", "(Z)V", (void*)nativeSetShowTouches},
{"setInteractive", "(Z)V", (void*)nativeSetInteractive},
{"reloadCalibration", "()V", (void*)nativeReloadCalibration},
{"vibrate", "(I[J[III)V", (void*)nativeVibrate},
{"vibrateCombined", "(I[JLandroid/util/SparseArray;II)V", (void*)nativeVibrateCombined},
{"cancelVibrate", "(II)V", (void*)nativeCancelVibrate},
{"isVibrating", "(I)Z", (void*)nativeIsVibrating},
{"getVibratorIds", "(I)[I", (void*)nativeGetVibratorIds},
{"getLights", "(I)Ljava/util/List;", (void*)nativeGetLights},
{"getLightPlayerId", "(II)I", (void*)nativeGetLightPlayerId},
{"getLightColor", "(II)I", (void*)nativeGetLightColor},
{"setLightPlayerId", "(III)V", (void*)nativeSetLightPlayerId},
{"setLightColor", "(III)V", (void*)nativeSetLightColor},
{"getBatteryCapacity", "(I)I", (void*)nativeGetBatteryCapacity},
{"getBatteryStatus", "(I)I", (void*)nativeGetBatteryStatus},
{"reloadKeyboardLayouts", "()V", (void*)nativeReloadKeyboardLayouts},
{"reloadDeviceAliases", "()V", (void*)nativeReloadDeviceAliases},
{"dump", "()Ljava/lang/String;", (void*)nativeDump},
{"monitor", "()V", (void*)nativeMonitor},
{"isInputDeviceEnabled", "(I)Z", (void*)nativeIsInputDeviceEnabled},
{"enableInputDevice", "(I)V", (void*)nativeEnableInputDevice},
{"disableInputDevice", "(I)V", (void*)nativeDisableInputDevice},
{"setPointerIconType", "(I)V", (void*)nativeSetPointerIconType},
{"reloadPointerIcons", "()V", (void*)nativeReloadPointerIcons},
{"setCustomPointerIcon", "(Landroid/view/PointerIcon;)V",
(void*)nativeSetCustomPointerIcon},
{"canDispatchToDisplay", "(II)Z", (void*)nativeCanDispatchToDisplay},
{"notifyPortAssociationsChanged", "()V", (void*)nativeNotifyPortAssociationsChanged},
{"changeUniqueIdAssociation", "()V", (void*)nativeChangeUniqueIdAssociation},
{"setDisplayEligibilityForPointerCapture", "(IZ)V",
(void*)nativeSetDisplayEligibilityForPointerCapture},
{"setMotionClassifierEnabled", "(Z)V", (void*)nativeSetMotionClassifierEnabled},
{"getSensorList", "(I)[Landroid/hardware/input/InputSensorInfo;",
(void*)nativeGetSensorList},
{"enableSensor", "(IIII)Z", (void*)nativeEnableSensor},
{"disableSensor", "(II)V", (void*)nativeDisableSensor},
{"flushSensor", "(II)Z", (void*)nativeFlushSensor},
{"cancelCurrentTouch", "()V", (void*)nativeCancelCurrentTouch},
{"setPointerDisplayId", "(I)V", (void*)nativeSetPointerDisplayId},
};
这里看到方法的映射对于java方法要声明方法名,签名,jni函数指针
typedef struct {
const char* name; // 本地方法的名称
const char* signature; // 本地方法的签名
void* fnPtr; // 本地方法的函数指针
} JNINativeMethod;
关于方法签名可以这样理解,对于java方法来说函数因返回值和参数不同而产生方法重载,签名主要也是针对参数和返回值设置,格式(参数)返回值,签名的作用可以保证调用到对应的jni函数
关于什么类型的参数使用什么签名如图所示
基本数据类型
引用数据类型
根据上述图片,我们就可以自己去定义方法的签名了
eg nativeInit
函数
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
jobject serviceObj, jobject contextObj, jobject messageQueueObj)
签名
"(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/"
"MessageQueue;)J"
nativeInit有4个参数jclass /* clazz */,
jobject serviceObj, jobject contextObj, jobject messageQueueObj
他对应的签名就是(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/"
"MessageQueue;)J
其中因为参数都是引用类型,所以使用L+类路径的方式,返回值是jlong类型所以签名使用J
关于jni方法映射签名,我们可以使用javap -s -p命令查看一个Java类的所有方法和成员签名
eg
javac .\MediaRecorder.java
Picked up _JAVA_OPTIONS: -Xmx1024M
javap -s -p .\MediaRecorder.class
Picked up _JAVA_OPTIONS: -Xmx1024M
Compiled from "MediaRecorder.java"
public class com.wayeal.module.MediaRecorder {
public com.wayeal.module.MediaRecorder();
descriptor: ()V
private static final native void native_init();
descriptor: ()V
private static final native void native_start(java.lang.String, java.io.File, java.lang.Byte[]);
descriptor: (Ljava/lang/String;Ljava/io/File;[Ljava/lang/Byte;)V
static {};
descriptor: ()V
}
首先javac 将java文件编译成.class文件,然后执行javap -s -p 查看class文件的方法签名信息
到这里我们就可以尝试自己写一个jni动态注册了
接着我们分析源码中的jni使用
- 5 java - jni - native
eg nativeInit
class NativeImpl implements NativeInputManagerService {
/** Pointer to native input manager service object, used by native code. */
@SuppressWarnings({"unused", "FieldCanBeLocal"})
private final long mPtr;
NativeImpl(InputManagerService service, Context context, MessageQueue messageQueue) {
mPtr = init(service, context, messageQueue);
}
private native long init(InputManagerService service, Context context,
MessageQueue messageQueue);
NativeImpl调用init -> jni nativeInit
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == nullptr) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast<jlong>(im);
}
- 6 native - jni - java
eg 以inputdispatcher拦截key事件举例
nsecs_t NativeInputManager::interceptKeyBeforeDispatching(
const sp<IBinder>& token,
const KeyEvent* keyEvent, uint32_t policyFlags) {
ATRACE_CALL();
// Policy:
// - Ignore untrusted events and pass them along.
// - Filter normal events and trusted injected events through the window manager policy to
// handle the HOME key and the like.
nsecs_t result = 0;
if (policyFlags & POLICY_FLAG_TRUSTED) {
JNIEnv* env = jniEnv();
ScopedLocalFrame localFrame(env);
// Token may be null
jobject tokenObj = javaObjectForIBinder(env, token);
jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
if (keyEventObj) {
jlong delayMillis = env->CallLongMethod(mServiceObj,
gServiceClassInfo.interceptKeyBeforeDispatching,
tokenObj, keyEventObj, policyFlags);
bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching");
android_view_KeyEvent_recycle(env, keyEventObj);
env->DeleteLocalRef(keyEventObj);
if (!error) {
if (delayMillis < 0) {
result = -1;
} else if (delayMillis > 0) {
result = milliseconds_to_nanoseconds(delayMillis);
}
}
} else {
ALOGE("Failed to obtain key event object for interceptKeyBeforeDispatching.");
}
}
return result;
}
在jni -> java 主要通过JNIEnv实现本地代码环境调用java代码
-
7 整体流程如图所示
JNI时序.jpg