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智能设备的按钮的时候,如果伴随有按键音效的话,会给用户更好的交互体验。本期我们将会结合Android系统源码来具体分析一下控件是如何发出按键音效的。 一、系统加载按键音效资源 1、在TV版的Android智能设备中,我们…...

PyTorch搭建神经网络
PyTorch版本:1.12.1PyTorch官方文档PyTorch中文文档 PyTorch中搭建并训练一个神经网络分为以下几步: 定义神经网络定义损失函数以及优化器训练:反向传播、梯度下降 下面以LeNet-5为例,搭建一个卷积神经网络用于手写数字识别。 …...
TiDB 优雅关闭
背景 今天使用tiup做实验的事后,将tidb节点从2个缩到1个,发现tiup返回成功但是tidb-server进程还在。 这就引发的我的好奇心,why? 实验复现 启动集群 #( 07/31/23 8:32下午 )( happyZBMAC-f298743e3 ):~/docker/tiup/tiproxy…...

食品厂能源管理系统助力节能减排,提升可持续发展
随着全球能源问题的日益突出,食品厂作为能源消耗较大的行业,如何有效管理和利用能源成为了一项重要任务。引入食品厂能源管理系统可以帮助企业实现节能减排,提高能源利用效率,同时也符合可持续发展的理念。 食品厂能源管理系统的…...
ABAP读取文本函数效率优化,read_text --->zread_text
FUNCTION zread_text. *“---------------------------------------------------------------------- "“本地接口: *” 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类型作为泛型参数。这个接口主要是作为一个标记接口,用来捕捉工作中的类型,并帮助你发现扩展这个接口的接口。 CrudRepository 和 ListCr…...

[ MySQL ] — 数据库环境安装、概念和基本使用
目录 安装MySQL 获取mysql官⽅yum源 安装mysql yum 源 安装mysql服务 启动服务 登录 方法1:获取临时root密码 方法2:无密码 方法3:跳过密码认证 配置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,看完可以吊打面试官 ReentrantLock是什么,有什么作用ReentrantLock的使用ReentrantLock源码解析ReentrantLock#lock方法FairSync#tryAcquire方法NonfairSync#tryAcquire方法 Reentrant…...

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

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

无向图-已知根节点求高度
深搜板子题,无向图,加边加两个,dfs输入两个参数变量,一个是当前深搜节点,另一个是父节点(避免重复搜索父节点),恢复现场 ///首先完成数组模拟邻接表#include<iostream> #incl…...
RIP动态路由协议 (已过时,逐渐退出舞台)
RIP 路由更新:RIP1/2 每30秒钟广播(255.255.255.255)/组播 (224.0.0.9)一次超时:180秒未收到更新,即标记为不可用(跳数16),240秒收不到,即从路由表中删除 ;跳…...
C++ operator关键字的使用(重载运算符、仿函数、类型转换操作符)
目录 定义operator重载运算符operator重载函数调用运算符operator类型转换操作符 定义 C11 中,operator 是一个关键字,用于重载运算符。通过重载运算符,您可以定义自定义类型的对象在使用内置运算符时的行为。 operator重载用法一般可以分为…...

深度学习笔记-暂退法(Drop out)
背景 在机器学习的模型中,如果模型的参数太多,而训练样本又太少,训练出来的模型很容易产生过拟合的现象。在训练神经网络的时候经常会遇到过拟合的问题,过拟合具体表现在:模型在训练数据上损失函数较小,预…...

使用自适应去噪在线顺序极限学习机预测飞机发动机剩余使用寿命(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
实验5-7 使用函数求1到10的阶乘和 (10 分)
实验5-7 使用函数求1到10的阶乘和 (10 分) 本题要求实现一个计算非负整数阶乘的简单函数,使得可以利用该函数,计算1!2!⋯10!的值。 函数接口定义: double fact( int n ); 其中n是用户传入的参数,其值不超过…...

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学习过程,当然大家可参考Spring Security5专栏对比学习 Spring Security5专栏地址:security5 Spring Security是spring家族产品中的一个安全框架,核心功能包括…...

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

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...

深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...
Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解
文章目录 一、开启慢查询日志,定位耗时SQL1.1 查看慢查询日志是否开启1.2 临时开启慢查询日志1.3 永久开启慢查询日志1.4 分析慢查询日志 二、使用EXPLAIN分析SQL执行计划2.1 EXPLAIN的基本使用2.2 EXPLAIN分析案例2.3 根据EXPLAIN结果优化SQL 三、使用SHOW PROFILE…...

解析“道作为序位生成器”的核心原理
解析“道作为序位生成器”的核心原理 以下完整展开道函数的零点调控机制,重点解析"道作为序位生成器"的核心原理与实现框架: 一、道函数的零点调控机制 1. 道作为序位生成器 道在认知坐标系$(x_{\text{物}}, y_{\text{意}}, z_{\text{文}}…...