鸿蒙OSUniApp 实现的语音输入与语音识别功能#三方框架 #Uniapp

news2025/5/15 22:22:55

UniApp 实现的语音输入与语音识别功能

最近在开发跨平台应用时,客户要求添加语音输入功能以提升用户体验。经过一番调研和实践,我成功在UniApp项目中实现了语音输入与识别功能,现将过程和方法分享出来,希望对有类似需求的开发者有所帮助。

为什么需要语音输入功能?

随着移动设备的普及,语音交互已成为一种高效的人机交流方式。与传统的文字输入相比,语音输入具有以下优势:

  1. 操作便捷:免去键盘敲击,尤其适合单手操作或行走等场景
  2. 输入高效:语音输入速度通常快于手动输入
  3. 提升体验:为特定人群(如老年人、视障人士)提供便利
  4. 解放双手:适用于驾车、做家务等无法腾出手打字的场景

在商业应用中,语音输入可以显著降低用户的操作门槛,提高转化率和用户留存。

技术方案选型

在UniApp环境中实现语音识别,主要有三种方案:

  1. 使用原生插件:调用各平台的原生语音识别能力
  2. 对接云服务:接入第三方语音识别API(如百度、讯飞等)
  3. Web API:在H5平台利用Web Speech API

经过对比和测试,我最终采用了混合方案:

  • 在App平台使用原生插件获取最佳体验
  • 在微信小程序使用微信自带的语音识别能力
  • 在H5平台尝试使用Web Speech API,不支持时降级为云服务API

实现步骤

1. App端实现(基于原生插件)

首先需要安装语音识别插件。我选择了市场上比较成熟的speech-baidu插件,这是基于百度语音识别SDK封装的UniApp插件。

安装插件后,在manifest.json中配置:

"app-plus": {
  "plugins": {
    "speech": {
      "baidu": {
        "appid": "你的百度语音识别AppID",
        "apikey": "你的API Key",
        "secretkey": "你的Secret Key"
      }
    }
  },
  "distribute": {
    "android": {
      "permissions": [
        "<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
        "<uses-permission android:name=\"android.permission.INTERNET\"/>"
      ]
    }
  }
}

接下来创建语音识别组件:

<template>
  <view class="voice-input-container">
    <view 
      class="voice-btn" 
      :class="{ 'recording': isRecording }"
      @touchstart="startRecord" 
      @touchend="stopRecord"
      @touchcancel="cancelRecord"
    >
      <image :src="isRecording ? '/static/mic-active.png' : '/static/mic.png'" mode="aspectFit"></image>
      <text>{{ isRecording ? '松开结束' : '按住说话' }}</text>
    </view>
    
    <view v-if="isRecording" class="recording-tip">
      <text>正在聆听...</text>
      <view class="wave-container">
        <view 
          v-for="(item, index) in waveItems" 
          :key="index" 
          class="wave-item"
          :style="{ height: item + 'rpx' }"
        ></view>
      </view>
    </view>
  </view>
</template>

<script>
// #ifdef APP-PLUS
const speechPlugin = uni.requireNativePlugin('speech-baidu');
// #endif

export default {
  name: 'VoiceInput',
  data() {
    return {
      isRecording: false,
      timer: null,
      waveItems: [10, 15, 20, 25, 30, 25, 20, 15, 10]
    }
  },
  props: {
    lang: {
      type: String,
      default: 'zh'  // zh: 中文, en: 英文
    },
    maxDuration: {
      type: Number,
      default: 60  // 最长录音时间,单位秒
    }
  },
  methods: {
    startRecord() {
      if (this.isRecording) return;
      
      // 申请录音权限
      uni.authorize({
        scope: 'scope.record',
        success: () => {
          this.isRecording = true;
          this.startWaveAnimation();
          
          // #ifdef APP-PLUS
          speechPlugin.start({
            vadEos: 3000,  // 静音超时时间
            language: this.lang === 'zh' ? 'zh-cn' : 'en-us'
          }, (res) => {
            if (res.errorCode === 0) {
              // 识别结果
              this.$emit('result', res.result);
            } else {
              uni.showToast({
                title: `识别失败: ${res.errorCode}`,
                icon: 'none'
              });
            }
            this.isRecording = false;
            this.stopWaveAnimation();
          });
          // #endif
          
          // 设置最长录制时间
          this.timer = setTimeout(() => {
            if (this.isRecording) {
              this.stopRecord();
            }
          }, this.maxDuration * 1000);
        },
        fail: () => {
          uni.showToast({
            title: '请授权录音权限',
            icon: 'none'
          });
        }
      });
    },
    
    stopRecord() {
      if (!this.isRecording) return;
      
      // #ifdef APP-PLUS
      speechPlugin.stop();
      // #endif
      
      clearTimeout(this.timer);
      this.isRecording = false;
      this.stopWaveAnimation();
    },
    
    cancelRecord() {
      if (!this.isRecording) return;
      
      // #ifdef APP-PLUS
      speechPlugin.cancel();
      // #endif
      
      clearTimeout(this.timer);
      this.isRecording = false;
      this.stopWaveAnimation();
    },
    
    // 波形动画
    startWaveAnimation() {
      this.waveAnimTimer = setInterval(() => {
        this.waveItems = this.waveItems.map(() => Math.floor(Math.random() * 40) + 10);
      }, 200);
    },
    
    stopWaveAnimation() {
      clearInterval(this.waveAnimTimer);
      this.waveItems = [10, 15, 20, 25, 30, 25, 20, 15, 10];
    }
  },
  beforeDestroy() {
    this.cancelRecord();
  }
}
</script>

<style scoped>
.voice-input-container {
  width: 100%;
}

.voice-btn {
  width: 200rpx;
  height: 200rpx;
  border-radius: 100rpx;
  background-color: #f5f5f5;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  margin: 0 auto;
}

.voice-btn.recording {
  background-color: #e1f5fe;
  box-shadow: 0 0 20rpx rgba(0, 120, 255, 0.5);
}

.voice-btn image {
  width: 80rpx;
  height: 80rpx;
  margin-bottom: 10rpx;
}

.recording-tip {
  margin-top: 30rpx;
  text-align: center;
}

.wave-container {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  height: 80rpx;
  margin-top: 20rpx;
}

.wave-item {
  width: 8rpx;
  background-color: #1890ff;
  margin: 0 5rpx;
  border-radius: 4rpx;
  transition: height 0.2s;
}
</style>

2. 微信小程序实现

微信小程序提供了原生的语音识别API,使用非常方便:

// 在小程序环境下的代码
startRecord() {
  // #ifdef MP-WEIXIN
  this.isRecording = true;
  this.startWaveAnimation();
  
  const recorderManager = wx.getRecorderManager();
  
  recorderManager.onStart(() => {
    console.log('录音开始');
  });
  
  recorderManager.onStop((res) => {
    this.isRecording = false;
    this.stopWaveAnimation();
    
    // 将录音文件发送到微信后台识别
    wx.showLoading({ title: '识别中...' });
    const { tempFilePath } = res;
    
    wx.uploadFile({
      url: 'https://api.weixin.qq.com/cgi-bin/media/voice/translatecontent',
      filePath: tempFilePath,
      name: 'media',
      formData: {
        access_token: this.accessToken,
        format: 'mp3',
        voice_id: Date.now(),
        lfrom: this.lang === 'zh' ? 'zh_CN' : 'en_US',
        lto: 'zh_CN'
      },
      success: (uploadRes) => {
        wx.hideLoading();
        const data = JSON.parse(uploadRes.data);
        if (data.errcode === 0) {
          this.$emit('result', data.result);
        } else {
          uni.showToast({
            title: `识别失败: ${data.errmsg}`,
            icon: 'none'
          });
        }
      },
      fail: () => {
        wx.hideLoading();
        uni.showToast({
          title: '语音识别失败',
          icon: 'none'
        });
      }
    });
  });
  
  recorderManager.start({
    duration: this.maxDuration * 1000,
    sampleRate: 16000,
    numberOfChannels: 1,
    encodeBitRate: 48000,
    format: 'mp3'
  });
  // #endif
},

stopRecord() {
  // #ifdef MP-WEIXIN
  wx.getRecorderManager().stop();
  // #endif
  
  // ...与App端相同的代码...
}

需要注意的是,微信小程序的语音识别需要获取access_token,这通常需要在后端实现并提供接口。

3. H5端实现

在H5端,我们可以利用Web Speech API来实现语音识别,当浏览器不支持时则降级为云服务API:

startRecord() {
  // #ifdef H5
  this.isRecording = true;
  this.startWaveAnimation();
  
  // 检查浏览器是否支持Speech Recognition
  if ('webkitSpeechRecognition' in window || 'SpeechRecognition' in window) {
    const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
    this.recognition = new SpeechRecognition();
    
    this.recognition.lang = this.lang === 'zh' ? 'zh-CN' : 'en-US';
    this.recognition.continuous = false;
    this.recognition.interimResults = false;
    
    this.recognition.onresult = (event) => {
      const result = event.results[0][0].transcript;
      this.$emit('result', result);
    };
    
    this.recognition.onerror = (event) => {
      uni.showToast({
        title: `识别错误: ${event.error}`,
        icon: 'none'
      });
    };
    
    this.recognition.onend = () => {
      this.isRecording = false;
      this.stopWaveAnimation();
    };
    
    this.recognition.start();
    
  } else {
    // 不支持Web Speech API,调用云服务API
    this.useCloudSpeechAPI();
  }
  // #endif
  
  // 设置最长录制时间
  this.timer = setTimeout(() => {
    if (this.isRecording) {
      this.stopRecord();
    }
  }, this.maxDuration * 1000);
},

stopRecord() {
  // #ifdef H5
  if (this.recognition) {
    this.recognition.stop();
  }
  // #endif
  
  // ...与App端相同的代码...
},

useCloudSpeechAPI() {
  // 这里实现降级方案,调用后端接口进行语音识别
  uni.chooseFile({
    count: 1,
    type: 'file',
    extension: ['.mp3', '.wav'],
    success: (res) => {
      const tempFilePath = res.tempFilePaths[0];
      
      // 上传音频文件到后端进行识别
      uni.uploadFile({
        url: this.apiBaseUrl + '/speech/recognize',
        filePath: tempFilePath,
        name: 'audio',
        formData: {
          lang: this.lang
        },
        success: (uploadRes) => {
          const data = JSON.parse(uploadRes.data);
          if (data.code === 0) {
            this.$emit('result', data.result);
          } else {
            uni.showToast({
              title: `识别失败: ${data.msg}`,
              icon: 'none'
            });
          }
        },
        complete: () => {
          this.isRecording = false;
          this.stopWaveAnimation();
        }
      });
    }
  });
}

4. 通用接口封装

为了让调用方便,我封装了一个统一的API:

// 在 utils/speech.js 中
const Speech = {
  // 开始语音识别
  startRecognize(options) {
    const { lang = 'zh', success, fail, complete } = options;
    
    // #ifdef APP-PLUS
    const speechPlugin = uni.requireNativePlugin('speech-baidu');
    speechPlugin.start({
      vadEos: 3000,
      language: lang === 'zh' ? 'zh-cn' : 'en-us'
    }, (res) => {
      if (res.errorCode === 0) {
        success && success(res.result);
      } else {
        fail && fail(res);
      }
      complete && complete();
    });
    return {
      stop: () => speechPlugin.stop(),
      cancel: () => speechPlugin.cancel()
    };
    // #endif
    
    // #ifdef MP-WEIXIN
    // 微信小程序实现逻辑
    // ...
    // #endif
    
    // #ifdef H5
    // H5实现逻辑
    // ...
    // #endif
  }
};

export default Speech;

实战案例:聊天应用中的语音输入

现在,我们来看一个实际应用场景 - 在聊天应用中添加语音输入功能:

<template>
  <view class="chat-input-container">
    <view class="chat-tools">
      <image 
        :src="isVoiceMode ? '/static/keyboard.png' : '/static/mic.png'" 
        @tap="toggleInputMode"
      ></image>
      <image src="/static/emoji.png" @tap="showEmojiPicker"></image>
    </view>
    
    <view v-if="!isVoiceMode" class="text-input">
      <textarea
        v-model="message"
        auto-height
        placeholder="请输入消息..."
        :focus="textFocus"
        @focus="onFocus"
        @blur="onBlur"
      ></textarea>
    </view>
    
    <view v-else class="voice-input">
      <voice-input @result="onVoiceResult"></voice-input>
    </view>
    
    <button 
      class="send-btn" 
      :disabled="!message.trim()" 
      @tap="sendMessage"
    >发送</button>
  </view>
</template>

<script>
import VoiceInput from '@/components/voice-input/voice-input.vue';

export default {
  components: {
    VoiceInput
  },
  data() {
    return {
      message: '',
      isVoiceMode: false,
      textFocus: false
    };
  },
  methods: {
    toggleInputMode() {
      this.isVoiceMode = !this.isVoiceMode;
      if (!this.isVoiceMode) {
        this.$nextTick(() => {
          this.textFocus = true;
        });
      }
    },
    
    onVoiceResult(result) {
      this.message = result;
      this.isVoiceMode = false;
    },
    
    sendMessage() {
      if (!this.message.trim()) return;
      
      this.$emit('send', this.message);
      this.message = '';
    },
    
    onFocus() {
      this.textFocus = true;
    },
    
    onBlur() {
      this.textFocus = false;
    },
    
    showEmojiPicker() {
      // 显示表情选择器
    }
  }
};
</script>

<style>
.chat-input-container {
  display: flex;
  align-items: center;
  padding: 20rpx;
  border-top: 1rpx solid #eee;
  background-color: #fff;
}

.chat-tools {
  display: flex;
  margin-right: 20rpx;
}

.chat-tools image {
  width: 60rpx;
  height: 60rpx;
  margin-right: 20rpx;
}

.text-input {
  flex: 1;
  background-color: #f5f5f5;
  border-radius: 10rpx;
  padding: 10rpx 20rpx;
}

.text-input textarea {
  width: 100%;
  min-height: 60rpx;
  max-height: 240rpx;
}

.voice-input {
  flex: 1;
  display: flex;
  justify-content: center;
}

.send-btn {
  width: 140rpx;
  height: 80rpx;
  line-height: 80rpx;
  font-size: 28rpx;
  margin-left: 20rpx;
  padding: 0;
  background-color: #1890ff;
  color: #fff;
}

.send-btn[disabled] {
  background-color: #ccc;
}
</style>

性能优化和注意事项

在实际开发中,我遇到了一些需要特别注意的问题:

1. 权限处理

语音识别需要麦克风权限,不同平台的权限处理方式不同:

// 统一请求录音权限
requestAudioPermission() {
  return new Promise((resolve, reject) => {
    // #ifdef APP-PLUS
    const permissions = ['android.permission.RECORD_AUDIO'];
    plus.android.requestPermissions(
      permissions,
      function(e) {
        if (e.granted.length === permissions.length) {
          resolve();
        } else {
          reject(new Error('未授予录音权限'));
        }
      },
      function(e) {
        reject(e);
      }
    );
    // #endif
    
    // #ifdef MP-WEIXIN || MP-BAIDU
    uni.authorize({
      scope: 'scope.record',
      success: () => resolve(),
      fail: (err) => reject(err)
    });
    // #endif
    
    // #ifdef H5
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
      navigator.mediaDevices.getUserMedia({ audio: true })
        .then(() => resolve())
        .catch(err => reject(err));
    } else {
      reject(new Error('浏览器不支持录音功能'));
    }
    // #endif
  });
}

2. 流量控制

语音识别需要上传音频数据,在移动网络下会消耗流量:

// 检查网络环境并提示用户
checkNetwork() {
  uni.getNetworkType({
    success: (res) => {
      if (res.networkType === '2g' || res.networkType === '3g') {
        uni.showModal({
          title: '流量提醒',
          content: '当前处于移动网络环境,语音识别可能消耗较多流量,是否继续?',
          success: (confirm) => {
            if (confirm.confirm) {
              this.startSpeechRecognition();
            }
          }
        });
      } else {
        this.startSpeechRecognition();
      }
    }
  });
}

3. 性能优化

长时间语音识别会增加内存和电量消耗,需要做好优化:

// 设置最大录音时长和自动结束
setupMaxDuration() {
  if (this.timer) {
    clearTimeout(this.timer);
  }
  
  this.timer = setTimeout(() => {
    if (this.isRecording) {
      uni.showToast({
        title: '录音时间过长,已自动结束',
        icon: 'none'
      });
      this.stopRecord();
    }
  }, this.maxDuration * 1000);
}

// 空闲自动停止
setupVAD() {
  // 监测静音,如果用户停止说话3秒,自动结束录音
  let lastAudioLevel = 0;
  let silenceCounter = 0;
  
  this.vadTimer = setInterval(() => {
    // 获取当前音量
    const currentLevel = this.getAudioLevel();
    
    if (Math.abs(currentLevel - lastAudioLevel) < 0.05) {
      silenceCounter++;
      if (silenceCounter > 30) { // 3秒 (30 * 100ms)
        this.stopRecord();
      }
    } else {
      silenceCounter = 0;
    }
    
    lastAudioLevel = currentLevel;
  }, 100);
}

增强功能:语音合成(TTS)

除了语音识别外,语音合成(Text-to-Speech)也是很有用的功能,可以将文本转换为语音:

// 语音合成
textToSpeech(text, options = {}) {
  const { lang = 'zh', speed = 5, volume = 5 } = options;
  
  // #ifdef APP-PLUS
  const speechPlugin = uni.requireNativePlugin('speech-baidu');
  return new Promise((resolve, reject) => {
    speechPlugin.textToSpeech({
      text,
      language: lang === 'zh' ? 'zh-cn' : 'en-us',
      speed,
      volume
    }, (res) => {
      if (res.errorCode === 0) {
        resolve(res);
      } else {
        reject(new Error(`语音合成失败: ${res.errorCode}`));
      }
    });
  });
  // #endif
  
  // #ifdef H5
  return new Promise((resolve, reject) => {
    if ('speechSynthesis' in window) {
      const speech = new SpeechSynthesisUtterance();
      speech.text = text;
      speech.lang = lang === 'zh' ? 'zh-CN' : 'en-US';
      speech.rate = speed / 10;
      speech.volume = volume / 10;
      
      speech.onend = () => {
        resolve();
      };
      
      speech.onerror = (err) => {
        reject(err);
      };
      
      window.speechSynthesis.speak(speech);
    } else {
      reject(new Error('当前浏览器不支持语音合成'));
    }
  });
  // #endif
}

踩坑记录与解决方案

开发过程中,我遇到了一些常见问题与解决方法,分享如下:

  1. 百度语音插件初始化失败:检查API密钥配置和网络环境,特别是HTTPS限制
  2. H5录音无法使用:多数浏览器要求必须在HTTPS环境下才能使用麦克风
  3. 识别结果不准确:尝试调整录音参数,如采样率、声道数等,或者使用更专业的噪声抑制算法
  4. 微信小程序调用失败:检查access_token是否有效,注意token有效期
  5. 不同设备体验差异大:针对低端设备优化,如减少动画效果、降低采样率等

我们的解决方案是进行兼容性检测,并根据设备性能自动调整参数:

// 检测设备性能并调整参数
detectDevicePerformance() {
  const platform = uni.getSystemInfoSync().platform;
  const brand = uni.getSystemInfoSync().brand;
  const model = uni.getSystemInfoSync().model;
  
  // 低端安卓设备优化
  if (platform === 'android') {
    // 特定型号的优化
    if (brand === 'samsung' && model.includes('SM-J')) {
      return {
        sampleRate: 8000,
        quality: 'low',
        useVAD: false // 禁用语音活动检测,降低CPU占用
      };
    }
  }
  
  // 默认配置
  return {
    sampleRate: 16000,
    quality: 'high',
    useVAD: true
  };
}

总结与展望

通过本文,我们探讨了在UniApp中实现语音输入与识别功能的多种方案,并提供了具体的代码实现。这些实现方案已在实际项目中得到验证,能够满足大多数应用场景的需求。

语音技术在移动应用中的重要性不断提升,未来可以探索更多高级功能:

  1. 离线语音识别:降低网络依赖,提高响应速度
  2. 多语言支持:增加更多语言的识别能力
  3. 声纹识别:通过语音实现用户身份验证
  4. 情感分析:从语音中识别用户情绪

希望本文对你在UniApp中实现语音功能有所帮助!如有问题欢迎在评论区交流讨论。

参考资料

  1. UniApp官方文档
  2. 百度语音识别API文档
  3. Web Speech API

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2376400.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

windows版redis的使用

redis下载 Releases microsoftarchive/redishttps://github.com/microsoftarchive/redis/releases redis的启动和停止 进入路径的cmd 启动&#xff1a;redis-server.exe redis.windows.conf 停止&#xff1a;ctrlc 连接redis 指定要连接的IP和端口号 -h IP地址 -p 端口…

Java版OA管理系统源码 手机版OA系统源码

Java版OA管理系统源码 手机版OA系统源码 一&#xff1a;OA系统的主要优势 1. 提升效率 减少纸质流程和重复性工作&#xff0c;自动化处理常规事务&#xff0c;缩短响应时间。 2. 降低成本 节省纸张、打印、通讯及人力成本&#xff0c;优化资源分配。 3. 规范管理 固化企…

NineData 社区版 V4.1.0 正式发布,新增 4 条迁移链路,本地化数据管理能力再升级

NineData 社区版 V4.1.0 正式更新发布。本次通过新增 4 条迁移链路扩展、国产数据库深度适配、敏感数据保护增强‌等升级&#xff0c;进一步巩固了其作为高效、安全、易用的数据管理工具的定位。无论是开发测试、数据迁移&#xff0c;还是多环境的数据管理&#xff0c;NineData…

进阶2_1:QT5多线程与定时器共生死

1、在widget.ui中使用 LCD Number控件 注意&#xff1a;若 LCD 控件不是多线程&#xff0c;LCD控件则会瞬间自增到最大的数值&#xff0c;如上图&#xff0c;说明两者都是多线程处理 2、实现方式 1、创建 LCD 控件并修改为 LCD1 2、创建任务类 mytask. h&#xff0c;对任务类…

在虚拟机Ubuntu18.04中安装NS2教程及应用

NS2简介 一、主要组成部分&#xff1a; 1.NS2&#xff1a;模拟器本身&#xff0c;负责执行TCL脚本进行模拟&#xff0c;并生成trace文件输出结果。 2.NAM&#xff1a;网络动画模拟器&#xff0c;用于将模拟结果可视化。 二、使用的语言&#xff1a; 1.C&#xff1a;NS2中最重要…

VBA —— 第6章子程序与函数

子程序&#xff1a;实现特定功能的程序代码块 子程序语法&#xff1a; [修饰符] Sub 子程序名称([参数1&#xff0c;参数2&#xff0c;参数3]) 代码块 End Sub 子程序如何调用&#xff1a; 1 . 子程序名 [参数1&#xff0c;参数2&#xff0c;...] 2. Call 子程序名 [(参…

全新开发-iVX图形化编程VS完整IDE

本文针对传统软件开发的效率与可控性矛盾&#xff0c;系统阐释 iVX"图形化编程 全栈 IDE" 的复合架构如何突破行业瓶颈。通过 "可视化建模 - 标准代码生成 - 独立运行" 的技术闭环&#xff0c;iVX 实现开发效率提升 60% 与源码完全可控的双重目标。研究揭…

【Linux系列】跨平台安装与配置 Vim 文本编辑器

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

十天学会嵌入式技术之51单片机—day-10

第 20 章 18B20 温度检测 20.1 18B20 概述 20.1.1 简介 18B20 是一种常用的数字温度传感器&#xff0c;广泛应用于环境监测、工业控制、家居自动化 和设备温度监控等领域。 20.1.2 引脚功能 18B20 引脚功能如下图所示&#xff0c;需要特别强调的是&#xff0c;18B20 采用 1-…

【C++】17. 多态

上一章节中我们讲了C三大特性的继承&#xff0c;这一章节我们接着来讲另一个特性——多态 1. 多态的概念 多态(polymorphism)的概念&#xff1a;通俗来说&#xff0c;就是多种形态。多态分为编译时多态(静态多态)和运行时多态(动态多态)&#xff0c;这里我们重点讲运行时多态…

家用或办公 Windows 电脑玩人工智能开源项目配备核显的必要性(含 NPU 及显卡类型补充)

一、GPU 与显卡的概念澄清 首先需要明确一个容易误解的概念&#xff1a;GPU 不等同于显卡。 显卡和GPU是两个不同的概念。 【概念区分】 在讨论图形计算领域时&#xff0c;需首先澄清一个常见误区&#xff1a;GPU&#xff08;图形处理单元&#xff09;与显卡&#xff08;视…

实现一个简单的 TCP 客户端/服务器

注意&#xff1a; TCP 三次握手建立连接建立连接后&#xff0c;TCP 提供全双工的通信服务&#xff0c;也就是在同一个连接中&#xff0c;通信双方 可以在同一时刻同时写数据&#xff0c;相对的概念叫做半双工&#xff0c;同一个连接的同一时刻&#xff0c;只能由一方来写数据T…

对抗帕金森:在疾病阴影下,如何重掌生活主动权?

帕金森病&#xff0c;一种影响全球超 1000 万人的神经退行性疾病&#xff0c;正无声地改变着患者的生活轨迹。随着大脑中多巴胺分泌减少&#xff0c;患者逐渐出现肢体震颤、肌肉僵硬、步态迟缓等症状&#xff0c;甚至连扣纽扣、端水杯这类日常动作都变得艰难。更棘手的是&#…

鸿蒙 UIAbility组件与UI的数据同步和窗口关闭

使用 EventHub 进行数据通信 Stage模型概念图 根据 Stage 模型概念图 UIAbility 先于 ArkUI Page 创建 所以&#xff0c;事件要先 .on 订阅 再 emit 发布 假如现在有页面 Page1 和他的 UIAbility // src/main/ets/page1ability/Page1Ability.ets onCreate(want: Want, laun…

Vue3学习(组合式API——计算属性computed详解)

目录 一、计算属性computed。 Vue官方提供的案例。(普通写法与计算属性写法) 使用计算属性computed重构——>简化描述响应式状态的复杂逻辑。 &#xff08;1&#xff09;计算属性computed小案例。 <1>需求说明。&#xff08;筛选原数组——>得新数组&#xff09; &…

Android Studio 模拟器配置方案

Android Studio 模拟器配置方案 1.引言2.使用Android Studio中的模拟器3.使用国产模拟器1.引言 前面介绍【React Native基础环境配置】的时候需要配置模拟器,当时直接使用了USB调试方案,但是有些时候可能不太方便连接手机调试,比如没有iPhone调不了ios。接下来说明另外两种可…

k8s中ingress-nginx介绍

1. 介绍 Ingress是一种Kubernetes资源&#xff0c;用于将外部流量路由到Kubernetes集群内的服务。与NodePort相比&#xff0c;它提供了更高级别的路由功能和负载平衡&#xff0c;可以根据HTTP请求的路径、主机名、HTTP方法等来路由流量。可以说Ingress是为了弥补NodePort在流量…

字节DeerFlow开源框架:多智能体深度研究框架,实现端到端自动化研究流程

&#x1f98c; DeerFlow DeerFlow&#xff08;Deep Exploration and Efficient Research Flow&#xff09;是一个社区驱动的深度研究框架&#xff0c;它建立在开源社区的杰出工作基础之上。目标是将语言模型与专业工具&#xff08;如网络搜索、爬虫和Python代码执行&#xff0…

算法第十八天|530. 二叉搜索树的最小绝对差、501.二叉搜索树中的众数、236. 二叉树的最近公共祖先

530. 二叉搜索树的最小绝对差 题目 思路与解法 第一想法&#xff1a; 一个二叉搜索树的最小绝对差&#xff0c;从根结点看&#xff0c;它的结点与它的最小差值一定出现在 左子树的最右结点&#xff08;左子树最大值&#xff09;和右子树的最左结点&#xff08;右子树的最小值…

微服务调试问题总结

本地环境调试。 启动本地微服务&#xff0c;使用公共nacos配置。利用如apifox进行本地代码调试解决调试问题。除必要的业务微服务依赖包需要下载到本地。使用mvn clean install -DskipTests进行安装启动前选择好profile环境进行启动&#xff0c;启动前记得mvn clean清理项目。…