JNI基础

1.JNI介绍

JNI全称Java Native Interface,是Java和C/C++之间通信的接口,它允许Java应用程序调用和被native代码调用。JNI提供了一种机制,使得Java应用程序可以与使用其他编程语言编写的native库进行交互。

通过JNI,Java应用程序可以调用使用C、C++、Objective-C等编程语言编写的native库中的函数。这对于需要访问底层系统资源、使用特定硬件功能或利用其他语言实现的库的Java应用程序非常有用。

在JNI中,Java代码可以声明native方法,这些方法是使用Java语言编写的函数的声明,但实际上它们的实现是在native代码中完成的。native代码可以是使用其他编程语言编写的代码,例如C或C++。

2.JNI类型和数据结构

2.1Java 原始类型及其对应的Native类型
Java类型Native类型描述
booleanjboolean无符号 8 位
bytejbyte有符号 8 位
charjchar无符号 16 位
shortjshort有符号 16 位
intjint有符号 32 位
longjlong有符号 64 位
floatjfloat32位
doublejdouble64位
voidvoidNA
2.2签名类型

JNI的签名类型对应的Java类型为:

签名类型java类型

Z

boolean

B

byte

C

char

S

short

I

int

J

long

F

float

D

double

L fully-qualified-class ;

fully-qualified-class

[ type

type[]

( arg-types ) ret-type

method type

例如,以下方法

long f (int n, String s, int[] arr); 

它的签名为:

(ILjava/lang/String;[I)J 

I:表示的是第一个参数类型int

Ljava/lang/String;:表示第二个参数类型是String

[I:表示第三个参数类型为int[]

J:表示返回值为long类型

3.JNI注册方式

JNI函数的注册方式分为静态注册和动态注册

3.1静态注册

静态注册一般是在java文件里定义一个native方法,然后在对应的cpp文件里实现就可以了,JVM会按照默认映射规则去找对应的native方法。

java文件代码:

package com.example.jindemo

Public class JniTest {
    static
    {
        System.loadLibrary ("jni_test");//加载so库
    }

    public native void testJniMethod();
}

JniTest类在加载的时候会加载静态代码块,调用System.loadLibrary()方法来加载native库jni_test,在这个库中实现了testJniMethod()方法。

C/C++文件代码:

jnitest.cpp

#include <jni.h>

extern "C" {

/**
 * Native (JNI) implementation of JniTest.testJniMethod()
 */
JNIEXPORT void JNICALL Java_com_example_jindemo_JniTest_testJniMethod(
        JNIEnv* env, jobject obj) {

}

}

上面例子中java文件中JniTest.testJniMethod()方法对应C/C++文件的Java_com_example_jindemo_JniTest_testJniMethod方法,在调用Java层的native方法时,JVM会从JNI库中寻找默认的方法并调用,按照如下规则匹配对应的C/C++函数:

Java_方法的完整路径(中间用下划线隔开)

上面testJniMethod的完整路径就是com.example.jindemo.JniTest.testJniMethod,所以会匹配Java_com_example_jindemo_JniTest_testJniMethod方法。

3.2动态注册

虽然静态注册比较简单,但是如果修改了java层的包名或者类名,那么也要修改对应的C/C++文件。

而动态注册不需要按照特定的规则去实现native方法,只需要在C/C++文件里声明对应的函数即可,相当于直接告诉JVM,java层对应的native函数是C/C++文件里的哪个函数。

以原生PowerManagerService.java里的native方法为例:

    frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

    private native void nativeInit();
    private static native void nativeAcquireSuspendBlocker(String name);
    private static native void nativeReleaseSuspendBlocker(String name);
    private static native void nativeSetAutoSuspend(boolean enable);
    private static native void nativeSetPowerBoost(int boost, int durationMs);
    private static native boolean nativeSetPowerMode(int mode, boolean enabled);
    private static native boolean nativeForceSuspend();

com_android_server_power_PowerManagerService.cpp里声明对应的规则:

frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp

static const JNINativeMethod gPowerManagerServiceMethods[] = {//方法对应关系表
        /* name, signature, funcPtr */
        {"nativeInit", "()V", (void*)nativeInit},
        {"nativeAcquireSuspendBlocker", "(Ljava/lang/String;)V",
         (void*)nativeAcquireSuspendBlocker},
        {"nativeForceSuspend", "()Z", (void*)nativeForceSuspend},
        {"nativeReleaseSuspendBlocker", "(Ljava/lang/String;)V",
         (void*)nativeReleaseSuspendBlocker},
        {"nativeSetAutoSuspend", "(Z)V", (void*)nativeSetAutoSuspend},
        {"nativeSetPowerBoost", "(II)V", (void*)nativeSetPowerBoost},
        {"nativeSetPowerMode", "(IZ)Z", (void*)nativeSetPowerMode},
};


int register_android_server_PowerManagerService(JNIEnv* env) {
    int res = jniRegisterNativeMethods(env, "com/android/server/power/PowerManagerService",
            gPowerManagerServiceMethods, NELEM(gPowerManagerServiceMethods));
    (void) res;  // Faked use when LOG_NDEBUG.
    LOG_FATAL_IF(res < 0, "Unable to register native methods.");

    // Callbacks

    jclass clazz;
    FIND_CLASS(clazz, "com/android/server/power/PowerManagerService");

    GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivityFromNative, clazz,
            "userActivityFromNative", "(JIII)V");

    // Initialize
    for (int i = 0; i <= USER_ACTIVITY_EVENT_LAST; i++) {
        gLastEventTime[i] = LLONG_MIN;
    }
    gPowerManagerServiceObj = NULL;
    return 0;
}

Java与JNI通过JNINativeMethod的结构来建立联系,结构如下:

typedef struct {
    const char* name;
    const char* signature;
    void*       fnPtr;
} JNINativeMethod;

 

name:对应Java文件中函数的名字

signature:函数签名,对应了函数的参数和返回值

fnPtr:指向对应的C函数

{"nativeInit", "()V", (void*)nativeInit},的意思是java中的nativeInit函数,对应C库中的nativeInit函数,这个函数没有参数,并且没有返回值。

然后通过jniRegisterNativeMethods()方法注册,第二个参数对应着java类,即com/android/server/power/PowerManagerService;第三个参数为方法对应关系表。

看下register_android_server_PowerManagerService这个方法是在哪里调用的:

frameworks/base/services/core/jni/onload.cpp

xtern "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);
    ....
}

当java通过System.loadLibrary加载完JNI动态库后,同时会调用C库中的JNI_OnLoad的函数

,从而使java层可以通过调用声明的方法,来调用对应的C库中的方法。

这个库是在SystemServer里加载的:

frameworks/base/services/java/com/android/server/SystemServer.java

    private void run() {
        TimingsTraceAndSlog t = new TimingsTraceAndSlog();
        try {
            t.traceBegin("InitBeforeStartServices");
            ......
            // Initialize native services.
            System.loadLibrary("android_servers");
        .......
    }

4.JNIEnv

JNIEnv全称Java Native Interface Environment,Java本地接口环境。JNIEnv主要用于native函数调用java层函数、变量、对象等。

JNIEnv是和线程绑定的,只能在创建它的线程中使用,不能跨线程传递,因此,不同线程的JNIEnv是彼此独立的。

4.1获取JNIEnv

在同一个线程下可以直接通过参数获取JNIEnv,参考上述静态注册的例子

jnitest.cpp

#include <jni.h>

extern "C" {

/**
 * Native (JNI) implementation of JniTest.testJniMethod()
 */
JNIEXPORT void JNICALL Java_com_example_jindemo_JniTest_testJniMethod(
        JNIEnv* env, jobject obj) {

}

}

java层调用jni函数的时候始终处于同一线程,所以jni可以直接操作从java层传递过来的JNIEnv对象。

跨进程使用AttachCurrentThread()方法重新创建JNIEnv:

 JNIEnv* env = AttachCurrentThread();

4.2JNIEnv的主要方法

JNIEnv主要用于native函数调用java层函数、变量、对象等,它的主要方法有:

函数名功能
jclass FindClass(JNIEnv *env, const char *name)获取类
jclass GetObjectClass(JNIEnv *env, jobject obj)返回对象的类
jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig)返回类或接口的实例(非静态)方法的方法 ID
jmethodID GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig)返回类的静态方法的方法 ID
jobject NewGlobalRef(JNIEnv *env, jobject obj)创建对 obj 参数所引用的对象的新全局引用
jobject NewObject()构造一个新的 Java 对象
jstring NewString(JNIEnv *env, const jchar *unicodeChars, jsize len)从 Unicode 字符数组构造一个新的 java.lang.String 对

其他方法参考:

Java Native Interface Specification: Contents

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值