JNI机制
JNI函数注册有2种方式:静态注册,动态注册
android大部分是属于动态注册。
静态注册:
JNI本身是使用C++语言实现,所谓静态注册就是直接去声明引用外部的某个函数,用静态代码来注册。
javah命令是用于根据JAVA本地方法,生成对应的c语言头文件及相应的stub文件的命令
下面举例来说明java如何调用C++
$~/jni_test/one$ cat A.java package one; public class A{ static{ System.loadLibrary("hello"); } public native void printHello(); public static void main(String s[]){ System.out.println("call native method:"); new A().printHello(); } }
$~/jni_test$ javac one/A.java //生成one/A.class
$~/jni_test$ javah one.A //生成one_A.h
$~/jni_test$ ls
one one_A.h

// one_A.h /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class one_A */ #ifndef _Included_one_A #define _Included_one_A #ifdef __cplusplus extern "C" { #endif /* * Class: one_A * Method: printHello * Signature: ()V */ JNIEXPORT void JNICALL Java_one_A_printHello (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
依据one_A.h写好one_A.cpp
//one_A.cpp #include <stdio.h> #include <jni.h> #include "one_A.h" JNIEXPORT void JNICALL Java_one_A_printHello(JNIEnv *, jobject) { printf("Hello world ! \n"); }
$~/jni_test$ g++ -I /home/daichenghui/soft/jdk1.7.0_60/include -I /home/daichenghui/soft/jdk1.7.0_60/include/linux -fPIC -shared -o libhello.so one_A.cpp //生成libhello.so
~/jni_test$ ls
libhello.so one one_A.cpp one_A.h
$~/jni_test$ export LD_LIBRARY_PATH=~/jni_test //这个可以通过设置环境变量来形成
$~/jni_test$ java one.A
call native method:
Hello world !
动态注册:
程序初始化后调用代码来进行注册。
JNIEnv是一个与线程相关的变量,不同线程的JNIEnv彼此独立。JavaVM是虚拟机在JNI层的代表,在一个虚拟机进程中只有一个JavaVM,因此该进程的所有线程都可以使用这个JavaVM。当后台线程需要调用JNI native时,在native库中使用全局变量保存JavaVM尤为重要,这样使得后台线程能通过JavaVM获得JNIEnv。native程序中频繁使用JNIEnv*和JavaVM*。
----------------------------------------
Java (MediaPlayer)
JNI (media_jni.so)
Native (libmedia.so)
------------------------------------
public class MediaPlayer { static { //1.原则上在调用native方法前加载这个库,这个库加载完后会自动调用JNI_OnLoad函数 //Linux平台扩展成media_jni.so, windows平台扩展为media_jni.dll //android不同平台开发包也不一样,应该就是因为不同平台库不一样引起的 //不同平台库不一样是因为API接口不一样,比如打开文件Windows上打开文件是openFile, 而Linux上是open System.loadLibrary("media_jni");//貌似MediaScanner中也加载了这个库,会重复加载吗?一个线程只加载一次?还是多次加载?
//这个函数在Natvie层做些初始化操作:“static fields_t fields;”。
//主要内容是:1. 通过引用的方式记住java层部分字段。2. 记住了java层某些JNI层需要调用函数的ID,这样JNI层也可以调用java层的函数了。
//这些变量的ID和方法的ID保存起来,以后每次调用的时候就非常方便。这就是native_init()的作用。 native_init(); } //2.声明native修饰的函数 private static native final void native_init(); private native final void native_setup(Object mediaplayer_this); private native void _start() throws IllegalStateException; private native void _stop() throws IllegalStateException; private native void _pause() throws IllegalStateException; private native void _setDataSource(FileDescriptor fd, long offset, long length); public MediaPlayer() { ............. /* Native setup requires a weak reference to our object. * It's easier to create it here than in C++. */ native_setup(new WeakReference<MediaPlayer>(this)); } }
再看看android_media_MediaPlayer.cpp在加载库时如何调用register_android_media_MediaScanner()
//frameworks/base/media/jni/android_media_MediaPlayer.cpp
//这个模块注册了很多类的方法,各个类在JNI对应的类
extern int register_android_media_ImageReader(JNIEnv *env);
extern int register_android_media_Crypto(JNIEnv *env);
extern int register_android_media_DLNAHTTPConnection(JNIEnv *env);
extern int register_android_media_Drm(JNIEnv *env);
extern int register_android_media_MediaCodec(JNIEnv *env);
extern int register_android_media_MediaExtractor(JNIEnv *env);
extern int register_android_media_MediaCodecList(JNIEnv *env);
extern int register_android_media_MediaHTTPConnection(JNIEnv *env);
extern int register_android_media_MediaMetadataRetriever(JNIEnv *env);
extern int register_android_media_MediaMuxer(JNIEnv *env);
extern int register_android_media_MediaRecorder(JNIEnv *env);
extern int register_android_media_MediaScanner(JNIEnv *env);
extern int register_android_media_ResampleInputStream(JNIEnv *env);
extern int register_android_media_MediaProfiles(JNIEnv *env);
extern int register_android_media_AmrInputStream(JNIEnv *env);
extern int register_android_mtp_MtpDatabase(JNIEnv *env);
extern int register_android_mtp_MtpDevice(JNIEnv *env);
extern int register_android_mtp_MtpServer(JNIEnv *env);
//参数是JNIEnv,它代表一个线程。也就是说哪个线程用到这个类,那么就把这个类对应的方法动态注册。
static int register_android_media_MediaPlayer(JNIEnv *env)
{
//给本地层MediaPlayer类注册方法,从而让java层调用时找到读应的函数地址
//AndroidRuntime::registerNativeMethods(JNIEnv * env, String className, JNINativeMethod gMethods, int numMethods);
return AndroidRuntime::registerNativeMethods(env, "android/media/MediaPlayer", gMethods, NELEM(gMethods));
}
//用System.loadLibrary()加载完JNI动态库后,就会查找该库中叫JNI_OnLoad()的函数
//动态注册就是在这个函数里面做的。
jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
// JavaVM* vm虚拟机在JNI层的代表,每个java进程只有一个。
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("ERROR: GetEnv failed\n");
goto bail;
}
assert(env != NULL);
if (register_android_media_DLNAHTTPConnection(env) < 0) {
ALOGE("ERROR: DLNAHTTPConnection native registration failed");
goto bail;
}
if (register_android_media_ImageReader(env) < 0) {
ALOGE("ERROR: ImageReader native registration failed");
goto bail;
}
if (register_android_media_MediaPlayer(env) < 0) {
ALOGE("ERROR: MediaPlayer native registration failed\n");
goto bail;
}
if (register_android_media_MediaRecorder(env) < 0) {
ALOGE("ERROR: MediaRecorder native registration failed\n");
goto bail;
}
if (register_android_media_MediaScanner(env) < 0) {
ALOGE("ERROR: MediaScanner native registration failed\n");
goto bail;
}
if (register_android_media_MediaMetadataRetriever(env) < 0) {
ALOGE("ERROR: MediaMetadataRetriever native registration failed\n");
goto bail;
}
if (register_android_media_AmrInputStream(env) < 0) {
ALOGE("ERROR: AmrInputStream native registration failed\n");
goto bail;
}
if (register_android_media_ResampleInputStream(env) < 0) {
ALOGE("ERROR: ResampleInputStream native registration failed\n");
goto bail;
}
if (register_android_media_MediaProfiles(env) < 0) {
ALOGE("ERROR: MediaProfiles native registration failed");
goto bail;
}
if (register_android_mtp_MtpDatabase(env) < 0) {
ALOGE("ERROR: MtpDatabase native registration failed");
goto bail;
}
if (register_android_mtp_MtpDevice(env) < 0) {
ALOGE("ERROR: MtpDevice native registration failed");
goto bail;
}
if (register_android_mtp_MtpServer(env) < 0) {
ALOGE("ERROR: MtpServer native registration failed");
goto bail;
}
if (register_android_media_MediaCodec(env) < 0) {
ALOGE("ERROR: MediaCodec native registration failed");
goto bail;
}
if (register_android_media_MediaExtractor(env) < 0) {
ALOGE("ERROR: MediaCodec native registration failed");
goto bail;
}
if (register_android_media_MediaMuxer(env) < 0) {
ALOGE("ERROR: MediaMuxer native registration failed");
goto bail;
}
if (register_android_media_MediaCodecList(env) < 0) {
ALOGE("ERROR: MediaCodec native registration failed");
goto bail;
}
if (register_android_media_Crypto(env) < 0) {
ALOGE("ERROR: MediaCodec native registration failed");
goto bail;
}
if (register_android_media_Drm(env) < 0) {
ALOGE("ERROR: MediaDrm native registration failed");
goto bail;
}
if (register_android_media_MediaHTTPConnection(env) < 0) {
ALOGE("ERROR: MediaHTTPConnection native registration failed");
goto bail;
}
/* success -- return valid version number */
result = JNI_VERSION_1_4;
bail:
return result;
}
总的来说就是:一个线程中如果用到哪个类,那么就在这个线程中注册这个类里面的方法。
JNINativeMethod这个结构体来保存java native函数和JNI函数的一一对应关系。
typedef struct{
const char * name;//java中函数的名字例如"scan" 或者"native_init"
const char* signature; /*签名信息:描述了函数的参数和返回值*/
void * fnPtr; //JNI层对应函数的函数指针
} JNINativeMethod;
gMethods结构体数组保存了当前类里面需要注册的函数
//frameworks/base/media/jni/android_media_MediaPlayer.cpp static JNINativeMethod gMethods[] = {
{
"nativeSetDataSource",
"(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;"
"[Ljava/lang/String;)V",
(void *)android_media_MediaPlayer_setDataSourceAndHeaders
},
{"_setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD},
{"_setVideoSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaPlayer_setVideoSurface},
{"_prepare", "()V", (void *)android_media_MediaPlayer_prepare},
{"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync},
{"_start", "()V", (void *)android_media_MediaPlayer_start},
{"_stop", "()V", (void *)android_media_MediaPlayer_stop},
{"getVideoWidth", "()I", (void *)android_media_MediaPlayer_getVideoWidth},
{"getVideoHeight", "()I", (void *)android_media_MediaPlayer_getVideoHeight},
{"seekTo", "(I)V", (void *)android_media_MediaPlayer_seekTo},
{"_pause", "()V", (void *)android_media_MediaPlayer_pause},
{"isPlaying", "()Z", (void *)android_media_MediaPlayer_isPlaying},
{"getCurrentPosition", "()I", (void *)android_media_MediaPlayer_getCurrentPosition},
{"getDuration", "()I", (void *)android_media_MediaPlayer_getDuration},
{"_release", "()V", (void *)android_media_MediaPlayer_release},
{"_reset", "()V", (void *)android_media_MediaPlayer_reset},
{"_setAudioStreamType", "(I)V", (void *)android_media_MediaPlayer_setAudioStreamType},
{"_getAudioStreamType", "()I", (void *)android_media_MediaPlayer_getAudioStreamType},
{"setParameter", "(ILandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_setParameter},
{"setLooping", "(Z)V", (void *)android_media_MediaPlayer_setLooping},
{"isLooping", "()Z", (void *)android_media_MediaPlayer_isLooping},
{"_setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume},
{"native_invoke", "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke},
{"native_setMetadataFilter", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_setMetadataFilter},
{"native_getMetadata", "(ZZLandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_getMetadata},
{"native_init", "()V", (void *)android_media_MediaPlayer_native_init},
{"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup},
{"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize},
{"getAudioSessionId", "()I", (void *)android_media_MediaPlayer_get_audio_session_id},
{"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id},
{"_setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
{"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer_attachAuxEffect},
{"native_pullBatteryData", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_pullBatteryData},
{"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I", (void *)android_media_MediaPlayer_setRetransmitEndpoint},
{"setNextMediaPlayer", "(Landroid/media/MediaPlayer;)V", (void *)android_media_MediaPlayer_setNextMediaPlayer},
{"_suspend", "()Z", (void *)android_media_MediaPlayer_suspend},
{"_resume", "()Z", (void *)android_media_MediaPlayer_resume},
{"_setSpeed", "(F)V", (void *)android_media_MediaPlayer_setSpeed},
{"_setDseeHxEnabled", "(Z)V", (void *)android_media_MediaPlayer_setDseeHxEnabled},
};
register_android_media_MediaScanner()最后调到这个类里面的函数
/libnativehelper/JNIHelp.cpp
extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods) { JNIEnv* e = reinterpret_cast<JNIEnv*>(env); scoped_local_ref<jclass> c(env, findClass(env, className)); if (c.get() == NULL) { char* msg; asprintf(&msg, "Native registration unable to find class '%s'; aborting...", className); e->FatalError(msg); }
//将这个类中的方法注册到这个线程环境里。
//c.get()是类名(含包名),gMethods中只有方法名非全路径 if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) { char* msg; asprintf(&msg, "RegisterNatives failed for '%s'; aborting...", className); e->FatalError(msg); } return 0; }
-------------------------------------------------------------------------------------------
#if defined(__cplusplus) typedef _JNIEnv JNIEnv; typedef _JavaVM JavaVM; #else typedef const struct JNINativeInterface* JNIEnv; typedef const struct JNIInvokeInterface* JavaVM; #endif struct JNINativeInterface { void* reserved0; void* reserved1; void* reserved2; void* reserved3; jint (*GetVersion)(JNIEnv *); jclass (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*, jsize); jclass (*FindClass)(JNIEnv*, const char*); .............. }
对于C语言代码而言 typedef const struct JNINativeInterface* JNIEnv; 也就是说JNIEnv本身是一个指针。
对于C++而言 typedef _JNIEnv JNIEnv; 也就是说JNIEnv只是一个结构体
struct _JNIEnv { /* do not rename this; it does not seem to be entirely opaque */ const struct JNINativeInterface* functions; #if defined(__cplusplus) jint GetVersion() { return functions->GetVersion(this); } jclass DefineClass(const char *name, jobject loader, const jbyte* buf, jsize bufLen) { return functions->DefineClass(this, name, loader, buf, bufLen); } .......... #endif }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架