android_media_AudioTrack

c++ 复制代码
// JNINativeMethod 是一个结构体
//typedef struct {
//    const char* name;      // 1. Java 中 native 方法的名字
//    const char* signature; // 2. 方法的签名(参数类型和返回值类型)
//    void*       fnPtr;     // 3. C++ 中对应实现函数的指针
//} JNINativeMethod;

// 例子
// 简单的
//      {"native_start", "()V", (void *)android_media_AudioTrack_start},
//      native_start:在AudioTrack.java中,你会看到 private native void native_start();
//      ()V:()表示该方法没有参数。 V表示返回值是void
//      android_media_AudioTrack_start:对应C++文件中名为这个的函数
// 复杂的
//      {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZILjava/lang/Object;Ljava/lang/String;)I", (void *)android_media_AudioTrack_setup},
//      这是 AudioTrack 最核心的初始化方法。
//      签名解析:这一串乱码一样的 (Ljava/lang... 其实是类型缩写:
//              Ljava/lang/Object;:传入一个对象
//              [I:传入一个整数数组(int[])
//              I: 传入一个整数(int)
//              J: 传入一个长整型(long)。
//              Z: 传入一个布尔值(boolean)。
//              )I: 最后括号外的 I 表示返回一个整数(int)。
//      作用:当你 new AudioTrack(...) 时,Java 构造函数最终会调用这个 native_setup 来在 C++ 层创建真正的音频对象
// 数据写入
//      {"native_write_byte", "([BIIIZ)I", (void *)android_media_AudioTrack_writeArray<jbyteArray>},
//      [B: 表示 byte[] 数组。
//      android_media_AudioTrack_writeArray<jbyteArray>: 这是一个 C++ 模板函数,用于处理不同类型(byte, short, float)的音频数据写入
static const JNINativeMethod gMethods[] = {
        // name,              signature,     funcPtr
        {"native_is_direct_output_supported", "(IIIIIII)Z",
         (void *)android_media_AudioTrack_is_direct_output_supported},
        {"native_start", "()V", (void *)android_media_AudioTrack_start},
        {"native_stop", "()V", (void *)android_media_AudioTrack_stop},
        {"native_pause", "()V", (void *)android_media_AudioTrack_pause},
        {"native_flush", "()V", (void *)android_media_AudioTrack_flush},
        {"native_setup",
         "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZILjava/lang/Object;Ljava/lang/String;)I",
         (void *)android_media_AudioTrack_setup},
        {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize},
        {"native_release", "()V", (void *)android_media_AudioTrack_release},
        {"native_write_byte", "([BIIIZ)I", (void *)android_media_AudioTrack_writeArray<jbyteArray>},
        {"native_write_native_bytes", "(Ljava/nio/ByteBuffer;IIIZ)I",
         (void *)android_media_AudioTrack_write_native_bytes},
        {"native_write_short", "([SIIIZ)I",
         (void *)android_media_AudioTrack_writeArray<jshortArray>},
        {"native_write_float", "([FIIIZ)I",
         (void *)android_media_AudioTrack_writeArray<jfloatArray>},
        {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume},
        {"native_get_buffer_size_frames", "()I",
         (void *)android_media_AudioTrack_get_buffer_size_frames},
        {"native_set_buffer_size_frames", "(I)I",
         (void *)android_media_AudioTrack_set_buffer_size_frames},
        {"native_get_buffer_capacity_frames", "()I",
         (void *)android_media_AudioTrack_get_buffer_capacity_frames},
        {"native_set_playback_rate", "(I)I", (void *)android_media_AudioTrack_set_playback_rate},
        {"native_get_playback_rate", "()I", (void *)android_media_AudioTrack_get_playback_rate},
        {"native_set_playback_params", "(Landroid/media/PlaybackParams;)V",
         (void *)android_media_AudioTrack_set_playback_params},
        {"native_get_playback_params", "()Landroid/media/PlaybackParams;",
         (void *)android_media_AudioTrack_get_playback_params},
        {"native_set_marker_pos", "(I)I", (void *)android_media_AudioTrack_set_marker_pos},
        {"native_get_marker_pos", "()I", (void *)android_media_AudioTrack_get_marker_pos},
        {"native_set_pos_update_period", "(I)I",
         (void *)android_media_AudioTrack_set_pos_update_period},
        {"native_get_pos_update_period", "()I",
         (void *)android_media_AudioTrack_get_pos_update_period},
        {"native_set_position", "(I)I", (void *)android_media_AudioTrack_set_position},
        {"native_get_position", "()I", (void *)android_media_AudioTrack_get_position},
        {"native_get_latency", "()I", (void *)android_media_AudioTrack_get_latency},
        {"native_get_underrun_count", "()I", (void *)android_media_AudioTrack_get_underrun_count},
        {"native_get_flags", "()I", (void *)android_media_AudioTrack_get_flags},
        {"native_get_timestamp", "([J)I", (void *)android_media_AudioTrack_get_timestamp},
        {"native_getMetrics", "()Landroid/os/PersistableBundle;",
         (void *)android_media_AudioTrack_native_getMetrics},
        {"native_set_loop", "(III)I", (void *)android_media_AudioTrack_set_loop},
        {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload},
        {"native_get_output_sample_rate", "(I)I",
         (void *)android_media_AudioTrack_get_output_sample_rate},
        {"native_get_min_buff_size", "(III)I", (void *)android_media_AudioTrack_get_min_buff_size},
        {"native_setAuxEffectSendLevel", "(F)I",
         (void *)android_media_AudioTrack_setAuxEffectSendLevel},
        {"native_attachAuxEffect", "(I)I", (void *)android_media_AudioTrack_attachAuxEffect},
        {"native_setOutputDevice", "(I)Z", (void *)android_media_AudioTrack_setOutputDevice},
        {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
        {"native_enableDeviceCallback", "()V",
         (void *)android_media_AudioTrack_enableDeviceCallback},
        {"native_disableDeviceCallback", "()V",
         (void *)android_media_AudioTrack_disableDeviceCallback},
        {"native_applyVolumeShaper",
         "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I",
         (void *)android_media_AudioTrack_apply_volume_shaper},
        {"native_getVolumeShaperState", "(I)Landroid/media/VolumeShaper$State;",
         (void *)android_media_AudioTrack_get_volume_shaper_state},
        {"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation},
        {"native_getPortId", "()I", (void *)android_media_AudioTrack_get_port_id},
        {"native_set_delay_padding", "(II)V", (void *)android_media_AudioTrack_set_delay_padding},
        {"native_set_audio_description_mix_level_db", "(F)I",
         (void *)android_media_AudioTrack_setAudioDescriptionMixLeveldB},
        {"native_get_audio_description_mix_level_db", "([F)I",
         (void *)android_media_AudioTrack_getAudioDescriptionMixLeveldB},
        {"native_set_dual_mono_mode", "(I)I", (void *)android_media_AudioTrack_setDualMonoMode},
        {"native_get_dual_mono_mode", "([I)I", (void *)android_media_AudioTrack_getDualMonoMode},
        {"native_setLogSessionId", "(Ljava/lang/String;)V",
         (void *)android_media_AudioTrack_setLogSessionId},
        {"native_setPlayerIId", "(I)V", (void *)android_media_AudioTrack_setPlayerIId},
        {"native_setStartThresholdInFrames", "(I)I",
         (void *)android_media_AudioTrack_setStartThresholdInFrames},
        {"native_getStartThresholdInFrames", "()I",
         (void *)android_media_AudioTrack_getStartThresholdInFrames},
};
c++ 复制代码
// register_android_media_AudioTrack是AudioTrack JNI层的初始化入口
// 如果把JNI比作一座桥,之前的gMethods是规定了桥上有哪些车道(方法映射),那么这个函数就是修建这座桥的工程验收和开通仪式
// 核心任务:
//      注册方法:把gMethods表交给虚拟机,让Java能找到C++
//      缓存ID:提前找到Java类中的变量和方法的"门牌号"(Field ID / Method ID),存入全局变量供后续直接使用
int register_android_media_AudioTrack(JNIEnv *env)
{
    // must be first
    // 注册Native方法
    // RegisterMethodsOrDie:这是一个辅助函数。它的作用是告诉Android虚拟机 ART:"当有调用kClassPathName(即android.media.AudioTrack)类中的native方法的时,请去gMethods表里对应的C++函数"
    // OrDie 意义为 成功则继续,失败则抛出致命错误并终止进程
    //      因为如果基础的音频映射都失败了 系统也无法运行了
    int res = RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));

    javaAudioTrackFields.nativeTrackInJavaObj = NULL;
    javaAudioTrackFields.postNativeEventInJava = NULL;

    // Get the AudioTrack class
    // 缓存Java方法ID(为了C++回调Java)
    // FindClassOrDie 在Java环境中找到android.media.AudioTrack这个类
    // postNativeEventInJava 这是一个AudioTrack里的一个私有静态Java方法
    //      为什么要找到它呢
    //      当底层硬件发生变化 比如播放完了、标记点到了 C++ 层需要主动通知Java层。由于 C++ 无法直接调用Java函数,必须先拿到这个方法的 MethodID
    // (Ljava/lang/Object;IIILjava/lang/Object;)V: 这是 Java 方法的签名,描述了参数和返回值
    jclass audioTrackClass = FindClassOrDie(env, kClassPathName);

    // Get the postEvent method
    javaAudioTrackFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
            audioTrackClass, JAVA_POSTEVENT_CALLBACK_NAME,
            "(Ljava/lang/Object;IIILjava/lang/Object;)V");

    // Get the variables fields
    //      nativeTrackInJavaObj
    // 缓存 Java 变量 ID 为了存取状态
    // nativeTrackInJavaObj:对应 Java 类里的一个 long 类型的变量(mNativeTrackInJavaObj)
    // 重要作用:由于 Java 是垃圾回收机制,而 C++ 是手动内存管理。我们在 C++ 创建了一个 android::AudioTrack 对象后,会把这个 C++ 对象的内存地址 强行存到 Java 对象的这个 long 变量里。
    // 后续使用:下次 Java 调用 play() 时,C++ 只要从这个 Java 变量里读出地址,就能找回之前的 C++ 对象。"J" 代表 Java 的 long 类型。
    javaAudioTrackFields.nativeTrackInJavaObj = GetFieldIDOrDie(env,
            audioTrackClass, JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME, "J");
    //      jniData
    javaAudioTrackFields.jniData = GetFieldIDOrDie(env,
            audioTrackClass, JAVA_JNIDATA_FIELD_NAME, "J");
    //      fieldStreamType
    javaAudioTrackFields.fieldStreamType = GetFieldIDOrDie(env,
            audioTrackClass, JAVA_STREAMTYPE_FIELD_NAME, "I");
    // 清理与返回
    // DeleteLocalRef: 释放局部引用,防止 JNI 内存泄漏(注意:只是释放了对类对象的引用,之前拿到的 ID 是长期有效的)。
    // res: 返回注册结果(通常为 0 代表成功)。
    env->DeleteLocalRef(audioTrackClass);

    // initialize PlaybackParams field info
    // 辅助类的初始化
    // AudioTrack 不仅仅是开关和音量,还涉及播放速率(PlaybackParams)、音量塑形(VolumeShaper)和调谐配置(Tuner)
    // 这些代码是去初始化这些辅助类的 JNI 环境(同样也是找 ID、映射方法)。
    gPlaybackParamsFields.init(env);

    gVolumeShaperFields.init(env);

    // optional check that the TunerConfiguration class and fields exist.
    TunerConfigurationHelper::initCheckOrDie(env);

    return res;
}

总结:为什么要搞得这么麻烦?

  • 如果你在 C++ 里每次调用 Java 方法都临时去找一次 ID(FindClass -> GetMethodID),性能会非常差。
  • 这个函数通过"预处理"完成了以下工作:
  • 打通道路:让 Java native 关键字能点点点到 C++。
  • 记住地址:把 Java 对象的成员变量地址记住,方便 C++ 存取数据(如存入 C++ 指针)。
  • 高性能回调:把 Java 的回调方法地址(postEvent)预先存好,确保底层音频事件能以最快速度通知到 Java 层。
相关推荐
木公子14 分钟前
Vue3源码精读04:Scheduler 调度器深度解析|异步批量更新与任务队列源码全解
前端·vue.js
YHL15 分钟前
🧠 React useContext 深度解析:告别 Props 层层传递的噩梦
前端·react.js
萧行之25 分钟前
Observalble Plot和图形语法理论
前端·数据可视化
开开心心就好27 分钟前
手机悬屏翻译工具外语游戏漫画APP全覆盖
android·前端·javascript·python·游戏·pdf·html
支支დ34 分钟前
Phind 前端特定优势:AI 编程助手的体验革新
前端
DDXYcoder39 分钟前
万字解析动态内存从编译到运行
java·前端·算法
光影少年40 分钟前
react navite封装一个RN 通用按钮组件
前端·javascript·react native·react.js·前端框架
晴天1641 分钟前
CSS 预处理器深度解析-Day35
前端·css
恋猫de小郭44 分钟前
AI 时代,也许你的 Flutter 需要一套 Dartastic OpenTelemetry 监控
android·前端·flutter