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
View Code
复制代码

依据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
}
复制代码

 

posted @   牧 天  阅读(1921)  评论(1编辑  收藏  举报
编辑推荐:
· 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 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示