JNI -学习2

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函数
关于什么类型的参数使用什么签名如图所示
基本数据类型


image.png

引用数据类型


image.png

根据上述图片,我们就可以自己去定义方法的签名了
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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容