在Android 中 动态注册 JNI 函数

在Android 中 动态注册 JNI 函数

写在最前面,本人没多写过文章,有什么建议或意见请告诉我

如果想学习更多的jni,看这篇文章

↑↑↑↑↑↑↑↑ 本文的表就是从这抄的 ↑↑↑↑↑↑↑↑
Android的JNI_OnLoad简介与应用

1. 普通的使用方法

在java类中 :

package com.fzy.test;

public class JNITest {
    //定义一个方法,该方法在C中实现
    public native void testMethod();

}

在Native中:

JNIEXPORT void JNICALL Java_com_fzy_test_JNITest_testMethod(JNIEnv *, jobject) {
    *******
}

2. 动态注册

2.2 示例代码 (只列出重点部分)

java层 :

package com.fzy.learn.nativelib;


public class JNIClass {

    public static native boolean testMethod(String info);

    public static native boolean testMethod2(String data);

    public static native String testMethod3(String data);
}

native 层:

/*----------------- 需要注册的函数 ------------------*/
// 对应 testMethod
jboolean needRegistMethod(JNIEnv *env, jobject /* this */,jstring info) {
    if (NULL == info) {
        return JNI_FALSE;
    }

    return JNI_TRUE;
}

// 对应 testMethod2
jboolean needRegistMethod2(JNIEnv *env,jobject /* this */, jstring data) {
    if (nullptr == data ) {
        return JNI_FALSE;
    }

    return JNI_TRUE;
}
// 对应 testMethod3
jstring needRegistMethod3(JNIEnv *env,jobject /* this */, jstring data) {
    if (NULL == data) {
        std::string hello = "参数为空";
        return env->NewStringUTF(hello.c_str());
    }
    return data;
}

JNI 注册方法

// java层的类名
static const char *classPath = "com/fzy/learn/nativelib/JNIClass";
// 需要注册的方法的信息,在此处我们可以对 函数名 和 参数 进行加密,在注册之前进行解密
static JNINativeMethod gs_needRegestMethods[] =
        {
                {"testMethod",  "(Ljava/lang/String;)Z", (void *) needRegistMethod},
                {"testMethod2", "(Ljava/lang/String;)Z", (void *) needRegistMethod2},
                {"testMethod3", "(Ljava/lang/String;)Ljava/lang/String;", (void *) needRegistMethod3},
        };


static int RegisterNatives(JNIEnv* env)
{
  int result = -1;
  // 获取到需要注册的函数的数量
  int max = sizeof(gs_needRegestMethods)/sizeof(gs_needRegestMethods[0x00]);
  // 找到对应的类(需要判断是否存在)
  jclass cls = env->FindClass(classPath);
  // 对函数列表进行注册 但不建议这样做 因为无法精确到对每个函数进行控制 
  result = env->RegisterNatives(cls, localNathiveMethod , max);
  // 注册结果
  return result;
}

// 实现 jni.h 中的 JNI_OnLoad 方法,加载动态库时自行调用(见下文详解)
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    jint result = -1;
    JNIEnv *env = NULL;
    
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        goto error;
    }
  
    result = RegisterNatives(env);
    if (JNI_OK != result) {
        goto error;
    }
    
    error:
    return result;
}


2.3 主要方法

2.3.1 注册方法

在 JNIEnv 中 提供有 RegisterNatives 方法 定义如下

jint RegisterNatives(JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods);

其中 JNINativeMethod 的定义为

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

参数详解:

  • clazz —> 代表java层包含native方法的类名

  • JNINativeMethod 注册方法的信息

    • name —> 代表java层中native方法的名称 例如上文中的 testMethod
    • signature —> 代表java层native方法参数的缩写 (JLjava/lang/String;)Z 详细对应符号见 表 1
    • fnPtr —> native 层中与java层对应的方法的函数 needRegistMethod
  • nMethods —> 需要注册的函数的数量,写少了就等着报错吧

表 1
Java 类型符号
booleanZ
byteB
charC
shortS
intI
longJ
floatF
doubleD
voidV
objects对象Lfully-qualified-class-name;L类名
Arrays数组[array-type [数组类型
methods方法(argument-types)return-type(参数类型)返回类型
2.3.2 入口方法:

在jni.h文件中,定义了如下的方法

/*
 * Prototypes for functions exported by loadable shared libs.  These are
 * called by JNI, not provided by JNI.
 */
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved);
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved);

本次的主角是 JNI_OnLoad,在我们学习jni的是,大部分给的示例都是例如我们在文章开头处所使用的那种方式,他会自动关联到的对应 native 方法,但实际上,这种关联到对应方法的过程,也是调用了__JNI_OnLoad__去实现的,

以下是从网络上找到的关于两种不同加载的介绍:

Android系统加载JNI Lib的方式有如下两种:

  1. 通过JNI_OnLoad
  2. 如果JNI Lib没有定义JNI_OnLoad,则dvm调用dvmResolveNativeMethod进行动态解析

System.loadLibrary调用流程如下所示:


System.loadLibrary->
   Runtime.loadLibrary->(Java)
     nativeLoad->(C: java_lang_Runtime.cpp)
       Dalvik_java_lang_Runtime_nativeLoad->
          dvmLoadNativeCode-> (dalvik/vm/Native.cpp)
              1) dlopen(pathName, RTLD_LAZY) (把.so mmap到进程空间,并把func等相关信息填充到soinfo中)
              2) dlsym(handle, "JNI_OnLoad")
              3) JNI_OnLoad->
                      RegisterNatives->
                         dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,
                                                const char* signature, void* fnPtr)->
                            dvmUseJNIBridge(method, fnPtr)->  (method->nativeFunc = func)

此处仅为说明 JNI_OnLoad 是我们动态注册的入口,关于这个函数更多的使用和流程,可以转去看我开篇放的《Android的JNI_OnLoad简介与应用》,他写的比较详尽。

将我们的注册方法,在此函数中调用,就可以在加载动态库的时候,把我们的native方法关联起来。 但问题也和明显,当java层的一个方法,没能成功的和c层的函数关联起来,在使用时就会报错:java.lang.UnsatisfiedLinkError: No implementation found for ***


代码链接: 在路上了,稍等一两年


  • 9
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值