真正重要的收获,往往都来自持续艰难的思考。
该文章首发于微信公众号“字节流动”
本博客 NDK 开发系列文章:
- NDK 编译的三种方式
- NDK 开发中引入第三方静态库和动态库
- NDK 开发中 Native 与 Java 交互
- NDK POSIX 多线程编程
- NDK Android OpenSL ES 音频采集与播放
- NDK FFmpeg 编译
- NDK FFmpeg 音视频解码
- NDK 直播流媒体服务器搭建
- NDK 直播推流与引流
- NDK 开发中快速定位 Crash 问题
- NDK 开发中 Native 方法的静态注册与动态注册
Native 方法的静态注册
NDK 开发中通过 javah -jni
命令生成的包含 JNI 的头文件,接口的命名方式一般是 Java_<PackageName>_<ClassName>_<MethodName>
,程序执行时系统会根据这种命名规则来调用对应的 Native 方法,这种注册方式称之为静态注册。
package com.haohao.framework;
public class NDKFramework {
private native int native_CreateFramework(String packageName);
private native void native_DestroyFramework();
}
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_haohao_framework_NDKFramework */
#ifndef _Included_com_haohao_framework_NDKFramework
#define _Included_com_haohao_framework_NDKFramework
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_haohao_framework_NDKFramework
* Method: native_CreateFramework
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_haohao_framework_NDKFramework_native_1CreateFramework
(JNIEnv *, jobject, jstring);
/*
* Class: com_haohao_framework_NDKFramework
* Method: native_DestroyFramework
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_haohao_framework_NDKFramework_native_1DestroyFramework
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
静态注册方式的优点:方便简单,IDE (高版本的 AndroidStudio)就可以自动帮你完成;缺点:JNI (Native 方法)名字过长,可读性差,由于命名规则的限制,不能灵活改变。
Native 方法的动态注册
由于静态注册存在命名局限性,生产环境中一般不采用静态注册的方式。动态注册的优点是可以自由命名 Native 方法,缺点是如果 Native 方法过多,操作比较麻烦。
动态注册的时机是在加载函数库(.a 或 .so)的时候进行注册,即在 JNI_OnLoad
方法里进行注册。
重新命名后的 Native 方法:
#include <jni.h>
#include <string>
#define CLASS_NAME_NDK_FRAMEWORK "com/haohao/framework/NDKFramework"
extern "C"
JNIEXPORT jint JNICALL
CreateFramework(JNIEnv *env, jobject instance, jstring jPackageName)
{
LOGCATE("native_CreateFramework");
jint jRet = JNI_ERR;
const char *packageName = env->GetStringUTFChars(jPackageName, 0);
//if(NDKFramework::CreateFramework(packageName) != NULL)
// jRet = JNI_OK;
env->ReleaseStringUTFChars(jPackageName, packageName);
return jRet;
}
extern "C"
JNIEXPORT void JNICALL
DestroyFramework(JNIEnv *env, jobject instance)
{
LOGCATE("native_DestroyFramework");
//NDKFramework::DestroyFramework();
}
定义 Native 方法数组:
//{"Java 方法名", "JNI 签名", "重命名的 Native 方法"}
static JNINativeMethod g_NDKFrameMethods[] = {
{ "native_CreateFramework", "(Ljava/lang/String;)I", (void *)CreateFramework},
{ "native_DestroyFramework", "()V", (void *)DestroyFramework}
};
//定义注册函数
static int RegisterNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* methods, int methodNum)
{
LOGCATE("RegisterNativeMethods");
jclass clazz = env->FindClass(className);
if (clazz == NULL)
{
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, methods, methodNum) < 0)
{
return JNI_FALSE;
}
return JNI_TRUE;
}
在 JNI_OnLoad
方法里进行注册:
extern "C" jint JNI_OnLoad(JavaVM *jvm, void *p)
{
LOGCATE("================ JNI_OnLoad ================");
jint jniRet = JNI_ERR;
JNIEnv *env = NULL;
if (jvm->GetEnv((void **)(&env), JNI_VERSION_1_6) != JNI_OK)
return jniRet;
jint regRet = RegisterNativeMethods(env, CLASS_NAME_NDK_FRAMEWORK, g_NDKFrameMethods, sizeof(g_NDKFrameMethods) /
sizeof(g_NDKFrameMethods[0]));
if(regRet != JNI_TRUE)
return JNI_ERR;
return JNI_VERSION_1_6;
}
以上 3 步便可实现动态注册。