更新记录

1.0.1(2026-01-16)

增加通知栏使用方法

1.0.0(2026-01-16)

新增功能

  • ✨ 首次发布
  • ✨ 支持实时监听音频设备状态变化
  • ✨ 支持检测扬声器、听筒、有线耳机、蓝牙设备
  • ✨ 提供开始监听、停止监听、获取当前状态三个核心API
  • ✨ 支持 Android 平台(通过广播接收器监听)
  • ✨ 支持 iOS 平台(通过 AVAudioSession 监听)
  • ✨ 完善的错误码和错误处理机制
  • ✨ 详细的使用文档和示例代码

平台兼容性

uni-app(4.85)

Vue2 Vue3 Chrome Safari app-vue app-vue插件版本 app-nvue app-nvue插件版本 Android Android插件版本 iOS iOS插件版本 鸿蒙 鸿蒙插件版本
× × × × 1.0.0 1.0.0 4.4 1.0.0 12 1.0.0 21 1.0.0
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 快应用-华为 快应用-联盟
× × × × × × × × × × ×

uni-app x(4.85)

Chrome Safari Android iOS 鸿蒙 微信小程序
- - - - - -

miko-audioListener

音频监听器插件 - 实时监听扬声器/耳机状态变化 & 通知栏播放控制

功能说明

🎧 音频设备状态监听

本插件提供了音频设备状态监听功能,可以实时检测以下设备状态变化:

  • 📢 扬声器 - 检测是否使用扬声器播放
  • 📞 听筒 - 检测是否使用听筒播放
  • 🎧 有线耳机 - 检测耳机的插入和拔出
  • 🔊 蓝牙设备 - 检测蓝牙音频设备的连接和断开

🎵 通知栏播放控制

插件还提供了完整的通知栏播放控制功能:

  • 🎛️ 播放控制按钮 - 播放/暂停、上一曲、下一曲
  • 📊 播放信息显示 - 歌曲标题、艺术家、封面图片
  • 🔄 状态同步 - 实时更新播放状态
  • 📱 跨平台支持 - Android & iOS 统一API

平台支持

  • ✅ Android
  • ✅ iOS

使用方法

1. 开始监听音频设备状态

在需要监听音频设备状态的页面中,调用 startAudioListener 方法:

import { startAudioListener } from "@/uni_modules/miko-audioListener"

export default {
  onLoad() {
    startAudioListener({
      // 状态变化回调(必填)
      onChange: (status) => {
        console.log("音频设备状态变化:", status)
        console.log("输出类型:", status.outputType)        // "speaker" | "earpiece" | "wiredHeadset" | "bluetooth" | "unknown"
        console.log("是否使用扬声器:", status.isSpeaker)    // true/false
        console.log("是否连接耳机:", status.isHeadsetConnected)  // true/false
        console.log("是否连接蓝牙:", status.isBluetoothConnected) // true/false
        console.log("设备描述:", status.description)        // "扬声器" | "听筒" | "有线耳机" | "蓝牙设备"

        // 根据状态做相应处理
        if (status.outputType === 'wiredHeadset') {
          console.log('用户插入了耳机')
        } else if (status.outputType === 'speaker') {
          console.log('当前使用扬声器播放')
        }
      },
      // 成功回调(可选)
      success: (res) => {
        console.log("开始监听成功", res)
        console.log("当前设备状态:", res.currentStatus)
      },
      // 失败回调(可选)
      fail: (err) => {
        console.error("开始监听失败", err.errCode, err.errMsg)
      },
      // 完成回调(可选)
      complete: (res) => {
        console.log("监听器启动完成", res)
      }
    })
  }
}

2. 停止监听音频设备状态

在页面卸载或不需要监听时,调用 stopAudioListener 方法:

import { stopAudioListener } from "@/uni_modules/miko-audioListener"

export default {
  onUnload() {
    stopAudioListener({
      success: (res) => {
        console.log("停止监听成功", res)
      },
      fail: (err) => {
        console.error("停止监听失败", err.errCode, err.errMsg)
      }
    })
  }
}

3. 获取当前音频设备状态

可以随时查询当前的音频设备状态:

import { getCurrentAudioStatus } from "@/uni_modules/miko-audioListener"

getCurrentAudioStatus({
  success: (res) => {
    console.log("获取状态成功", res)
    const status = res.status
    console.log("当前输出设备:", status.outputType)
    console.log("是否使用扬声器:", status.isSpeaker)
    console.log("是否连接耳机:", status.isHeadsetConnected)
    console.log("是否连接蓝牙:", status.isBluetoothConnected)
  },
  fail: (err) => {
    console.error("获取状态失败", err.errCode, err.errMsg)
  }
})

通知栏播放控制

插件还提供了通知栏播放控制功能,可以在通知栏显示媒体播放控制按钮。

4. 初始化通知栏控制器

在使用通知栏功能前需要先初始化控制器:

import { initializeNotificationController } from "@/uni_modules/miko-audioListener"

initializeNotificationController({
  success: (res) => {
    console.log("通知栏控制器初始化成功", res)
  },
  fail: (err) => {
    console.error("通知栏控制器初始化失败", err.errCode, err.errMsg)
  }
})

5. 显示通知栏播放控制

初始化后可以显示通知栏:

import { showNotification } from "@/uni_modules/miko-audioListener"

showNotification({
  success: (res) => {
    console.log("通知栏显示成功", res)
  },
  fail: (err) => {
    console.error("通知栏显示失败", err.errCode, err.errMsg)
  }
})

6. 隐藏通知栏播放控制

不需要显示时可以隐藏通知栏:

import { hideNotification } from "@/uni_modules/miko-audioListener"

hideNotification({
  success: (res) => {
    console.log("通知栏隐藏成功", res)
  },
  fail: (err) => {
    console.error("通知栏隐藏失败", err.errCode, err.errMsg)
  }
})

7. 更新通知栏播放信息

更新通知栏中显示的歌曲信息:

import { updateNotificationInfo } from "@/uni_modules/miko-audioListener"

updateNotificationInfo({
  title: "歌曲名称",
  artist: "艺术家",
  coverUrl: "https://example.com/cover.jpg"
}, {
  success: (res) => {
    console.log("通知栏信息更新成功", res)
  },
  fail: (err) => {
    console.error("通知栏信息更新失败", err.errCode, err.errMsg)
  }
})

8. 更新通知栏播放状态

更新通知栏中的播放状态(播放/暂停):

import { updateNotificationState } from "@/uni_modules/miko-audioListener"

updateNotificationState({
  isPlaying: true  // true: 播放状态, false: 暂停状态
}, {
  success: (res) => {
    console.log("通知栏状态更新成功", res)
  },
  fail: (err) => {
    console.error("通知栏状态更新失败", err.errCode, err.errMsg)
  }
})

9. 设置通知栏按钮回调监听器

设置持续监听通知栏按钮点击事件的回调:

import { onNotificationCallbacks } from "@/uni_modules/miko-audioListener"

// 设置监听器,持续监听按钮点击事件
onNotificationCallbacks((callbackData) => {
  console.log("通知栏按钮被点击:", callbackData.action)

  switch (callbackData.action) {
    case 'playPause':
      // 处理播放/暂停按钮点击
      console.log('播放/暂停按钮被点击')
      break
    case 'previous':
      // 处理上一曲按钮点击
      console.log('上一曲按钮被点击')
      break
    case 'next':
      // 处理下一曲按钮点击
      console.log('下一曲按钮被点击')
      break
    default:
      console.log('未知按钮被点击:', callbackData.action)
  }
})

10. 停止通知栏按钮回调监听

停止监听通知栏按钮点击事件:

import { stopNotificationCallbacks } from "@/uni_modules/miko-audioListener"

stopNotificationCallbacks({
  success: (res) => {
    console.log("停止通知栏回调监听成功", res)
  },
  fail: (err) => {
    console.error("停止通知栏回调监听失败", err.errCode, err.errMsg)
  }
})

11. 释放通知栏控制器

在应用退出或不需要使用通知栏功能时释放资源:

import { releaseNotificationController } from "@/uni_modules/miko-audioListener"

releaseNotificationController()

完整示例

<template>
  <view class="container">
    <text class="title">音频设备监听器</text>

    <view class="status-card">
      <text class="status-label">当前设备:</text>
      <text class="status-value">{{ deviceStatus.description }}</text>
    </view>

    <view class="status-card">
      <text class="status-label">输出类型:</text>
      <text class="status-value">{{ deviceStatus.outputType }}</text>
    </view>

    <view class="status-card">
      <text class="status-label">是否扬声器:</text>
      <text class="status-value">{{ deviceStatus.isSpeaker ? '是' : '否' }}</text>
    </view>

    <view class="status-card">
      <text class="status-label">耳机连接:</text>
      <text class="status-value">{{ deviceStatus.isHeadsetConnected ? '已连接' : '未连接' }}</text>
    </view>

    <view class="status-card">
      <text class="status-label">蓝牙连接:</text>
      <text class="status-value">{{ deviceStatus.isBluetoothConnected ? '已连接' : '未连接' }}</text>
    </view>

    <button @click="startListening" :disabled="isListening">开始监听</button>
    <button @click="stopListening" :disabled="!isListening">停止监听</button>
    <button @click="checkStatus">查询当前状态</button>

    <!-- 通知栏控制区域 -->
    <view class="section-title">通知栏播放控制</view>

    <button @click="initNotification" :disabled="notificationInitialized">初始化通知栏</button>
    <button @click="showNotificationBar" :disabled="!notificationInitialized">显示通知栏</button>
    <button @click="hideNotificationBar" :disabled="!notificationInitialized">隐藏通知栏</button>

    <view class="input-group">
      <text class="input-label">歌曲标题:</text>
      <input v-model="songTitle" placeholder="输入歌曲标题" />
    </view>

    <view class="input-group">
      <text class="input-label">艺术家:</text>
      <input v-model="songArtist" placeholder="输入艺术家" />
    </view>

    <button @click="updateNotificationInfo" :disabled="!notificationInitialized">更新通知栏信息</button>

    <view class="toggle-group">
      <text class="toggle-label">播放状态:</text>
      <switch :checked="isNotificationPlaying" @change="toggleNotificationPlay" :disabled="!notificationInitialized" />
      <text>{{ isNotificationPlaying ? '播放中' : '已暂停' }}</text>
    </view>
  </view>
</template>

<script>
import {
  startAudioListener,
  stopAudioListener,
  getCurrentAudioStatus,
  initializeNotificationController,
  showNotification,
  hideNotification,
  updateNotificationInfo,
  updateNotificationState,
  onNotificationCallbacks,
  stopNotificationCallbacks,
  releaseNotificationController
} from "@/uni_modules/miko-audioListener"

export default {
  data() {
    return {
      isListening: false,
      deviceStatus: {
        outputType: 'unknown',
        isSpeaker: false,
        isHeadsetConnected: false,
        isBluetoothConnected: false,
        description: '未知'
      },
      // 通知栏相关数据
      notificationInitialized: false,
      songTitle: '示例歌曲',
      songArtist: '示例艺术家',
      isNotificationPlaying: false
    }
  },

  methods: {
    // 开始监听
    startListening() {
      startAudioListener({
        onChange: (status) => {
          console.log('设备状态变化:', status)
          this.deviceStatus = status

          // 可以根据状态做相应处理
          if (status.outputType === 'wiredHeadset') {
            uni.showToast({ title: '检测到耳机插入', icon: 'none' })
          } else if (status.outputType === 'speaker') {
            uni.showToast({ title: '当前使用扬声器', icon: 'none' })
          }
        },
        success: (res) => {
          console.log('开始监听成功:', res)
          this.isListening = true
          this.deviceStatus = res.currentStatus
          uni.showToast({ title: '开始监听成功', icon: 'success' })
        },
        fail: (err) => {
          console.error('开始监听失败:', err)
          uni.showToast({ title: err.errMsg, icon: 'none' })
        }
      })
    },

    // 停止监听
    stopListening() {
      stopAudioListener({
        success: (res) => {
          console.log('停止监听成功:', res)
          this.isListening = false
          uni.showToast({ title: '停止监听成功', icon: 'success' })
        },
        fail: (err) => {
          console.error('停止监听失败:', err)
          uni.showToast({ title: err.errMsg, icon: 'none' })
        }
      })
    },

    // 查询当前状态
    checkStatus() {
      getCurrentAudioStatus({
        success: (res) => {
          console.log('获取状态成功:', res)
          this.deviceStatus = res.status
          uni.showToast({
            title: `当前设备: ${res.status.description}`,
            icon: 'none'
          })
        },
        fail: (err) => {
          console.error('获取状态失败:', err)
          uni.showToast({ title: err.errMsg, icon: 'none' })
        }
      })
    },

    // 初始化通知栏
    initNotification() {
      initializeNotificationController({
        success: (res) => {
          console.log('通知栏初始化成功:', res)
          this.notificationInitialized = true
          uni.showToast({ title: '通知栏初始化成功', icon: 'success' })

          // 设置通知栏回调监听器
          this.setupNotificationCallbacks()
        },
        fail: (err) => {
          console.error('通知栏初始化失败:', err)
          uni.showToast({ title: err.errMsg, icon: 'none' })
        }
      })
    },

    // 设置通知栏回调监听器
    setupNotificationCallbacks() {
      onNotificationCallbacks((callbackData) => {
        console.log('通知栏按钮点击:', callbackData.action)

        switch (callbackData.action) {
          case 'playPause':
            this.handleNotificationPlayPause()
            break
          case 'previous':
            this.handleNotificationPrevious()
            break
          case 'next':
            this.handleNotificationNext()
            break
        }
      })
    },

    // 处理通知栏播放/暂停
    handleNotificationPlayPause() {
      this.isNotificationPlaying = !this.isNotificationPlaying
      this.updateNotificationState()
      uni.showToast({
        title: this.isNotificationPlaying ? '播放' : '暂停',
        icon: 'none'
      })
    },

    // 处理通知栏上一曲
    handleNotificationPrevious() {
      uni.showToast({ title: '上一曲', icon: 'none' })
    },

    // 处理通知栏下一曲
    handleNotificationNext() {
      uni.showToast({ title: '下一曲', icon: 'none' })
    },

    // 显示通知栏
    showNotificationBar() {
      showNotification({
        success: (res) => {
          console.log('显示通知栏成功:', res)
          uni.showToast({ title: '通知栏已显示', icon: 'success' })
        },
        fail: (err) => {
          console.error('显示通知栏失败:', err)
          uni.showToast({ title: err.errMsg, icon: 'none' })
        }
      })
    },

    // 隐藏通知栏
    hideNotificationBar() {
      hideNotification({
        success: (res) => {
          console.log('隐藏通知栏成功:', res)
          uni.showToast({ title: '通知栏已隐藏', icon: 'success' })
        },
        fail: (err) => {
          console.error('隐藏通知栏失败:', err)
          uni.showToast({ title: err.errMsg, icon: 'none' })
        }
      })
    },

    // 更新通知栏信息
    updateNotificationInfo() {
      updateNotificationInfo({
        title: this.songTitle,
        artist: this.songArtist,
        coverUrl: 'https://example.com/cover.jpg'
      }, {
        success: (res) => {
          console.log('更新通知栏信息成功:', res)
          uni.showToast({ title: '通知栏信息已更新', icon: 'success' })
        },
        fail: (err) => {
          console.error('更新通知栏信息失败:', err)
          uni.showToast({ title: err.errMsg, icon: 'none' })
        }
      })
    },

    // 更新通知栏播放状态
    updateNotificationState() {
      updateNotificationState({
        isPlaying: this.isNotificationPlaying
      }, {
        success: (res) => {
          console.log('更新通知栏状态成功:', res)
        },
        fail: (err) => {
          console.error('更新通知栏状态失败:', err)
        }
      })
    },

    // 切换通知栏播放状态
    toggleNotificationPlay() {
      this.isNotificationPlaying = !this.isNotificationPlaying
      this.updateNotificationState()
    }
  },

  onUnload() {
    // 页面卸载时停止监听
    if (this.isListening) {
      this.stopListening()
    }

    // 停止通知栏回调监听
    if (this.notificationInitialized) {
      stopNotificationCallbacks({
        success: (res) => {
          console.log('停止通知栏回调监听成功:', res)
        },
        fail: (err) => {
          console.error('停止通知栏回调监听失败:', err)
        }
      })

      // 释放通知栏控制器
      releaseNotificationController()
    }
  }
}
</script>

<style>
.container {
  padding: 40rpx;
}

.title {
  font-size: 36rpx;
  font-weight: bold;
  text-align: center;
  margin-bottom: 40rpx;
}

.status-card {
  display: flex;
  justify-content: space-between;
  padding: 20rpx;
  margin-bottom: 20rpx;
  background-color: #f5f5f5;
  border-radius: 10rpx;
}

.status-label {
  font-size: 28rpx;
  color: #666;
}

.status-value {
  font-size: 28rpx;
  color: #333;
  font-weight: bold;
}

button {
  margin-top: 20rpx;
}

.section-title {
  font-size: 32rpx;
  font-weight: bold;
  margin: 40rpx 0 20rpx 0;
  color: #333;
}

.input-group {
  display: flex;
  align-items: center;
  margin-bottom: 20rpx;
  padding: 20rpx;
  background-color: #f9f9f9;
  border-radius: 10rpx;
}

.input-label {
  font-size: 28rpx;
  color: #666;
  width: 160rpx;
  flex-shrink: 0;
}

input {
  flex: 1;
  padding: 10rpx;
  border: 1rpx solid #ddd;
  border-radius: 6rpx;
  font-size: 28rpx;
}

.toggle-group {
  display: flex;
  align-items: center;
  padding: 20rpx;
  margin-bottom: 20rpx;
  background-color: #f9f9f9;
  border-radius: 10rpx;
}

.toggle-label {
  font-size: 28rpx;
  color: #666;
  margin-right: 20rpx;
}

switch {
  margin-right: 20rpx;
}
</style>

API说明

startAudioListener(options)

开始监听音频设备状态

参数说明:

参数 类型 必填 说明
onChange Function 状态变化回调,参数为 AudioDeviceStatus
success Function 成功回调
fail Function 失败回调
complete Function 完成回调(成功或失败都会执行)

AudioDeviceStatus 对象说明:

字段 类型 说明
outputType String 输出设备类型:"speaker"(扬声器) / "earpiece"(听筒) / "wiredHeadset"(有线耳机) / "bluetooth"(蓝牙设备) / "unknown"(未知)
isSpeaker Boolean 是否使用扬声器
isHeadsetConnected Boolean 是否连接耳机
isBluetoothConnected Boolean 是否连接蓝牙
description String 设备描述

stopAudioListener(options)

停止监听音频设备状态

参数说明:

参数 类型 必填 说明
success Function 成功回调
fail Function 失败回调
complete Function 完成回调(成功或失败都会执行)

getCurrentAudioStatus(options)

获取当前音频设备状态

参数说明:

参数 类型 必填 说明
success Function 成功回调,参数包含 status 字段
fail Function 失败回调
complete Function 完成回调(成功或失败都会执行)

initializeNotificationController(options)

初始化通知栏控制器

参数说明:

参数 类型 必填 说明
success Function 成功回调
fail Function 失败回调
complete Function 完成回调(成功或失败都会执行)

showNotification(options)

显示通知栏播放控制

参数说明:

参数 类型 必填 说明
success Function 成功回调
fail Function 失败回调
complete Function 完成回调(成功或失败都会执行)

hideNotification(options)

隐藏通知栏播放控制

参数说明:

参数 类型 必填 说明
success Function 成功回调
fail Function 失败回调
complete Function 完成回调(成功或失败都会执行)

updateNotificationInfo(info, options)

更新通知栏播放信息

参数说明:

参数 类型 必填 说明
info Object 播放信息对象
info.title String 歌曲标题
info.artist String 艺术家
info.coverUrl String 封面图片URL
success Function 成功回调
fail Function 失败回调
complete Function 完成回调(成功或失败都会执行)

updateNotificationState(state, options)

更新通知栏播放状态

参数说明:

参数 类型 必填 说明
state Object 播放状态对象
state.isPlaying Boolean 是否正在播放
success Function 成功回调
fail Function 失败回调
complete Function 完成回调(成功或失败都会执行)

onNotificationCallbacks(callback)

设置通知栏按钮回调监听器(持续监听模式)

参数说明:

参数 类型 必填 说明
callback Function 按钮点击回调函数,参数为包含action的对象

回调数据格式:

{
  action: "playPause" | "previous" | "next",  // 按钮动作
  type: "playPause" | "previous" | "next"     // 动作类型(兼容字段)
}

stopNotificationCallbacks(options)

停止通知栏按钮回调监听

参数说明:

参数 类型 必填 说明
success Function 成功回调
fail Function 失败回调
complete Function 完成回调(成功或失败都会执行)

releaseNotificationController()

释放通知栏控制器资源

参数说明:

错误码说明

音频监听器错误码

错误码 说明
9030001 监听器未启动
9030002 监听器已经在运行
9030003 启动监听器失败
9030004 停止监听器失败
9030005 获取音频状态失败

通知栏错误码

错误码 说明
9040001 获取应用上下文失败
9040002 通知栏控制器初始化异常
9040003 显示通知栏异常
9040004 隐藏通知栏异常
9040005 更新通知栏信息异常
9040006 更新通知栏状态异常
9040007 通知栏控制器未初始化
9040008 设置通知栏回调监听器异常

注意事项

  1. 及时停止监听:在不需要监听时(如页面卸载),务必调用 stopAudioListener 停止监听,避免内存泄漏
  2. 权限要求:插件会自动处理所需权限,无需额外配置
  3. 重复启动:如果监听器已经在运行,再次调用 startAudioListener 会返回错误码 9030002
  4. Android 特性:Android 端通过广播接收器监听设备变化,响应迅速
  5. iOS 特性:iOS 端通过 AVAudioSession 监听路由变化,兼容性良好

使用场景

音频设备监听

  • 🎵 音乐/视频播放器:检测耳机拔出自动暂停播放
  • 📞 通话应用:检测音频输出设备切换
  • 🎮 游戏应用:根据设备类型调整音量
  • 📱 社交应用:语音/视频通话时的设备管理

通知栏播放控制

  • 🎵 音乐播放器App:在通知栏显示播放控制按钮
  • 🎬 视频播放器App:后台播放时保持控制界面
  • 📻 音频直播App:实时控制播放状态
  • 🏃‍♂️ 运动健身App:边运动边控制音乐播放

开发文档

更新日志

详见 changelog.md

问题反馈

如有问题或建议,请提交 Issue 或 Pull Request。

微信:cnshaoyu(170 33333 111)

License

MIT

隐私、权限声明

1. 本插件需要申请的系统权限列表:

<!-- 音频相关权限 --> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- Media3 相关权限 --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。