当前位置: 首页 > news >正文

Android 9系统源码_音频管理(一)按键音效源码解析

前言

当用户点击Android智能设备的按钮的时候,如果伴随有按键音效的话,会给用户更好的交互体验。本期我们将会结合Android系统源码来具体分析一下控件是如何发出按键音效的。

一、系统加载按键音效资源

1、在TV版的Android智能设备中,我们可以通过调节设置页面的开关来控制按键音效的有无,该设置页面对应的系统源码如下所示。

packages/apps/TvSettings/Settings/src/com/android/tv/settings/device/sound/SoundFragment.java

public class SoundFragment extends PreferenceControllerFragment implementsPreference.OnPreferenceChangeListener {private AudioManager mAudioManager;private Map<Integer, Boolean> mFormats;public static SoundFragment newInstance() {return new SoundFragment();}@Overridepublic void onAttach(Context context) {mAudioManager = context.getSystemService(AudioManager.class);mFormats = mAudioManager.getSurroundFormats();super.onAttach(context);}//用户的点击行为首先触发此方法@Overridepublic boolean onPreferenceTreeClick(Preference preference) {if (TextUtils.equals(preference.getKey(), KEY_SOUND_EFFECTS)) {final TwoStatePreference soundPref = (TwoStatePreference) preference;//调用setSoundEffectsEnabled来设置按键音的开启与关闭setSoundEffectsEnabled(soundPref.isChecked());}return super.onPreferenceTreeClick(preference);}//获取按键音效是否开启public static boolean getSoundEffectsEnabled(ContentResolver contentResolver) {return Settings.System.getInt(contentResolver, Settings.System.SOUND_EFFECTS_ENABLED, 1)!= 0;}//设置是否开启按键音效private void setSoundEffectsEnabled(boolean enabled) {if (enabled) {//如果开启按键音,则调用AudioManager的loadSoundEffects方法来加载按键音效资源mAudioManager.loadSoundEffects();} else {mAudioManager.unloadSoundEffects();}Settings.System.putInt(getActivity().getContentResolver(),Settings.System.SOUND_EFFECTS_ENABLED, enabled ? 1 : 0);}}

我们在设置页面点击按键音效开关按钮,最终会触发SoundFragment的setSoundEffectsEnabled方法,该方法会判断是否开启按键音,如果开启,则调用AudioManager的loadSoundEffects方法来加载按键音效资源,反之则会调用unloadSoundEffects方法不加载音效资源。

2、AudioManager的loadSoundEffects方法如下所示。

frameworks/base/media/java/android/media/AudioManager.java

public class AudioManager {//获取AudioService的代理对象private static IAudioService getService(){if (sService != null) {return sService;}IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);sService = IAudioService.Stub.asInterface(b);return sService;}public void loadSoundEffects() {final IAudioService service = getService();try {//调用AudioService服务的loadSoundEffects方法service.loadSoundEffects();} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}public void unloadSoundEffects() {final IAudioService service = getService();try {service.unloadSoundEffects();} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}
}

AudioManager的首先通过getService方法获取了音频服务AudioService的代理对象,然后调用该对象的具体方法。

3、AudioService的loadSoundEffects方法如下所示。

public class AudioService extends IAudioService.Stubimplements AccessibilityManager.TouchExplorationStateChangeListener,AccessibilityManager.AccessibilityServicesStateChangeListener {private AudioHandler mAudioHandler;//加载音效资源public boolean loadSoundEffects() {int attempts = 3;LoadSoundEffectReply reply = new LoadSoundEffectReply();synchronized (reply) {//调用sendMsg发送消息给mAudioHandler。sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE, 0, 0, reply, 0);while ((reply.mStatus == 1) && (attempts-- > 0)) {try {reply.wait(SOUND_EFFECTS_LOAD_TIMEOUT_MS);} catch (InterruptedException e) {Log.w(TAG, "loadSoundEffects Interrupted while waiting sound pool loaded.");}}}return (reply.mStatus == 0);}//不加载音效资源public void unloadSoundEffects() {sendMsg(mAudioHandler, MSG_UNLOAD_SOUND_EFFECTS, SENDMSG_QUEUE, 0, 0, null, 0);}private static void sendMsg(Handler handler, int msg,int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {if (existingMsgPolicy == SENDMSG_REPLACE) {handler.removeMessages(msg);} else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {return;}synchronized (mLastDeviceConnectMsgTime) {long time = SystemClock.uptimeMillis() + delay;if (msg == MSG_SET_A2DP_SRC_CONNECTION_STATE ||msg == MSG_SET_A2DP_SINK_CONNECTION_STATE ||msg == MSG_SET_HEARING_AID_CONNECTION_STATE ||msg == MSG_SET_WIRED_DEVICE_CONNECTION_STATE ||msg == MSG_A2DP_DEVICE_CONFIG_CHANGE ||msg == MSG_BTA2DP_DOCK_TIMEOUT) {if (mLastDeviceConnectMsgTime >= time) {// add a little delay to make sure messages are ordered as expectedtime = mLastDeviceConnectMsgTime + 30;}mLastDeviceConnectMsgTime = time;}//调用handler的sendMessageAtTime方法handler.sendMessageAtTime(handler.obtainMessage(msg, arg1, arg2, obj), time);}}private class AudioHandler extends Handler {//加载音效private boolean onLoadSoundEffects() {...代码暂时省略...}@Overridepublic void handleMessage(Message msg) {switch (msg.what) {...代码省略...case MSG_UNLOAD_SOUND_EFFECTS:onUnloadSoundEffects();//不加载音效break;case MSG_LOAD_SOUND_EFFECTS://加载音效boolean loaded = onLoadSoundEffects();//调用onLoadSoundEffects加载音效,并将加载结果赋值给loadedif (msg.obj != null) {LoadSoundEffectReply reply = (LoadSoundEffectReply)msg.obj;synchronized (reply) {reply.mStatus = loaded ? 0 : -1;reply.notify();}}break;...代码省略...}}
}

这里我们只要分析一下AudioService的加载音效资源的loadSoundEffects方法,该方法会调用sendMsg,发送类型为MSG_UNLOAD_SOUND_EFFECTS的msg给mAudioHandler。然后会进一步触发AudioHandler的handleMessage方法,该消息最终会触发onLoadSoundEffects方法。

4、AudioHandler的onLoadSoundEffects方法如下所示。

public class AudioService extends IAudioService.Stubimplements AccessibilityManager.TouchExplorationStateChangeListener,AccessibilityManager.AccessibilityServicesStateChangeListener {//音效资源文件名称private static final List<String> SOUND_EFFECT_FILES = new ArrayList<String>();private class AudioHandler extends Handler {//加载音效private boolean onLoadSoundEffects() {int status;synchronized (mSoundEffectsLock) {//如果系统未启动完毕直接返回if (!mSystemReady) {Log.w(TAG, "onLoadSoundEffects() called before boot complete");return false;}//如果mSoundPool不为空直接返回if (mSoundPool != null) {return true;}//加载触摸音效loadTouchSoundAssets();...代码暂时省略...}//加载按键音效资源private void loadTouchSoundAssets() {XmlResourceParser parser = null;//如果音效资源文件列表不为空直接返回if (!SOUND_EFFECT_FILES.isEmpty()) {return;}//加载按键默认的音效资源loadTouchSoundAssetDefaults();...代码省略...}private void loadTouchSoundAssetDefaults() {//在类型为List<String>的SOUND_EFFECT_FILES中添加默认的按键音效资源Effect_Tick.oggSOUND_EFFECT_FILES.add("Effect_Tick.ogg");for (int i = 0; i < AudioManager.NUM_SOUND_EFFECTS; i++) {SOUND_EFFECT_FILES_MAP[i][0] = 0;SOUND_EFFECT_FILES_MAP[i][1] = -1;}}
}

onLoadSoundEffects方法首先判断系统是否已经启动完毕,如果未启动直接返回;然后判断mSoundPool是否空,如果不为空则直接返回;
然后首先会调用一个关键方法loadTouchSoundAssets,该方法首先判断音效资源文件列表SOUND_EFFECT_FILES是否为空,不为空直接返回。如果以上判断都不成立,则会调用loadTouchSoundAssetDefaults方法加载按键默认的音效资源,该方法首先在SOUND_EFFECT_FILES的添加音效资源Effect_Tick.ogg。

5、继续往下看AudioHandler的onLoadSoundEffects方法。

public class AudioService extends IAudioService.Stubimplements AccessibilityManager.TouchExplorationStateChangeListener,AccessibilityManager.AccessibilityServicesStateChangeListener {//音效资源文件名称private static final List<String> SOUND_EFFECT_FILES = new ArrayList<String>();private class AudioHandler extends Handler {//加载音效private boolean onLoadSoundEffects() {int status;synchronized (mSoundEffectsLock) {if (!mSystemReady) {Log.w(TAG, "onLoadSoundEffects() called before boot complete");return false;}if (mSoundPool != null) {return true;}//加载触摸音效loadTouchSoundAssets();//创建SoundPool对象mSoundPool = new SoundPool.Builder().setMaxStreams(NUM_SOUNDPOOL_CHANNELS).setAudioAttributes(new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION).setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).build()).build();...代码省略...int numSamples = 0;for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {// Do not load sample if this effect uses the MediaPlayerif (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {continue;}if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {//获取音效资源文件路径String filePath = getSoundEffectFilePath(effect);//使用SoundPool加载音效资源文件int sampleId = mSoundPool.load(filePath, 0);if (sampleId <= 0) {Log.w(TAG, "Soundpool could not load file: "+filePath);} else {SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;numSamples++;}} else {SOUND_EFFECT_FILES_MAP[effect][1] =poolId[SOUND_EFFECT_FILES_MAP[effect][0]];}}...代码省略...}//获取音效资源文件路径,默认返回的音效资源文件路径为/system/media/audio/ui/Effect_Tick.oggprivate String getSoundEffectFilePath(int effectType) {//  /product + /media/audio/ui/ + Effect_Tick.oggString filePath = Environment.getProductDirectory() + SOUND_EFFECTS_PATH+ SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effectType][0]);if (!new File(filePath).isFile()) {//   /system + /media/audio/ui/ + Effect_Tick.oggfilePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH+ SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effectType][0]);}return filePath;}}}

onLoadSoundEffects先是调用loadTouchSoundAssets方法加载默认的音效资源文件名称,然后构建SoundPool实例对象,随后调用getSoundEffectFilePath获取按键音效资源文件路径,默认返回的音效资源文件路径为/system/media/audio/ui/Effect_Tick.ogg,并调用SoundPool加载该音效资源。

6、简单回顾一下以上步骤。
系统加载按键音效资源

二、点击控件,播放音效资源

在系统开启按键音效之后,当我们点击任意控件之后,都会触发按键音效。接下来我们将会结合View的系统源码来梳理该流程。

1、当我们点击一个控件的时候,首先会触发该View的performClick方法。

frameworks/base/core/java/android/view/View.java

public class View implements Drawable.Callback, KeyEvent.Callback,AccessibilityEventSource {public boolean performClick() {// We still need to call this method to handle the cases where performClick() was called// externally, instead of through performClickInternal()notifyAutofillManagerOnClick();final boolean result;final ListenerInfo li = mListenerInfo;if (li != null && li.mOnClickListener != null) {playSoundEffect(SoundEffectConstants.CLICK);//播放按键点击音效li.mOnClickListener.onClick(this);result = true;} else {result = false;}sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);notifyEnterOrExitForAutoFillIfNeeded(true);return result;}public void playSoundEffect(int soundConstant) {//判断mAttachInfo.mRootCallbacks是否为空,以及系统是否开启了按键音效if (mAttachInfo == null || mAttachInfo.mRootCallbacks == null || !isSoundEffectsEnabled()) {return;}//调用mAttachInfo.mRootCallbacks的playSoundEffect方法mAttachInfo.mRootCallbacks.playSoundEffect(soundConstant);}}

View的performClick方法会调用playSoundEffect方法,playSoundEffect方法首先判断mAttachInfo.mRootCallbacks是否为空,以及系统是否开启了按键音效,然后调用mAttachInfo.mRootCallbacks的playSoundEffect方法。我们知道WindowManager在将View添加到窗口的过程中,都需要用到ViewRootImpl这个类,具体请参考Android 9.0系统源码_窗口管理(二)WindowManager对窗口的管理过程这篇文章。

2、mAttachInfo最初是在ViewRootImpl的构造方法中被创建的。

frameworks/base/core/java/android/view/ViewRootImpl.java

public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {public ViewRootImpl(Context context, Display display) {mContext = context;mWindowSession = WindowManagerGlobal.getWindowSession();mDisplay = display;mBasePackageName = context.getBasePackageName();mThread = Thread.currentThread();mLocation = new WindowLeaked(null);mLocation.fillInStackTrace();mWidth = -1;mHeight = -1;mDirty = new Rect();mTempRect = new Rect();mVisRect = new Rect();mWinFrame = new Rect();mWindow = new W(this);mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;mViewVisibility = View.GONE;mTransparentRegion = new Region();mPreviousTransparentRegion = new Region();mFirst = true; // true for the first time the view is addedmAdded = false;//创建AttachInfo对象,倒数第二个参数就是View的playSoundEffect方法所用到的回调对象mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this, context);...代码省略...}
}

3、看完ViewRootImpl的构造方法,再来看下AttachInfo的构造方法。

public class View implements Drawable.Callback, KeyEvent.Callback,AccessibilityEventSource {final static class AttachInfo {//关键回调接口interface Callbacks {//播放音效void playSoundEffect(int effectId);boolean performHapticFeedback(int effectId, boolean always);}final Callbacks mRootCallbacks;AttachInfo(IWindowSession session, IWindow window, Display display,ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer,Context context) {mSession = session;mWindow = window;mWindowToken = window.asBinder();mDisplay = display;mViewRootImpl = viewRootImpl;mHandler = handler;mRootCallbacks = effectPlayer;//View的playSoundEffect方法所用到的回调对象就是这个mTreeObserver = new ViewTreeObserver(context);}}}

AttachInfo构造方法的最后一个参数很关键,因为View的playSoundEffect方法所调用的对象就是这个,结合ViewRootImpl的代码我们可以知道是ViewRootImpl实现了这个回调。

4、ViewRootImpl的playSoundEffect方法如下所示。

public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {public void playSoundEffect(int effectId) {checkThread();//检测是否是UI线程try {final AudioManager audioManager = getAudioManager();switch (effectId) {case SoundEffectConstants.CLICK://播放按键点击音效audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);return;case SoundEffectConstants.NAVIGATION_DOWN:audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN);return;case SoundEffectConstants.NAVIGATION_LEFT:audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT);return;case SoundEffectConstants.NAVIGATION_RIGHT:audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT);return;case SoundEffectConstants.NAVIGATION_UP:audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP);return;default:throw new IllegalArgumentException("unknown effect id " + effectId +" not defined in " + SoundEffectConstants.class.getCanonicalName());}} catch (IllegalStateException e) {// Exception thrown by getAudioManager() when mView is nullLog.e(mTag, "FATAL EXCEPTION when attempting to play sound effect: " + e);e.printStackTrace();}}
}

ViewRootImpl的playSoundEffect方法首先会检测一下当前线程是不是UI线程,然后会根据传入的effectId类型来判断要播放那种音效。因为View的performClick方法传入的是SoundEffectConstants.CLICK,所以会触发audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK)。

4、AudioManager的playSoundEffect方法如下所示。

public class AudioManager {public void  playSoundEffect(int effectType) {//检测音效类型是否合规if (effectType < 0 || effectType >= NUM_SOUND_EFFECTS) {return;}//确定音效是否可用if (!querySoundEffectsEnabled(Process.myUserHandle().getIdentifier())) {return;}//获取AudioService服务final IAudioService service = getService();try {//调用服务的playSoundEffect方法service.playSoundEffect(effectType);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}/*** Settings has an in memory cache, so this is fast.*/private boolean querySoundEffectsEnabled(int user) {return Settings.System.getIntForUser(getContext().getContentResolver(),Settings.System.SOUND_EFFECTS_ENABLED, 0, user) != 0;}
}

AudioManager的playSoundEffect会做一些校验,如果校验通过则会获取AudioService服务对象,并调用该对象的playSoundEffect方法进行音效播放。

5、AudioService和playSoundEffect相关的代码如下所示。

public class AudioService extends IAudioService.Stubimplements AccessibilityManager.TouchExplorationStateChangeListener,AccessibilityManager.AccessibilityServicesStateChangeListener {/*** 播放音效* @param effectType*/public void playSoundEffect(int effectType) {playSoundEffectVolume(effectType, -1.0f);}public void playSoundEffectVolume(int effectType, float volume) {// do not try to play the sound effect if the system stream is mutedif (isStreamMutedByRingerOrZenMode(STREAM_SYSTEM)) {return;}if (effectType >= AudioManager.NUM_SOUND_EFFECTS || effectType < 0) {Log.w(TAG, "AudioService effectType value " + effectType + " out of range");return;}//发送播放音效的消息给mAudioHandlersendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_QUEUE,effectType, (int) (volume * 1000), null, 0);}private class AudioHandler extends Handler {//加载音效private boolean onLoadSoundEffects() {...代码暂时省略...}@Overridepublic void handleMessage(Message msg) {switch (msg.what) {...代码省略...case MSG_UNLOAD_SOUND_EFFECTS:onUnloadSoundEffects();//不加载音效break;case MSG_LOAD_SOUND_EFFECTS://加载音效boolean loaded = onLoadSoundEffects();//调用onLoadSoundEffects加载音效,并将加载结果赋值给loaded...代码省略...break;case MSG_PLAY_SOUND_EFFECT://播放音效onPlaySoundEffect(msg.arg1, msg.arg2);break;...代码省略...}}
}

AudioService的playSoundEffect方法进一步调用playSoundEffectVolume,该方法会发送播放音效的消息MSG_PLAY_SOUND_EFFECT给mAudioHandler,最终会触发onPlaySoundEffect方法。

6、AudioService的onPlaySoundEffec方法如下所示。

public class AudioService extends IAudioService.Stubimplements AccessibilityManager.TouchExplorationStateChangeListener,AccessibilityManager.AccessibilityServicesStateChangeListener {private void onPlaySoundEffect(int effectType, int volume) {synchronized (mSoundEffectsLock) {onLoadSoundEffects();//加载音效if (mSoundPool == null) {return;}float volFloat;// use default if volume is not specified by callerif (volume < 0) {volFloat = (float)Math.pow(10, (float)sSoundEffectVolumeDb/20);} else {volFloat = volume / 1000.0f;}if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {//通过SoundPool播放音效mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1],volFloat, volFloat, 0, 0, 1.0f);} else {//通过MediaPlayer播放音效MediaPlayer mediaPlayer = new MediaPlayer();try {String filePath = getSoundEffectFilePath(effectType);mediaPlayer.setDataSource(filePath);mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);mediaPlayer.prepare();mediaPlayer.setVolume(volFloat);mediaPlayer.setOnCompletionListener(new OnCompletionListener() {public void onCompletion(MediaPlayer mp) {cleanupPlayer(mp);}});mediaPlayer.setOnErrorListener(new OnErrorListener() {public boolean onError(MediaPlayer mp, int what, int extra) {cleanupPlayer(mp);return true;}});mediaPlayer.start();} catch (IOException ex) {Log.w(TAG, "MediaPlayer IOException: "+ex);} catch (IllegalArgumentException ex) {Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);} catch (IllegalStateException ex) {Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);}}}}}

7、简单回顾一下以上步骤。
点击控件,播放音效资源

相关文章:

Android 9系统源码_音频管理(一)按键音效源码解析

前言 当用户点击Android智能设备的按钮的时候&#xff0c;如果伴随有按键音效的话&#xff0c;会给用户更好的交互体验。本期我们将会结合Android系统源码来具体分析一下控件是如何发出按键音效的。 一、系统加载按键音效资源 1、在TV版的Android智能设备中&#xff0c;我们…...

PyTorch搭建神经网络

PyTorch版本&#xff1a;1.12.1PyTorch官方文档PyTorch中文文档 PyTorch中搭建并训练一个神经网络分为以下几步&#xff1a; 定义神经网络定义损失函数以及优化器训练&#xff1a;反向传播、梯度下降 下面以LeNet-5为例&#xff0c;搭建一个卷积神经网络用于手写数字识别。 …...

TiDB 优雅关闭

背景 今天使用tiup做实验的事后&#xff0c;将tidb节点从2个缩到1个&#xff0c;发现tiup返回成功但是tidb-server进程还在。 这就引发的我的好奇心&#xff0c;why&#xff1f; 实验复现 启动集群 #( 07/31/23 8:32下午 )( happyZBMAC-f298743e3 ):~/docker/tiup/tiproxy…...

食品厂能源管理系统助力节能减排,提升可持续发展

随着全球能源问题的日益突出&#xff0c;食品厂作为能源消耗较大的行业&#xff0c;如何有效管理和利用能源成为了一项重要任务。引入食品厂能源管理系统可以帮助企业实现节能减排&#xff0c;提高能源利用效率&#xff0c;同时也符合可持续发展的理念。 食品厂能源管理系统的…...

ABAP读取文本函数效率优化,read_text --->zread_text

FUNCTION zread_text. *“---------------------------------------------------------------------- "“本地接口&#xff1a; *” IMPORTING *” VALUE(CLIENT) LIKE SY-MANDT DEFAULT SY-MANDT *" VALUE(ID) LIKE THEAD-TDID *" VALUE(LANGUAGE) LIKE THEAD-…...

Spring Data Repository 使用详解

8.1. 核心概念 Spring Data repository 抽象的中心接口是 Repository。它把要管理的 domain 类以及 domain 类的ID类型作为泛型参数。这个接口主要是作为一个标记接口&#xff0c;用来捕捉工作中的类型&#xff0c;并帮助你发现扩展这个接口的接口。 CrudRepository 和 ListCr…...

[ MySQL ] — 数据库环境安装、概念和基本使用

目录 安装MySQL 获取mysql官⽅yum源 安装mysql yum 源 安装mysql服务 启动服务 登录 方法1&#xff1a;获取临时root密码 方法2&#xff1a;无密码 方法3&#xff1a;跳过密码认证 配置my.cnf 卸载环境 设置开机启动(可以不设) 常见问题 安装遇到秘钥过期的问题&…...

Apache Thrift C++库的TThreadPoolServer模式的完整示例

1. 本程序功能 1) 要有完整的request 和 response; 2) 支持多进程并行处理任务; 3)子进程任务结束后无僵尸进程 2.Apache Thrift C++库的编译和安装 见 步步详解:Apache Thrift C++库从编译到工作模式DEMO_北雨南萍的博客-CSDN博客 3.框架生成 数据字段定义: cat D…...

图解java.util.concurrent并发包源码系列——深入理解ReentrantLock,看完可以吊打面试官

图解java.util.concurrent并发包源码系列——深入理解ReentrantLock&#xff0c;看完可以吊打面试官 ReentrantLock是什么&#xff0c;有什么作用ReentrantLock的使用ReentrantLock源码解析ReentrantLock#lock方法FairSync#tryAcquire方法NonfairSync#tryAcquire方法 Reentrant…...

【计算机网络】网络基础(上)

文章目录 1. 网络发展认识协议 2.网络协议初识协议分层OSI七层模型 | TCP/IP网络传输基本流程情况1&#xff1a;同一个局域网(子网)数据在两台通信机器中如何流转协议报头的理解局域网通信原理(故事版本)一般原理数据碰撞结论 情况2&#xff1a;跨一个路由器的两个子网IP地址与…...

51单片机(普中HC6800-EM3 V3.0)实验例程软件分析 实验四 蜂鸣器

目录 前言 一、原理图及知识点介绍 1.1、蜂鸣器原理图&#xff1a; 二、代码分析 前言 第一个实验:51单片机&#xff08;普中HC6800-EM3 V3.0&#xff09;实验例程软件分析 实验一 点亮第一个LED_ManGo CHEN的博客-CSDN博客 第二个实验:51单片机&#xff08;普中HC6800-EM…...

无向图-已知根节点求高度

深搜板子题&#xff0c;无向图&#xff0c;加边加两个&#xff0c;dfs输入两个参数变量&#xff0c;一个是当前深搜节点&#xff0c;另一个是父节点&#xff08;避免重复搜索父节点&#xff09;&#xff0c;恢复现场 ///首先完成数组模拟邻接表#include<iostream> #incl…...

RIP动态路由协议 (已过时,逐渐退出舞台)

RIP 路由更新&#xff1a;RIP1/2 每30秒钟广播(255.255.255.255)/组播 &#xff08;224.0.0.9&#xff09;一次超时&#xff1a;180秒未收到更新&#xff0c;即标记为不可用&#xff08;跳数16&#xff09;&#xff0c;240秒收不到&#xff0c;即从路由表中删除 &#xff1b;跳…...

C++ operator关键字的使用(重载运算符、仿函数、类型转换操作符)

目录 定义operator重载运算符operator重载函数调用运算符operator类型转换操作符 定义 C11 中&#xff0c;operator 是一个关键字&#xff0c;用于重载运算符。通过重载运算符&#xff0c;您可以定义自定义类型的对象在使用内置运算符时的行为。 operator重载用法一般可以分为…...

深度学习笔记-暂退法(Drop out)

背景 在机器学习的模型中&#xff0c;如果模型的参数太多&#xff0c;而训练样本又太少&#xff0c;训练出来的模型很容易产生过拟合的现象。在训练神经网络的时候经常会遇到过拟合的问题&#xff0c;过拟合具体表现在&#xff1a;模型在训练数据上损失函数较小&#xff0c;预…...

使用自适应去噪在线顺序极限学习机预测飞机发动机剩余使用寿命(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

实验5-7 使用函数求1到10的阶乘和 (10 分)

实验5-7 使用函数求1到10的阶乘和 &#xff08;10 分&#xff09; 本题要求实现一个计算非负整数阶乘的简单函数&#xff0c;使得可以利用该函数&#xff0c;计算1!2!⋯10!的值。 函数接口定义&#xff1a; double fact( int n ); 其中n是用户传入的参数&#xff0c;其值不超过…...

kafka部署

1.kafka安装部署 1.1 kafaka下载 https://archive.apache.org/dist/kafka/2.4.0/kafka_2.12-2.4.0.tgz Binary downloads是指预编译的软件包,可供直接下载和安装,无需手动编译。在计算机领域中,二进制下载通常指预构建的软件分发包,可以直接安装在系统上并使用 "2.…...

Spring Security6入门及自定义登录

一、前言 Spring Security已经更新到了6.x,通过本专栏记录以下Spring Security6学习过程&#xff0c;当然大家可参考Spring Security5专栏对比学习 Spring Security5专栏地址&#xff1a;security5 Spring Security是spring家族产品中的一个安全框架&#xff0c;核心功能包括…...

开放式蓝牙耳机哪个品牌好用?盘点几款很不错的开放式耳机

​相比传统入耳式耳机&#xff0c;开放式耳机因其不入耳不伤耳的开放设计&#xff0c;不仅带来了舒适的佩戴体验&#xff0c;还创造了一种与周围环境互动的全新方式&#xff0c;户外运动过程时也无需担心发生事故&#xff0c;安全性更高。我整理了几款比较好用的开放式耳机给大…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

Reasoning over Uncertain Text by Generative Large Language Models

https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...

Golang——7、包与接口详解

包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...