今天来说说 native 中的代码是如何调用 java 侧代码的。
在看 setEnabled 代码的时候,我们了解到,最终在函数 EffectHandle::setEnabled 中会调用 java 侧的函数, 
将状态改变的事件通知到 java 侧。
今天就以 AudioEffect 中的 native 侧调用 java 函数作为例子,来说明 JNI 中的 callback 函数。

#######################说明################################
/*1、首先,在 JNI 中会有一个 init 函数,java 侧会首先调用该 init 函数来完成初始化。 对于 AudioEffect 来说,该函数就是 android_media_AudioEffect_native_init 函数。 */ // ++++++++++++++++++++++++++++++android_media_AudioEffect_native_init++++++++++++++++++++++++++++++++++ // This function gets some field IDs, which in turn causes class initialization. // It is called from a static block in AudioEffect, which won't run until the // first time an instance of this class is used. static void android_media_AudioEffect_native_init(JNIEnv *env) { LOGV("android_media_AudioEffect_native_init"); fields.clazzEffect = NULL; fields.clazzDesc = NULL; // Get the AudioEffect class // 获取 java 侧的类 // static const char* const kClassPathName = "android/media/audiofx/AudioEffect"; // 此处的 path name 其实是 java 侧类所在的包。 // java 侧, AudioEffect 类所在地包为 package android.media.audiofx; jclass clazz = env->FindClass(kClassPathName); if (clazz == NULL) { LOGE("Can't find %s", kClassPathName); return; } // 创建 java 侧 AudioEffect 对象的一个全局引用 fields.clazzEffect = (jclass)env->NewGlobalRef(clazz); // Get the postEvent method // 根据函数名称及参数类型从 AudioEffect 对象中获取函数 fields.midPostNativeEvent = env->GetStaticMethodID( fields.clazzEffect, "postEventFromNative", // 函数名 "(Ljava/lang/Object;IIILjava/lang/Object;)V"); // 参数类型(object, int, int, int, object) if (fields.midPostNativeEvent == NULL) { LOGE("Can't find AudioEffect.%s", "postEventFromNative"); return; } // Get the variables fields // nativeTrackInJavaObj fields.fidNativeAudioEffect = env->GetFieldID( fields.clazzEffect, "mNativeAudioEffect", "I"); if (fields.fidNativeAudioEffect == NULL) { LOGE("Can't find AudioEffect.%s", "mNativeAudioEffect"); return; } // fidJniData; fields.fidJniData = env->GetFieldID( fields.clazzEffect, "mJniData", "I"); if (fields.fidJniData == NULL) { LOGE("Can't find AudioEffect.%s", "mJniData"); return; } // 查找类 Descriptor clazz = env->FindClass("android/media/audiofx/AudioEffect$Descriptor"); if (clazz == NULL) { LOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class"); return; } fields.clazzDesc = (jclass)env->NewGlobalRef(clazz); // 得到 Descriptor 的 init 函数 fields.midDescCstor = env->GetMethodID( fields.clazzDesc, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); if (fields.midDescCstor == NULL) { LOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class constructor"); return; } } // ------------------------------android_media_AudioEffect_native_init---------------------------------- /* 2、 java 侧会调用 native_setup 函数, 该函数中会创建 native 侧对象。 对应 AudioEffect 来说, native_setup 函数就是 android_media_AudioEffect_native_setup 函数。*/ // ++++++++++++++++++++++++++++android_media_AudioEffect_native_setup++++++++++++++++++++++++++++++++++++ static jint android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_this, jstring type, jstring uuid, jint priority, jint sessionId, jintArray jId, jobjectArray javadesc) { LOGV("android_media_AudioEffect_native_setup"); AudioEffectJniStorage* lpJniStorage = NULL; int lStatus = AUDIOEFFECT_ERROR_NO_MEMORY; AudioEffect* lpAudioEffect = NULL; jint* nId = NULL; const char *typeStr = NULL; const char *uuidStr = NULL; effect_descriptor_t desc; jobject jdesc; char str[EFFECT_STRING_LEN_MAX]; jstring jdescType; jstring jdescUuid; jstring jdescConnect; jstring jdescName; jstring jdescImplementor; if (type != NULL) { typeStr = env->GetStringUTFChars(type, NULL); if (typeStr == NULL) { // Out of memory jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); goto setup_failure; } } if (uuid != NULL) { uuidStr = env->GetStringUTFChars(uuid, NULL); if (uuidStr == NULL) { // Out of memory jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); goto setup_failure; } } if (typeStr == NULL && uuidStr == NULL) { lStatus = AUDIOEFFECT_ERROR_BAD_VALUE; goto setup_failure; } lpJniStorage = new AudioEffectJniStorage(); if (lpJniStorage == NULL) { LOGE("setup: Error creating JNI Storage"); goto setup_failure; } // 创建 java 侧 AudioEffect 对象的一个全局引用, 并将其保存到 AudioEffectJniStorage 对象中 lpJniStorage->mCallbackData.audioEffect_class = (jclass)env->NewGlobalRef(fields.clazzEffect); // we use a weak reference so the AudioEffect object can be garbage collected. // weak_this 是 java 侧 AudioEffect 对象的一个 weak reference , 通过 native_setup 函数传进来 lpJniStorage->mCallbackData.audioEffect_ref = env->NewGlobalRef(weak_this); LOGV("setup: lpJniStorage: %p audioEffect_ref %p audioEffect_class %p, &mCallbackData %p", lpJniStorage, lpJniStorage->mCallbackData.audioEffect_ref, lpJniStorage->mCallbackData.audioEffect_class, &lpJniStorage->mCallbackData); if (jId == NULL) { LOGE("setup: NULL java array for id pointer"); lStatus = AUDIOEFFECT_ERROR_BAD_VALUE; goto setup_failure; } // create the native AudioEffect object lpAudioEffect = new AudioEffect(typeStr, uuidStr, priority, effectCallback, // 真正实现在 native 侧调用 java 侧代码的函数 &lpJniStorage->mCallbackData, // 创建 native 侧 AudioEffect 对象,将 lpJniStorage->mCallbackData 作为 user data 传入 sessionId, 0); if (lpAudioEffect == NULL) { LOGE("Error creating AudioEffect"); goto setup_failure; } lStatus = translateError(lpAudioEffect->initCheck()); if (lStatus != AUDIOEFFECT_SUCCESS && lStatus != AUDIOEFFECT_ERROR_ALREADY_EXISTS) { LOGE("AudioEffect initCheck failed %d", lStatus); goto setup_failure; } nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL); if (nId == NULL) { LOGE("setup: Error retrieving id pointer"); lStatus = AUDIOEFFECT_ERROR_BAD_VALUE; goto setup_failure; } nId[0] = lpAudioEffect->id(); env->ReleasePrimitiveArrayCritical(jId, nId, 0); nId = NULL; if (typeStr) { env->ReleaseStringUTFChars(type, typeStr); typeStr = NULL; } if (uuidStr) { env->ReleaseStringUTFChars(uuid, uuidStr); uuidStr = NULL; } // get the effect descriptor desc = lpAudioEffect->descriptor(); AudioEffect::guidToString(&desc.type, str, EFFECT_STRING_LEN_MAX); jdescType = env->NewStringUTF(str); AudioEffect::guidToString(&desc.uuid, str, EFFECT_STRING_LEN_MAX); jdescUuid = env->NewStringUTF(str); if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { jdescConnect = env->NewStringUTF("Auxiliary"); } else { jdescConnect = env->NewStringUTF("Insert"); } jdescName = env->NewStringUTF(desc.name); jdescImplementor = env->NewStringUTF(desc.implementor); // 创建一个 Descriptor 对象。 // 使用的函数是在函数 android_media_AudioEffect_native_init 中得到的 Descriptor 的 init 函数 jdesc = env->NewObject(fields.clazzDesc, fields.midDescCstor, jdescType, jdescUuid, jdescConnect, jdescName, jdescImplementor); env->DeleteLocalRef(jdescType); env->DeleteLocalRef(jdescUuid); env->DeleteLocalRef(jdescConnect); env->DeleteLocalRef(jdescName); env->DeleteLocalRef(jdescImplementor); if (jdesc == NULL) { LOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)"); goto setup_failure; } env->SetObjectArrayElement(javadesc, 0, jdesc); env->SetIntField(thiz, fields.fidNativeAudioEffect, (int)lpAudioEffect); env->SetIntField(thiz, fields.fidJniData, (int)lpJniStorage); return AUDIOEFFECT_SUCCESS; // failures: setup_failure: if (nId != NULL) { env->ReleasePrimitiveArrayCritical(jId, nId, 0); } if (lpAudioEffect) { delete lpAudioEffect; } env->SetIntField(thiz, fields.fidNativeAudioEffect, 0); if (lpJniStorage) { delete lpJniStorage; } env->SetIntField(thiz, fields.fidJniData, 0); if (uuidStr != NULL) { env->ReleaseStringUTFChars(uuid, uuidStr); } if (typeStr != NULL) { env->ReleaseStringUTFChars(type, typeStr); } return lStatus; } // ----------------------------android_media_AudioEffect_native_setup------------------------------------ /* 3、在 native 侧对象的构造函数中,会将传入的包含 java 侧对象和函数的 callback data 赋值给自己的成员变量。 同时将传入的 callback 函数赋值给自己的成员变量。 4、 native 侧对象的构造函数中,会创建一个 client 对象(mIEffectClient), 并将 native 侧 AudioTrack 对象的指针作为参数传入。 client 对象的构造函数中会将传入的 AudioTrack 对象的指针赋值给自己的成员变量(mEffect)。 5、 native 侧 AudioTrack 对象的构造函数中,接下来调用函数 AudioFlinger::createEffect , 并将 mIEffectClient 作为参数传入。 6、函数 AudioFlinger::createEffect 中调用函数 AudioFlinger::PlaybackThread::createEffect_l ,并继续将 effectClient 下传。 7、函数 AudioFlinger::PlaybackThread::createEffect_l 中会创建一个 EffectHandle 对象,并将 effectClient 传入。 8、 AudioFlinger::EffectHandle::EffectHandle 的构造函数中将传入的 client 对象赋值给了自己的成员变量 mEffectClient 。 9、 函数 AudioFlinger::EffectHandle::setEnabled 会调用 mEffectClient 的 enableStatusChanged 函数。*/ // ++++++++++++++++++++++++++AudioFlinger::EffectHandle::setEnabled++++++++++++++++++++++++++++++++++++++ void AudioFlinger::EffectHandle::setEnabled(bool enabled) { if (mEffectClient != 0) { mEffectClient->enableStatusChanged(enabled); } } // --------------------------AudioFlinger::EffectHandle::setEnabled-------------------------------------- /* 10、 函数 EffectClient::enableStatusChanged 直接调用了函数 AudioEffect::enableStatusChanged 。 11、函数 AudioEffect::enableStatusChanged 的实现。 其中的 mCbf 其实就是函数 effectCallback 。*/ // +++++++++++++++++++++++++++AudioEffect::enableStatusChanged+++++++++++++++++++++++++++++++++++++ void AudioEffect::enableStatusChanged(bool enabled) { LOGV("enableStatusChanged %p enabled %d mCbf %p", this, enabled, mCbf); if (mStatus == ALREADY_EXISTS) { if (enabled) { android_atomic_or(1, &mEnabled); } else { android_atomic_and(~1, &mEnabled); } if (mCbf) { mCbf(EVENT_ENABLE_STATUS_CHANGED, mUserData, &enabled); } } } // ---------------------------AudioEffect::enableStatusChanged------------------------------------- // 12、函数 effectCallback 的实现: // +++++++++++++++++++++++++++++effectCallback+++++++++++++++++++++++++++++++++++ static void effectCallback(int event, void* user, void *info) { effect_param_t *p; int arg1 = 0; int arg2 = 0; jobject obj = NULL; jbyteArray array = NULL; jbyte *bytes; bool param; size_t size; effect_callback_cookie *callbackInfo = (effect_callback_cookie *)user; JNIEnv *env = AndroidRuntime::getJNIEnv(); LOGV("effectCallback: callbackInfo %p, audioEffect_ref %p audioEffect_class %p", callbackInfo, callbackInfo->audioEffect_ref, callbackInfo->audioEffect_class); if (!user || !env) { LOGW("effectCallback error user %p, env %p", user, env); return; } switch (event) { case AudioEffect::EVENT_CONTROL_STATUS_CHANGED: if (info == 0) { LOGW("EVENT_CONTROL_STATUS_CHANGED info == NULL"); goto effectCallback_Exit; } param = *(bool *)info; arg1 = (int)param; LOGV("EVENT_CONTROL_STATUS_CHANGED"); break; case AudioEffect::EVENT_ENABLE_STATUS_CHANGED: if (info == 0) { LOGW("EVENT_ENABLE_STATUS_CHANGED info == NULL"); goto effectCallback_Exit; } param = *(bool *)info; arg1 = (int)param; LOGV("EVENT_ENABLE_STATUS_CHANGED"); break; case AudioEffect::EVENT_PARAMETER_CHANGED: if (info == 0) { LOGW("EVENT_PARAMETER_CHANGED info == NULL"); goto effectCallback_Exit; } p = (effect_param_t *)info; if (p->psize == 0 || p->vsize == 0) { goto effectCallback_Exit; } // arg1 contains offset of parameter value from start of byte array arg1 = sizeof(effect_param_t) + ((p->psize - 1) / sizeof(int) + 1) * sizeof(int); size = arg1 + p->vsize; array = env->NewByteArray(size); if (array == NULL) { LOGE("effectCallback: Couldn't allocate byte array for parameter data"); goto effectCallback_Exit; } bytes = env->GetByteArrayElements(array, NULL); memcpy(bytes, p, size); env->ReleaseByteArrayElements(array, bytes, 0); obj = array; LOGV("EVENT_PARAMETER_CHANGED"); break; case AudioEffect::EVENT_ERROR: LOGW("EVENT_ERROR"); break; } // 此处实现了对 java 侧函数的调用 env->CallStaticVoidMethod( callbackInfo->audioEffect_class, // java 侧的对象的引用 fields.midPostNativeEvent, // java 侧的函数, 在函数 android_media_AudioEffect_native_init 中 callbackInfo->audioEffect_ref, event, arg1, arg2, obj); // 这几个都是调用 java 侧函数时的参数 effectCallback_Exit: if (array) { env->DeleteLocalRef(array); } if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); } } // -----------------------------effectCallback----------------------------------- // 13、 java 侧 postEventFromNative 函数的实现: // +++++++++++++++++++++++++++postEventFromNative+++++++++++++++++++++++++++++++++++++ private static void postEventFromNative(Object effect_ref, int what, int arg1, int arg2, Object obj) { AudioEffect effect = (AudioEffect) ((WeakReference) effect_ref).get(); if (effect == null) { return; } if (effect.mNativeEventHandler != null) { Message m = effect.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj); effect.mNativeEventHandler.sendMessage(m); } } // ---------------------------postEventFromNative-------------------------------------
###########################################################

&&&&&&&&&&&&&&&&&&&&&&&总结&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
上面列出了 native 代码中调用 java 函数的过程。
其实,从 java 侧对象的创建时机来看, JNI 回调分为两种情况:
java 对象在 java 侧创建
java 对象中 JNI 中创建。

一、java 对象在 java 侧创建
    我们上面介绍的 AudioEffect 中的回调,就是这种方式。
这种情况下,主控程序在 java 侧。
1、调用函数 env->FindClass 找到指定的类。
2、调用 env->NewGlobalRef 函数获取对象的引用。
3、调用 env->GetStaticMethodID 函数获取 java 侧的函数。
4、在 JNI 中定义一个 callback 函数调用 java 侧的函数。
5、创建 native 侧对象的时候将 callback 函数作为参数传给 native 对象的构造函数。
6、 native 对象就可以通过 callback 函数来调用 java 侧的函数。

二、java 对象中 JNI 中创建
    这种情况下,主控程序在 native 侧。
1、调用函数 env->FindClass 找到指定的类。
2、调用 env->NewGlobalRef 函数获取对象的引用。
3、调用函数 env->GetMethodID 得到 java 侧创建对象的函数。
4、调用函数 env->NewObject ,通过第三步中得到的函数创建 java 侧对象。
5、调用 env->GetStaticMethodID 函数获取 java 侧的函数。
6、在 JNI 中定义一个 callback 函数调用 java 侧的函数。
7、创建 native 侧对象的时候将 callback 函数作为参数传给 native 对象的构造函数。
8、 native 对象就可以通过 callback 函数来调用 java 侧的函数。
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

Android Audio代码分析25 - JNI callback相关推荐

  1. Android Audio代码分析8 - AudioHardwareALSA::openOutputStream函数

    发现以前写的东西,对调用函数的展开放在了函数的前面,导致不方便找到原来代码及设置的函数参数. 以后打算稍作改动,把对被调函数的展开放在原代码的后面,这样看起来应该方便些. 闲言少叙,跳入代码. 前两天 ...

  2. Android Audio代码分析(2): AudioPoilicyService 启动

    policy: 设备的选择 https://www.cxyzjd.com/article/VNanyesheshou/115659838 Android 音频源码分析--AudioTrack设备选择_ ...

  3. Android Audio代码分析7 - stream type

    在看AudioTrack代码的时候,我们看到,要创建一个AudioTrack对象,需要指定一个StreamType. 今天我们只把stream type相关的代码抽取出来,详细看看stream typ ...

  4. Android Audio代码分析2 - 函数getMinBufferSize

    AudioTrack的使用示例中,用到了函数getMinBufferSize,今天把它倒出来,再嚼嚼. *****************************************源码***** ...

  5. android audio代码分析,Android10.0AudioFocus之源码分析(二)

    前言 上一篇我们简单说了AudioFocus如何使用,那么今天就从源码角度看一下AudioFocus的实现原理. 正文 先说下requestAudioFocus,源码如下: public int re ...

  6. Android Audio代码分析(4): audiohalservice 启动

    hal interface IDeviceFactory openDevice得到IDevice, IDevice openInput/outputStream得到IStream android.ha ...

  7. 基于Android T代码分析: 在freeform窗口的标题栏拖动时移动窗口流程和拖动freeform窗口边沿改变大小流程

    基于Android T代码分析: 在freeform窗口的标题栏拖动时移动窗口流程和拖动freeform窗口边沿改变大小流程在线看Android源代码网址: http://aospxref.com/a ...

  8. Android Audio 架构分析

    一个音频系统大概包括音频的管理.声音播放.声音录音和声音音效几个部分,这几个部分分工协作来完成音频的功能, ·音频管理:负责音量调节.音频设备选择.响铃模式选择等: ·声音播放:负责一个音频流的创建. ...

  9. Android Region代码分析

    一.Region的定义和合法性检查 在Android系统中,定义了Region的概念,它代表屏幕上的一个区域,它是由一个或多个Rect组成的,代码位于frameworks/native/libs/ui ...

最新文章

  1. ios时间差,以时间格式显示
  2. sap 订单状态修改时间_SAP订单状态详解
  3. C语言switch怎么算,超级新手,用switch写了个计算器程序,求指导
  4. app每秒并发数_性能测试连载 (38) jmeter 线程数与性能测试的负载模式
  5. 子类怎么继承父类方法中的变量_JavaOOP_04 封装 继承
  6. Python编程基础05:运算符与表达式
  7. vscode找不到config_vscode中的 jsconfig.json
  8. plsql提示列快捷键_20种VSCode快捷键清单,助你更快编码
  9. 详解:Oracle 数据库空间表、自定义用户、权限管理、序列、同义词、索引
  10. Windows系统端口占用,使用命令行查找并杀进程
  11. mysql 定时备份 空_如何在不停止Mysql服务的状况下,定时备份mysql数据库
  12. NOIP2017错题
  13. WPS Excel 模板设置与生成
  14. 虚拟机安装苹果系统(Mac OSX 10.15包含所有资源)
  15. unix linux_在Unix桌面环境中创建独特的Linux体验
  16. Python 语言创建 Abaqus inp 文件
  17. 网站设计之常见简单实用的JavaScript特效总结(上篇)
  18. java 四边形_java求教,编写一个四边形的类与子类
  19. Java方法创建及调用--------06
  20. Qt - WPS文本编辑器(WPS字体格式)

热门文章

  1. Python爱好者,这里有一个库可以帮助你作为新手掌握人工智能!
  2. python中gil锁和线程锁_Python线程——GIL锁、线程锁(互斥锁)、递归锁(RLock)...
  3. windows 处理bat连接本地mysql
  4. Docker虚拟化解析
  5. tkinter实现弹出输入对话框并获取输入对话框中的值
  6. java线程dump_Java线程Dump分析 - PerfMa
  7. 计算机二级c真题108套,2016年计算机二级108套程序.docx
  8. python中文字符画、编写程序合理选取中文字符构造_编写程序,合理选取中文字符构造字符表,生成中文字符画。...
  9. python字节转字符串中文乱码_黄聪:解决python中文处理乱码,先要弄懂“字符”和“字节”的差别...
  10. 1351.统计有序矩阵中的负数