Android音量调节参考一

基于android 9平台分析。
在Android系统中,默认的设备(phone等)音量都是分开控制的,这些包括媒体、铃声、闹铃、蓝牙、通话通过音频流来区别不同的音量类型。每种流类型都定义最大音量、最小音量及默认音量,Android 9定了了11中音频流类型:

流类型

//frameworks/base/media/java/android/media/AudioSystem.javapublic static final String[] STREAM_NAMES = new String[] {"STREAM_VOICE_CALL","STREAM_SYSTEM","STREAM_RING","STREAM_MUSIC","STREAM_ALARM","STREAM_NOTIFICATION","STREAM_BLUETOOTH_SCO","STREAM_SYSTEM_ENFORCED","STREAM_DTMF","STREAM_TTS","STREAM_ACCESSIBILITY"};

最大音量(音量等级)

//frameworks/base/services/core/java/com/android/server/audio/AudioService.java/** Maximum volume index values for audio streams */protected static int[] MAX_STREAM_VOLUME = new int[] {5,  // STREAM_VOICE_CALL7,  // STREAM_SYSTEM7,  // STREAM_RING15, // STREAM_MUSIC7,  // STREAM_ALARM7,  // STREAM_NOTIFICATION15, // STREAM_BLUETOOTH_SCO7,  // STREAM_SYSTEM_ENFORCED15, // STREAM_DTMF15, // STREAM_TTS15  // STREAM_ACCESSIBILITY};

根据音量曲线表,一般情况音量等级最大可以设置为100。但是,有些音频音量调节并不经过音箱曲线表,而是直接调用HAL层的set_volume,而HAL层对音量又做了类似“音量曲线”的转换。所有修改音量级别,可能会有以下问题:

1、调至15时音量已经最大,15级以上的音量等级无效。

比如amlogic T972将MAX_STREAM_VOLUME 调整为30等级,HAL层audio_hw.c对音量调节:out_set_volume()–>volume2Ms12DBGain()–>AmplToDb(),因15等级时DB值已经“够大”,再往上调音量变化不明显,修改如下:

 2、调节音量时音量过大导致输出波形失真。

因喇叭性能或功放电路的原因(最好改电路,否则产品声音小),CPU音量输出增益不能太大,否则引起波形失真。比如Mst358在HAL层audio_hw.c对音量调节:

 最小音量

//frameworks/base/services/core/java/com/android/server/audio/AudioService.java/** Minimum volume index values for audio streams */protected static int[] MIN_STREAM_VOLUME = new int[] {1,  // STREAM_VOICE_CALL0,  // STREAM_SYSTEM0,  // STREAM_RING0,  // STREAM_MUSIC1,  // STREAM_ALARM0,  // STREAM_NOTIFICATION0,  // STREAM_BLUETOOTH_SCO0,  // STREAM_SYSTEM_ENFORCED0,  // STREAM_DTMF0,  // STREAM_TTS1   // STREAM_ACCESSIBILITY};

设置最小音量的目的是有些音频不能单独设置为静音。

默认音量

//frameworks/base/media/java/android/media/AudioSystem.javapublic static int[] DEFAULT_STREAM_VOLUME = new int[] {4,  // STREAM_VOICE_CALL7,  // STREAM_SYSTEM0,  // STREAM_RING5, // STREAM_MUSIC0,  // STREAM_ALARM5,  // STREAM_NOTIFICATION7,  // STREAM_BLUETOOTH_SCO7,  // STREAM_SYSTEM_ENFORCED5, // STREAM_DTMF5, // STREAM_TTS5, // STREAM_ACCESSIBILITY};

音频流映射StreamAlias

不同设备的映射定义不同,系统中一共定义三种设备的音频流的映射,分别是STREAM_VOLUME_ALIAS_VOICE,STREAM_VOLUME_ALIAS_TELEVISION,STREAM_VOLUME_ALIAS_DEFAULT

StreamAlias存在的意义:流类型别名,某音频流的行为等同于另外一种音频流,可以将它映射为另一种音频流,比如AudioSystem.STREAM_RING用作AudioSystem.STREAM_MUSIC来处理。

StreamAlias实际使用的意义是,Android为了兼容各种设备,定义了尽可能多的音频流。但是,在有些简单的设备中,可能仅有一个喇叭,所以对音频操作没有必要区分音频流。所以通过StreamAlias,在手机和平板上实际上能调节的就是五种音量,在TV和就机顶盒之类设备能调节的仅仅一种音量即music,故如有需求需要统一音量的可以将当前的音频流改为TV类型。
调用AudioSsytem::getPlatformType()可知道系统是手机、平板或TV类型。

//frameworks/base/services/core/java/com/android/server/audio/AudioService.java
1.voice--具有语音功能的设备,电话等
private final int[] STREAM_VOLUME_ALIAS_VOICE = new int[] {AudioSystem.STREAM_VOICE_CALL,      // STREAM_VOICE_CALLAudioSystem.STREAM_RING,            // STREAM_SYSTEMAudioSystem.STREAM_RING,            // STREAM_RINGAudioSystem.STREAM_MUSIC,           // STREAM_MUSICAudioSystem.STREAM_ALARM,           // STREAM_ALARMAudioSystem.STREAM_RING,            // STREAM_NOTIFICATIONAudioSystem.STREAM_BLUETOOTH_SCO,   // STREAM_BLUETOOTH_SCOAudioSystem.STREAM_RING,            // STREAM_SYSTEM_ENFORCEDAudioSystem.STREAM_RING,            // STREAM_DTMFAudioSystem.STREAM_MUSIC            // STREAM_TTS
};
2. television--电视机顶盒或投影设备
private final int[] STREAM_VOLUME_ALIAS_TELEVISION = new int[] {AudioSystem.STREAM_MUSIC,       // STREAM_VOICE_CALLAudioSystem.STREAM_MUSIC,       // STREAM_SYSTEMAudioSystem.STREAM_MUSIC,       // STREAM_RINGAudioSystem.STREAM_MUSIC,       // STREAM_MUSICAudioSystem.STREAM_MUSIC,       // STREAM_ALARMAudioSystem.STREAM_MUSIC,       // STREAM_NOTIFICATIONAudioSystem.STREAM_MUSIC,       // STREAM_BLUETOOTH_SCOAudioSystem.STREAM_MUSIC,       // STREAM_SYSTEM_ENFORCEDAudioSystem.STREAM_MUSIC,       // STREAM_DTMFAudioSystem.STREAM_MUSIC        // STREAM_TTS
};
3. default--平板之类的设备
private final int[] STREAM_VOLUME_ALIAS_DEFAULT = new int[] {AudioSystem.STREAM_VOICE_CALL,      // STREAM_VOICE_CALLAudioSystem.STREAM_RING,            // STREAM_SYSTEMAudioSystem.STREAM_RING,            // STREAM_RINGAudioSystem.STREAM_MUSIC,           // STREAM_MUSICAudioSystem.STREAM_ALARM,           // STREAM_ALARMAudioSystem.STREAM_RING,            // STREAM_NOTIFICATIONAudioSystem.STREAM_BLUETOOTH_SCO,   // STREAM_BLUETOOTH_SCOAudioSystem.STREAM_RING,            // STREAM_SYSTEM_ENFORCEDAudioSystem.STREAM_RING,            // STREAM_DTMFAudioSystem.STREAM_MUSIC            // STREAM_TTS
};

音量按键处理流程

 在Android平台上,音量键,主页键(home),都是全局按键,但是主页键是个例外不能被应用所捕获。

下面分析一下音量按键的流程,主要从framework层处理开始,至于EventHub 从驱动的/dev/input/event0获取按键信息到上抛属于Android input 系统方面的流程。

framework层接收音量按键

ViewRootImpl.processKeyEvent 处理Activity 上面收到的按键

private int processKeyEvent(QueuedInputEvent q) {final KeyEvent event = (KeyEvent)q.mEvent;if (mUnhandledKeyManager.preViewDispatch(event)) {return FINISH_HANDLED;}// Deliver the key to the view hierarchy.if (mView.dispatchKeyEvent(event)) {return FINISH_HANDLED;}if (shouldDropInputEvent(q)) {return FINISH_NOT_HANDLED;}。。。。。}

从中可以看到mView.dispatchKeyEvent(event),完成将按键发送给Activity处理,由于每个Activity都是view的子类,所有这些按键将dispatchKeyEvent传递给onKeyDown:

   /*** Called to process key events.  You can override this to intercept all* key events before they are dispatched to the window.  Be sure to call* this implementation for key events that should be handled normally.** @param event The key event.** @return boolean Return true if this event was consumed.*/public boolean dispatchKeyEvent(KeyEvent event) {onUserInteraction();// Let action bars open menus in response to the menu key prioritized over// the window handling itfinal int keyCode = event.getKeyCode();if (keyCode == KeyEvent.KEYCODE_MENU &&mActionBar != null && mActionBar.onMenuKeyEvent(event)) {return true;}Window win = getWindow();if (win.superDispatchKeyEvent(event)) {return true;}View decor = mDecor;if (decor == null) decor = win.getDecorView();return event.dispatch(this, decor != null? decor.getKeyDispatcherState() : null, this);}

注意这个方法可以被子类覆盖。
win.superDispatchKeyEvent()不处理音量键,调用根View的dispatchKeyEvent,进而调用ViewGroup的dispatchKeyEvent,如果都没处理,则调用View的dispatchKeyEvent:

    public boolean dispatchKeyEvent(KeyEvent event) {if (mInputEventConsistencyVerifier != null) {mInputEventConsistencyVerifier.onKeyEvent(event, 0);}// Give any attached key listener a first crack at the event.//noinspection SimplifiableIfStatementListenerInfo li = mListenerInfo;if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {return true;}if (event.dispatch(this, mAttachInfo != null? mAttachInfo.mKeyDispatchState : null, this)) {return true;}if (mInputEventConsistencyVerifier != null) {mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);}return false;}

通过event.dispatch进一步分发

//framework/base/core/java/android/view/KeyEvent.javapublic final boolean dispatch(Callback receiver, DispatcherState state,Object target) {switch (mAction) {case ACTION_DOWN: {mFlags &= ~FLAG_START_TRACKING;if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state+ ": " + this);boolean res = receiver.onKeyDown(mKeyCode, this);if (state != null) {if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) {if (DEBUG) Log.v(TAG, "  Start tracking!");state.startTracking(this, target);} else if (isLongPress() && state.isTracking(this)) {try {if (receiver.onKeyLongPress(mKeyCode, this)) {if (DEBUG) Log.v(TAG, "  Clear from long press!");state.performedLongPress(this);res = true;}} catch (AbstractMethodError e) {}}}return res;}......}

上面只关注了ACTION_DOWN的处理。KeyEvent.dispatch通过receiver.onKeyDown将最终的按键消息发送给当前的Activity,而receiver即为KeyEvent.Callback的实现类(View的子类等等),至此如果上面上传应用处理完了就会返回,如果没有处理就会流向mFallbackEventHandler.dispatchKeyEvent(event),其实mFallbackEventHandler就是PhoneFallbackEventHandler,接着看 PhoneFallbackEventHandler.dispatchKeyEvent的处理流程
 

//framework/base/core/java/com/android/internal/policy/PhoneFallbackEventHandler.javapublic boolean dispatchKeyEvent(KeyEvent event) {final int action = event.getAction();final int keyCode = event.getKeyCode();if (action == KeyEvent.ACTION_DOWN) {return onKeyDown(keyCode, event);} else {return onKeyUp(keyCode, event);}}

进入onKeyDown

    boolean onKeyDown(int keyCode, KeyEvent event) {/* ***************************************************************************** HOW TO DECIDE WHERE YOUR KEY HANDLING GOES.* See the comment in PhoneWindow.onKeyDown* ****************************************************************************/final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState();switch (keyCode) {case KeyEvent.KEYCODE_VOLUME_UP:case KeyEvent.KEYCODE_VOLUME_DOWN:case KeyEvent.KEYCODE_VOLUME_MUTE: {handleVolumeKeyEvent(event);return true;}。。。。。}
    private void handleVolumeKeyEvent(KeyEvent keyEvent) {getMediaSessionManager().dispatchVolumeKeyEventAsSystemService(keyEvent,AudioManager.USE_DEFAULT_STREAM_TYPE);}

调用MeidaSessionManager::dispatchVolumeKeyEventAsSystemService()–>MediaSessionService::dispatchVolumeKeyEvent()

		/*将音量按钮事件分派给其中一个已注册的侦听器。如果有一个音量键长按侦听器,并且没有活动的全局优先级会话,长按将被发送到长按侦听器,而不是调整音量。如果没有注册长按监听器、没有活动的全局优先级会话,则进行音量调节*/@Overridepublic void dispatchVolumeKeyEvent(String packageName, boolean asSystemService,KeyEvent keyEvent, int stream, boolean musicOnly) {......try {synchronized (mLock) {//如果没有注册长按监听器,则调用dispatchVolumeKeyEventLocked进行音量调节。if (isGlobalPriorityActiveLocked()|| mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService,keyEvent, stream, musicOnly);} else {// TODO: Consider the case when both volume up and down keys are pressed//       at the same time.if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {if (keyEvent.getRepeatCount() == 0) {// Keeps the copy of the KeyEvent because it can be reused.mCurrentFullUserRecord.mInitialDownVolumeKeyEvent =KeyEvent.obtain(keyEvent);mCurrentFullUserRecord.mInitialDownVolumeStream = stream;mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly;mHandler.sendMessageDelayed(mHandler.obtainMessage(MessageHandler.MSG_VOLUME_INITIAL_DOWN,mCurrentFullUserRecord.mFullUserId, 0),mLongPressTimeout);}if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null) {dispatchVolumeKeyLongPressLocked(mCurrentFullUserRecord.mInitialDownVolumeKeyEvent);// Mark that the key is already handled.mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null;}dispatchVolumeKeyLongPressLocked(keyEvent);}} else { // if up......}

继续dispatchVolumeKeyEventLocked()–>dispatchAdjustVolumeLocked()

private void dispatchAdjustVolumeLocked(String packageName, int pid, int uid,boolean asSystemService, int suggestedStream, int direction, int flags) {MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession: mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession();boolean preferSuggestedStream = false;if (isValidLocalStreamType(suggestedStream)&& AudioSystem.isStreamActive(suggestedStream, 0)) {preferSuggestedStream = true;}if (session == null || preferSuggestedStream) {// Execute mAudioService.adjustSuggestedStreamVolume() on// handler thread of MediaSessionService.// This will release the MediaSessionService.mLock sooner and avoid// a potential deadlock between MediaSessionService.mLock and// ActivityManagerService lock.mHandler.post(new Runnable() {@Overridepublic void run() {try {String packageName = getContext().getOpPackageName();mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,flags, packageName, TAG);} }});} else {session.adjustVolume(packageName, pid, uid, null, asSystemService,direction, flags, true);}}

两种情况,一种调用mAudioService.adjustSuggestedStreamVolume(),一种调用session.adjustVolume()。这里以adjustSuggestedStreamVolume()为例。

AudioService音量控制流程

adjustSuggestedStreamVolume 过渡到adjustStreamVolume,进入音量设置的主要流程,主要对流类型,设备,声音设备状态,步进大小进行判断处理,另外蓝牙设备音量和主设备音量进行了控制,最后通过mVolumePanel刷新界面音量显示,并且广播通过上层应用。
 

protected void adjustStreamVolume(int streamType, int direction, int flags,String callingPackage, String caller, int uid) {。。。。。
if (adjustVolume && (direction != AudioManager.ADJUST_SAME)) {mAudioHandler.removeMessages(MSG_UNMUTE_STREAM);if (isMuteAdjust) {boolean state;if (direction == AudioManager.ADJUST_TOGGLE_MUTE) {state = !streamState.mIsMuted;} else {state = direction == AudioManager.ADJUST_MUTE;}if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {setSystemAudioMute(state);}for (int stream = 0; stream < mStreamStates.length; stream++) {if (streamTypeAlias == mStreamVolumeAlias[stream]) {if (!(readCameraSoundForced()&& (mStreamStates[stream].getStreamType()== AudioSystem.STREAM_SYSTEM_ENFORCED))) {mStreamStates[stream].mute(state);}}}} else if ((direction == AudioManager.ADJUST_RAISE) &&!checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {Log.e(TAG, "adjustStreamVolume() safe volume index = " + oldIndex);mVolumeController.postDisplaySafeVolumeWarning(flags);} else if (streamState.adjustIndex(direction * step, device, caller)|| streamState.mIsMuted) {// Post message to set system volume (it in turn will post a// message to persist).if (streamState.mIsMuted) {// Unmute the stream if it was previously mutedif (direction == AudioManager.ADJUST_RAISE) {// unmute immediately for volume upstreamState.mute(false);} else if (direction == AudioManager.ADJUST_LOWER) {if (mIsSingleVolume) {sendMsg(mAudioHandler, MSG_UNMUTE_STREAM, SENDMSG_QUEUE,streamTypeAlias, flags, null, UNMUTE_STREAM_DELAY);}}}//发送MSG_SET_DEVICE_VOLUME消息去设置系统音量,在handleMessage()被处理sendMsg(mAudioHandler,MSG_SET_DEVICE_VOLUME,SENDMSG_QUEUE,device,0,streamState,0);}int newIndex = mStreamStates[streamType].getIndex(device);// Check if volume update should be send to AVRCP//蓝牙音量的控制if (streamTypeAlias == AudioSystem.STREAM_MUSIC &&(device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&(flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {synchronized (mA2dpAvrcpLock) {if (mA2dp != null && mAvrcpAbsVolSupported) {mA2dp.setAvrcpAbsoluteVolume(newIndex / 10);}}}// Check if volume update should be send to Hearing Aidif ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {setHearingAidVolume(newIndex, streamType);}// Check if volume update should be sent to Hdmi system audio.//与HDMI输出相关if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {setSystemAudioVolume(oldIndex, newIndex, getStreamMaxVolume(streamType), flags);}if (mHdmiManager != null) {......}}int index = mStreamStates[streamType].getIndex(device);//UI更新系统音量sendVolumeUpdate(streamType, oldIndex, index, flags);}

蓝牙音量的控制

由上可知,如果当前连接了蓝牙也将对音量进行控制,mA2dp.adjustAvrcpAbsoluteVolume,暂不分析。

音频处理设置

音频处理由AudioHandler来进行,adjustStreamVolume做完相关处理后,通过sendMsg发送音量变化消息MSG_SET_DEVICE_VOLUME进入AudioHandler.handleMessage调用AudioHandler.setDeviceVolume

private void setDeviceVolume(VolumeStreamState streamState, int device) {synchronized (VolumeStreamState.class) {// Apply volumestreamState.applyDeviceVolume_syncVSS(device);// Apply change to all streams using this one as aliasint numStreamTypes = AudioSystem.getNumStreamTypes();for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {if (streamType != streamState.mStreamType &&mStreamVolumeAlias[streamType] == streamState.mStreamType) {// Make sure volume is also maxed out on A2DP device for aliased stream// that may have a different device selectedint streamDevice = getDeviceForStream(streamType);if ((device != streamDevice) && mAvrcpAbsVolSupported &&((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0)) {mStreamStates[streamType].applyDeviceVolume_syncVSS(device);}mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice);}}}// Post a persist volume msgsendMsg(mAudioHandler,MSG_PERSIST_VOLUME,SENDMSG_QUEUE,device,0,streamState,PERSIST_DELAY);}

VolumeStreamState.applyDeviceVolume_syncVSS设置设备音量

// must be called while synchronized VolumeStreamState.classpublic void applyDeviceVolume_syncVSS(int device) {int index;if (mIsMuted) {index = 0;} else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && mAvrcpAbsVolSupported) {index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);} else if ((device & mFullVolumeDevices) != 0) {index = (mIndexMax + 5)/10;} else if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {index = (mIndexMax + 5)/10;} else {index = (getIndex(device) + 5)/10;}AudioSystem.setStreamVolumeIndex(mStreamType, index, device);}

接着发送MSG_PERSIST_VOLUME消息通过handleMessage进入persistVolume,最终调用System.putIntForUser将用户设置的内容设置到Settings.system中。

AudioSystem调节音量

applyDeviceVolume处理完,AudioSystem就开始接着往下设置setStreamVolumeIndex,通过JNI调用到AudioSystem.cpp中setStreamVolumeIndex()。


status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream,int index,audio_devices_t device)
{const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();if (aps == 0) return PERMISSION_DENIED;return aps->setStreamVolumeIndex(stream, index, device);
}

获取去音频策略服务(AudioPolicyService.cpp),进行设置
AudioPolicyService::setStreamVolumeIndex()–>AudioPolicyManager::setStreamVolumeIndex()。

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

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

相关文章

点评项目——商户查询缓存

2023.12.7 redis实现商户查询缓存 在企业开发中&#xff0c;用户的访问量动辄成百上千万&#xff0c;如果没有缓存机制&#xff0c;数据库将承受很大的压力。本章我们使用redis来实现商户查询缓存。 原来的操作是根据商铺id直接从数据库查询商铺信息&#xff0c;为了防止频繁地…

python中时间戳与时间字符的相互转换

1.获取时间的方法 使用的是time模块 import time time1 time.time() # time1: 1701934920.676534 timestruct time.localtime(time1) # timestruct: time.struct_time(tm_year2023, tm_mon12, tm_mday7, tm_hour15, tm_min42, tm_sec0, tm_wday3, tm_yday341, tm_isdst0)2.…

Apache Hive(部署+SQL+FineBI构建展示)

Hive架构 Hive部署 VMware虚拟机部署 一、在node1节点安装mysql数据库 二、配置Hadoop 三、下载 解压Hive 四、提供mysql Driver驱动 五、配置Hive 六、初始化元数据库 七、启动Hive(Hadoop用户) chown -R hadoop:hadoop apache-hive-3.1.3-bin hive 阿里云部…

【STM32单片机】简易电子琴设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用STM32F103C8T6单片机控制器&#xff0c;使用数码管模块、矩阵按键、无源蜂鸣器等。 主要功能&#xff1a; 系统运行后&#xff0c;蜂鸣器播放一首音乐&#xff0c;进入电子琴模式&#xff0c;…

无公网IP环境Windows系统使用VNC远程连接Deepin桌面

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《Linux》《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;…

MySQL Connector/J 数据库连接 URL的语法

详情请参考&#xff1a;https://dev.mysql.com/doc/connector-j/en/connector-j-reference-jdbc-url-format.html jdbc:mysql:是用于普通的、基本的故障转移连接使用&#xff1a; jdbc:mysql://[host][,failoverhost...][:port]/[database][?propertyName1][propertyValue1]…

联想范建平:联想混合AI架构具备两大明显优势

12月7日&#xff0c;首届AI PC创新论坛在北京联想集团总部举办。联想集团副总裁、联想研究院人工智能实验室负责人范建平表示&#xff0c;为提供真正可信、个性化的AI专属服务&#xff0c;联想提出了混合智能&#xff08;Hybrid AI&#xff09;概念&#xff0c;并已经显现出更强…

thinkphp 多表连接 子查询 group by 分组以最新的一条数据为组

用这个 $subQuery Db::name(wms_orderitems)->distinct(true)->field(kw_id,orders_id,product_id)->order(items_id desc)->buildSql();$list Db::name(wms_orders)->alias(order)->join($subQuery. item,item.orders_idorder.orders_id)->field(order…

【AntDB 数据库】国产分布式数据库发展趋势与难点

引言&#xff1a; 日前&#xff0c;为更好地满足亚信科技客户对于数据管理的需求&#xff0c;提高通用型数据库的产品服务能力与业务拓展能力&#xff0c;亚信科技分布式数据库AntDB发布V7.0版本产品&#xff0c;助力运营商核心系统实现全方位的自主可控与业务系统的平稳上线。…

搭建部署Hadoop2.x和3.x的区别

文章目录 2.x 和 3.x 的区别Java最小支持版本常用的端口号配置文件Classpath隔离NodeManager重连 进入官网自行查阅 2.x 和 3.x 的区别 Java最小支持版本 Hadoop 2.x&#xff1a;2.7 版本需要 Java 7&#xff0c;2.6 以及更早期版本支持 Java 6Hadoop 3.x&#xff1a;最低要求…

配置BFD状态与接口状态联动示例

BFD简介 定义 双向转发检测BFD&#xff08;Bidirectional Forwarding Detection&#xff09;是一种全网统一的检测机制&#xff0c;用于快速检测、监控网络中链路或者IP路由的转发连通状况。 目的 为了减小设备故障对业务的影响&#xff0c;提高网络的可靠性&#xff0c;网…

深度学习实战65-人脸检测模型LFFD的搭建,LFFD模型的架构与原理的详细介绍

大家好,我是微学AI,今天给大家介绍一下深度学习实战65-人脸检测模型LFFD的搭建,LFFD模型的架构与原理的详细介绍。LFFD(Light and Fast Face Detector)模型是一种用于人脸检测的深度学习模型,其设计旨在实现轻量级和快速的人脸检测。本文将详细介绍LFFD模型的定义、优点、原…

【Linux】进程见通信之匿名管道pipe

1.匿名管道的特点 以下管道的统称仅代表匿名管道。 管道是一个只能单向通信的通信信道。为了实现进程间通信.管道是面向字节流的。仅限于父子通信或者具有血缘关系的进程进行进程见通信。管道自带同步机制&#xff0c;原子性写入。管道的生命周期是随进程的。 2.匿名管道通信…

SpringSecurity(五)

深入理解HttpSecurity的设计 一、HttpSecurity的应用 在前章节的介绍中我们讲解了基于配置文件的使用方式&#xff0c;也就是如下的使用。 也就是在配置文件中通过 security:http 等标签来定义了认证需要的相关信息&#xff0c;但是在SpringBoot项目中&#xff0c;我们慢慢脱离…

Java零基础——RocketMQ篇

1.RocketMQ简介 官网&#xff1a; http://rocketmq.apache.org/ RocketMQ是阿里巴巴2016年MQ中间件&#xff0c;使用Java语言开发&#xff0c;RocketMQ 是一款开源的分布式消息系统&#xff0c;基于高可用分布式集群技术&#xff0c;提供低延时的、高可靠的消息发布与订阅服…

03_阿里云_配置OSS环境变量

关于aliyunOSS文件上传的系统变量配置 问题引出 在黑马程序员2023新版JavaWeb开发教程教程中&#xff0c;P148Day11-04. 案例-文件上传-阿里云OSS-准备到P150Day11-06. 案例-文件上传-阿里云OSS-集成阿里云给的参考代码已经更新了&#xff0c;需要配置阿里云的用户变量&#…

K8S 删除命令空间时 一直卡住怎么办?

当使用完一个命名空间后&#xff0c;想删除了又删除不掉&#xff0c;这个时候查看命名空间的状态一直是Terminating。使用强制删除&#xff0c;也是还是不行。&#xff08;找了好多办法都不行&#xff09; [rootk8s-master kubernetes-yaml]# kubectl delete ns mem-example Er…

各种滤波算法的比较(GF、KF、EKF、UKF、PF)

目录 一、前言 二、滤波算法介绍 1、GF&#xff08;高斯滤波&#xff09; 2、KF&#xff08;卡尔曼滤波&#xff09; 3、EKF&#xff08;可扩展卡尔曼滤波&#xff09; 4、UKF&#xff08;无迹卡尔曼滤波&#xff09; 5、PF&#xff08;粒子滤波&#xff09; 三、不同滤…

elementui中添加开关控制

<template><!-- 图层管理 --><div class"home-wrapper"><div class"table-list"><div class"list"><el-table :data"tableData" height"100%" style"width: 100%;" border>&…

数字营销影响消费者行为的 6 种方式

如果您正在考虑转向在线市场&#xff0c;那么这个决定就好了&#xff01;没有什么比数字营销更强大的了。但是&#xff0c;在开始之前&#xff0c;请了解数字营销如何影响消费者行为。由于客户是任何企业的基石&#xff0c;因此跟踪消费者行为至关重要。 数据分析在识别潜在客…