JNI 函数注册

静态注册:
1.java层的native方法, 如:public native void close();
2.头文件:JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close(JNIEnv *, jobject);
3.c文件:JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close (JNIEnv *env, jobject thiz) { //具体实现 }
规律: 固定开头 Java_ + native方法声明的包名(), .变为_ , _变为_1 + 类名 + 声明的方法名。

动态注册:
我们在调用 System.loadLibrary的时候,会在C/C++文件中回调一个名为JNI_OnLoad ()的函数, 
在JNI_Onload()函数中通过JNI中提供的RegisterNatives()方法来将C/C++方法和java方法对应起来(注册)。

    // jni头文件 #include <jni.h>#include <cassert>#include <cstdlib>#include <iostream>using namespace std;//native 方法实现jint get_random_num(){return rand();}static JNINativeMethod getMethods[] = {{"getRandomNum","()I",(void*)get_random_num},};JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){JNIEnv* env = NULL;//获取JNIEnvif (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {return -1;}assert(env != NULL);//注册函数 registerNatives ->registerNativeMethods ->env->RegisterNativesif(!registerNatives(env)){return -1;}//返回jni 的版本 return JNI_VERSION_1_6;}static int registerNatives(JNIEnv* env){//指定类的路径,通过FindClass 方法来找到对应的类const char* className  = "com/example/wenzhe/myjni/JniTest";return AndroidRuntime::registerNativeMethods(env,className,getMethods, sizeof(getMethods)/ sizeof(getMethods[0]));}

上面的方法需要我们在c文件中实现,最后调用JNIEnv的RegisterNatives方法进行注册。

需要注册的函数列表,放在JNINativeMethod 类型的数组中,以后如果需要增加函数,只需在这里添加就行了

参数:
1.java中用native关键字声明的函数名
2.签名(传进来参数类型和返回值类型的说明)  javap -s -p xxx.class 生成函数签名。
3.C/C++中对应函数的函数名(地址)

JNIEnv介绍:

线程相关的代表JNI环境的结构体。通过JNIEnv可以调用java的函数、操作jobject对象等。
调用getFieldID获取成员变量,getMethodID获取成员函数,通过CallVoidMethod(有一系列CallTypeMethod、CallStaticTypeMethod)来使用这两个值。

看下实例:

AudioTrack.java对应的native类是android_media_AudioTrack.cpp

frameworks\base\core\jni\android_media_AudioTrack.cpp:

int register_android_media_AudioTrack(JNIEnv *env)
{// must be firstint res = RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));javaAudioTrackFields.nativeTrackInJavaObj = NULL;javaAudioTrackFields.postNativeEventInJava = NULL;// Get the AudioTrack classjclass audioTrackClass = FindClassOrDie(env, kClassPathName);// Get the postEvent methodjavaAudioTrackFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,audioTrackClass, JAVA_POSTEVENT_CALLBACK_NAME,"(Ljava/lang/Object;IIILjava/lang/Object;)V");// Get the variables fields//      nativeTrackInJavaObjjavaAudioTrackFields.nativeTrackInJavaObj = GetFieldIDOrDie(env,audioTrackClass, JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME, "J");//      jniDatajavaAudioTrackFields.jniData = GetFieldIDOrDie(env,audioTrackClass, JAVA_JNIDATA_FIELD_NAME, "J");//      fieldStreamTypejavaAudioTrackFields.fieldStreamType = GetFieldIDOrDie(env,audioTrackClass, JAVA_STREAMTYPE_FIELD_NAME, "I");env->DeleteLocalRef(audioTrackClass);// initialize PlaybackParams field infogPlaybackParamsFields.init(env);gVolumeShaperFields.init(env);return res;
}

看到这个方法,调用了RegisterMethodsOrDie和FindClassOrDie这些,跟进去看

frameworks\base\core\jni\core_jni_helpers.h:

static inline int RegisterMethodsOrDie(JNIEnv* env, const char* className,const JNINativeMethod* gMethods, int numMethods) {int res = AndroidRuntime::registerNativeMethods(env, className, gMethods, numMethods);LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");return res;
}static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) {jclass clazz = env->FindClass(class_name);LOG_ALWAYS_FATAL_IF(clazz == NULL, "Unable to find class %s", class_name);return clazz;
}static inline jmethodID GetMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name,const char* method_signature) {jmethodID res = env->GetMethodID(clazz, method_name, method_signature);LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find method %s", method_name);return res;
}

可以看到这个就是一个工具类,RegisterMethodsOrDie用来动态注册JNI方法的,而FindClassOrDie和GetMethodIDOrDie就是用来获取Java层的类对象和函数对象的。

而register_android_media_AudioTrack这个方法什么时候被调用呢

frameworks\base\core\jni\AndroidRuntime.cpp:

extern int register_android_media_AudioAttributes(JNIEnv *env);
....static const RegJNIRec gRegJNI[] = {REG_JNI(register_android_media_AudioEffectDescriptor),REG_JNI(register_android_media_AudioSystem),REG_JNI(register_android_media_AudioRecord),REG_JNI(register_android_media_AudioTrack),REG_JNI(register_android_media_AudioAttributes),....
}/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{ATRACE_NAME("RegisterAndroidNatives");androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);env->PushLocalFrame(200);if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {env->PopLocalFrame(NULL);return -1;}env->PopLocalFrame(NULL);return 0;
}

上面的gRegJNI作为register_jni_procs方法的参数。

#ifdef NDEBUG#define REG_JNI(name)      { name }struct RegJNIRec {int (*mProc)(JNIEnv*);};
#else#define REG_JNI(name)      { name, #name }struct RegJNIRec {int (*mProc)(JNIEnv*);const char* mName;};
#endiftypedef void (*RegJAMProc)();static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{for (size_t i = 0; i < count; i++) {if (array[i].mProc(env) < 0) {
#ifndef NDEBUGALOGD("----------!!! %s failed to load\n", array[i].mName);
#endifreturn -1;}}return 0;
}

array[i].mProc(env)就直接相当于register_android_media_AudioTrack(env),所以在这里就进行了动态注册。

JAVA层如何使用这个AudioTrack呢

frameworks\base\media\java\android\media\AudioTrack.java:

    private AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,int mode, int sessionId, boolean offload)throws IllegalArgumentException {super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_JAM_AUDIOTRACK);
.....// native initializationint initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/,offload);if (initResult != SUCCESS) {loge("Error code "+initResult+" when initializing AudioTrack.");return; // with mState == STATE_UNINITIALIZED}......if (mDataLoadMode == MODE_STATIC) {mState = STATE_NO_STATIC_DATA;} else {mState = STATE_INITIALIZED;}baseRegisterPlayer();}

我们看这个类的构造函数,这里调用了一个native方法。

native_setup(new WeakReference<AudioTrack>(this), mAttributes,
                sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
                mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/,
                offload);

根据AudioTrack的getMethods数组,我们就可以知道他对应那个方法了。

{"native_setup",     "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZ)I",
                                         (void *)android_media_AudioTrack_setup}

可以知道对应的是 android_media_AudioTrack_setup

static jint
android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa,jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask,jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession,jlong nativeAudioTrack, jboolean offload) {sp<AudioTrack> lpTrack = 0;if (jSession == NULL) {ALOGE("Error creating AudioTrack: invalid session ID pointer");return (jint) AUDIO_JAVA_ERROR;}jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);if (nSession == NULL) {ALOGE("Error creating AudioTrack: Error retrieving session id pointer");return (jint) AUDIO_JAVA_ERROR;}audio_session_t sessionId = (audio_session_t) nSession[0];env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);nSession = NULL;AudioTrackJniStorage* lpJniStorage = NULL;jclass clazz = env->GetObjectClass(thiz);if (clazz == NULL) {ALOGE("Can't find %s when setting up callback.", kClassPathName);return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;}// if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one.if (nativeAudioTrack == 0) {if (jaa == 0) {ALOGE("Error creating AudioTrack: invalid audio attributes");return (jint) AUDIO_JAVA_ERROR;}if (jSampleRate == 0) {ALOGE("Error creating AudioTrack: invalid sample rates");return (jint) AUDIO_JAVA_ERROR;}int* sampleRates = env->GetIntArrayElements(jSampleRate, NULL);int sampleRateInHertz = sampleRates[0];env->ReleaseIntArrayElements(jSampleRate, sampleRates, JNI_ABORT);// Invalid channel representations are caught by !audio_is_output_channel() below.audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks(channelPositionMask, channelIndexMask);if (!audio_is_output_channel(nativeChannelMask)) {ALOGE("Error creating AudioTrack: invalid native channel mask %#x.", nativeChannelMask);return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;}uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask);// check the format.// This function was called from Java, so we compare the format against the Java constantsaudio_format_t format = audioFormatToNative(audioFormat);if (format == AUDIO_FORMAT_INVALID) {ALOGE("Error creating AudioTrack: unsupported audio format %d.", audioFormat);return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT;}// compute the frame countsize_t frameCount;if (audio_has_proportional_frames(format)) {const size_t bytesPerSample = audio_bytes_per_sample(format);frameCount = buffSizeInBytes / (channelCount * bytesPerSample);} else {frameCount = buffSizeInBytes;}// create the native AudioTrack objectlpTrack = new AudioTrack();// read the AudioAttributes valuesauto paa = JNIAudioAttributeHelper::makeUnique();jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get());if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {return jStatus;}ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s",paa->usage, paa->content_type, paa->flags, paa->tags);// initialize the callback information:// this data will be passed with every AudioTrack callbacklpJniStorage = new AudioTrackJniStorage();lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);// we use a weak reference so the AudioTrack object can be garbage collected.lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);lpJniStorage->mCallbackData.isOffload = offload;lpJniStorage->mCallbackData.busy = false;audio_offload_info_t offloadInfo;if (offload == JNI_TRUE) {offloadInfo = AUDIO_INFO_INITIALIZER;offloadInfo.format = format;offloadInfo.sample_rate = sampleRateInHertz;offloadInfo.channel_mask = nativeChannelMask;offloadInfo.has_video = false;offloadInfo.stream_type = AUDIO_STREAM_MUSIC; //required for offload}// initialize the native AudioTrack objectstatus_t status = NO_ERROR;switch (memoryMode) {case MODE_STREAM:status = lpTrack->set(AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)sampleRateInHertz,format,// word length, PCMnativeChannelMask,offload ? 0 : frameCount,offload ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD : AUDIO_OUTPUT_FLAG_NONE,audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack0,// shared memtrue,// thread can call JavasessionId,// audio session IDoffload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK : AudioTrack::TRANSFER_SYNC,offload ? &offloadInfo : NULL,-1, -1,                       // default uid, pid valuespaa.get());break;case MODE_STATIC:// AudioTrack is using shared memoryif (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");goto native_init_failure;}status = lpTrack->set(AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)sampleRateInHertz,format,// word length, PCMnativeChannelMask,frameCount,AUDIO_OUTPUT_FLAG_NONE,audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTracklpJniStorage->mMemBase,// shared memtrue,// thread can call JavasessionId,// audio session IDAudioTrack::TRANSFER_SHARED,NULL,                         // default offloadInfo-1, -1,                       // default uid, pid valuespaa.get());break;default:ALOGE("Unknown mode %d", memoryMode);goto native_init_failure;}if (status != NO_ERROR) {ALOGE("Error %d initializing AudioTrack", status);goto native_init_failure;}} else {  lpJniStorage = new AudioTrackJniStorage();lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);// we use a weak reference so the AudioTrack object can be garbage collected.lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);lpJniStorage->mCallbackData.busy = false;}nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);if (nSession == NULL) {ALOGE("Error creating AudioTrack: Error retrieving session id pointer");goto native_init_failure;}// read the audio session ID back from AudioTrack in case we create a new sessionnSession[0] = lpTrack->getSessionId();env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);nSession = NULL;{const jint elements[1] = { (jint) lpTrack->getSampleRate() };env->SetIntArrayRegion(jSampleRate, 0, 1, elements);}{   // scope for the lockMutex::Autolock l(sLock);sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData);}// save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field// of the Java object (in mNativeTrackInJavaObj)setAudioTrack(env, thiz, lpTrack);// save the JNI resources so we can free them later//ALOGV("storing lpJniStorage: %x\n", (long)lpJniStorage);env->SetLongField(thiz, javaAudioTrackFields.jniData, (jlong)lpJniStorage);// since we had audio attributes, the stream type was derived from them during the// creation of the native AudioTrack: push the same value to the Java objectenv->SetIntField(thiz, javaAudioTrackFields.fieldStreamType, (jint) lpTrack->streamType());return (jint) AUDIO_JAVA_SUCCESS;// failures:
native_init_failure:if (nSession != NULL) {env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);}env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class);env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref);delete lpJniStorage;env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);// lpTrack goes out of scope, so reference count drops to zeroreturn (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
}

可以看到这里根据两种模式MODE_STREAM、MODE_STATIC创建了两种FrameWork层的AudioTrack实例。

最后还调用了如下这些方法。

env->SetIntField(thiz, javaAudioTrackFields.fieldStreamType, (jint) lpTrack->streamType());

env->SetLongField(thiz, javaAudioTrackFields.jniData, (jlong)lpJniStorage);

我们前面了解到javaAudioTrackFields.jniData是通过GetFieldIDOrDie方法获取到的,GetFieldIDOrDie方法则是去获取JAVA层的AudioTrack类对象

#define JAVA_JNIDATA_FIELD_NAME                         "mJniData"
javaAudioTrackFields.jniData = GetFieldIDOrDie(env, audioTrackClass, JAVA_JNIDATA_FIELD_NAME, "J");

在Java层可以找到这个对象

    @SuppressWarnings("unused")@UnsupportedAppUsageprivate long mJniData;

所以javaAudioTrackFields.jniData是Java层的AudioTrack对象的mJniData属性,所以其实这里的setXXField等方法就是把Native层AudioTrack对象与Java层的AudioTrack关联到一起。

Android—Java层与Native层对象的关联相关推荐

  1. Android Java层和Native层通信入门指南开篇

        Android Java层和Native层通信入门指南开篇 引言    做Android平台系统开发的小伙伴,应该经常会遇到要打通Android Framework层和C/C++层通信的通道问 ...

  2. Android逆向之旅---Native层的Hook神器Cydia Substrate使用详解

    一.前言 在之前已经介绍过了Android中一款hook神器Xposed,那个框架使用非常简单,方法也就那几个,其实最主要的是我们如何找到一个想要hook的应用的那个突破点.需要逆向分析app即可.不 ...

  3. java native函数库_Java 层调用 Native 层函数的两种方式

    概述 Java 层如何调用Native层函数,大家都应该知道使用JNI(Java 本地接口). 通过在java层声明native方法,然后遵守JNI规范命名Native函数,即可建立Java层nati ...

  4. Android基础架构:Native层 Looper、Handler、Message 研究

    Android基础架构:Native层 Looper.Handler.Message 研究1,参考: https://www.cnblogs.com/roger-yu/p/15099541.html ...

  5. 【Android 异步操作】Handler 机制 ( MessageQueue 消息队列的阻塞机制 | Java 层机制 | native 层阻塞机制 | native 层解除阻塞机制 )

    文章目录 一.MessageQueue 的 Java 层机制 二.MessageQueue 的 native 层阻塞机制 三.MessageQueue 的 native 层解除阻塞机制 三.Messa ...

  6. android Java层和Native层frida hook的一些系统参数的修改

    目的:解决一些App对于手机端指纹的风控 1.Java参考脚本`. // An highlighted block var system_mapping = {"ro.build.id&qu ...

  7. ffmpeg--把一个eclipse目录结构的Android工程转为Android Studio结构的工程,并调试jni层和native层错误

    我下面介绍的使用CMake的方式编译native的,直接使用Android.mk也可以. 1.建立一个support c++的Android工程 2.MainActivity.java复制过去,把re ...

  8. framework层和native层实现联网控制(iptable方式)

    最近工作中,需要开发一个功能----联网控制,这个功能其实用过root的安卓机应该都知道,禁止某个应用连接移动网络或者wifi. root后,通过su去执行iptable的命令就可以根据uid去控制应 ...

  9. android native java_在Android Native层中创建Java虚拟机实例

    前言 Android应用中JNI代码,是作为本地方法运行的.而大部分情况下,这些JNI方法均需要传递Dalvik虚拟机实例作为第一个参数.例如,你需要用虚拟机实例来创建jstring和其他的Java对 ...

最新文章

  1. 如何基于OSS和MPS,快速搭建音视频文件上传服务?
  2. Mysql的库与表的基本操作
  3. 计算机教育杂志社投稿送样刊,山东教育杂志社投稿期刊论文征稿发表-陶润杂志网...
  4. oracle对象之存储函数
  5. unity3d 自动变化大小_一种可扩展的Unity3d资源检查方式
  6. vaadin_Vaadin和DukeScript中的Hello World
  7. (1-e^(-j5w))/(1-e^(-jw))=e^(-j2w)*sin(5w/2)/sin(w/2)的证明过程
  8. GridView点击行触发SelectedIndexChanged事件
  9. 什么是车辆识别代码(VIN)
  10. 一文带你了解Windows操作系统安全,保护自己的电脑不受侵害
  11. hosts文件路径及文件介绍
  12. 串口的原始模式和标准模式
  13. python数据分析与挖掘实战(2)帕累托法则菜品盈利分析与相关性分析
  14. Android 从零开始实现微信支付
  15. Tampermonkey中文文档(部分)
  16. 计算机会计实务好学吗,会计电算化难吗
  17. 【无标题】C语言连续输出输入语句执行跳过的问题
  18. EasyRecovery15手机电脑全功能数据恢复软件
  19. 涨停板第二天开盘的三种操作方法
  20. 深圳Java学习:Docker

热门文章

  1. 通话记录查询Java_Android获取手机通话记录的方法
  2. 分布式和集群实现的原理
  3. 世茂集团高端酒店品牌世御于河北省高碑店亮相
  4. 题10 能给出数据库物理存储结构和物理存取方法的是什么?
  5. python画圣诞树图片_庆祝新年?画一颗圣诞树?还是...
  6. 漏洞、bug、错误代码区别
  7. 可以检测手机帧率和温度的软件_没有特异功能 手机软件是如何测得电池温度的...
  8. python 控制有线网卡_用python写一些网络操作
  9. 最详细的小程序用户授权教程
  10. 令人惊讶的沙画艺术,不看后悔!(视频+截图)