一文了解 Android 车机如何处理中控的旋钮输入?
前言
上篇文章《从实体按键看 Android 车载的自定义事件机制》带大家了解了 Android 车机支持自定义输入的机制 CustomInputService
。事实上,除了支持自定义事件,对于中控上常见的音量控制、焦点控制的旋钮事件,Android 车机也是支持的。
那本篇文章带大家看下 Android 车机处理旋钮事件的内在原理:
- 定义
- 监听和订阅
- 接收
- 处理
- 模拟
1. 定义
和自定义输入所支持的事件一致,支持旋钮输入的事件类型也在如下文件 types.hal 中定义。
// hardware/interfaces/automotive/vehicle/2.0/types.hal/*** Property to feed H/W rotary events to android* ...*/HW_ROTARY_INPUT = (0x0A20| VehiclePropertyGroup:SYSTEM| VehiclePropertyType:INT32_VEC| VehicleArea:GLOBAL),enum RotaryInputType : int32_t {ROTARY_INPUT_TYPE_SYSTEM_NAVIGATION = 0,ROTARY_INPUT_TYPE_AUDIO_VOLUME = 1,
};
HW_ROTARY_INPUT
代表该事件在底层的 Property
定义,供 VehicleHal
对其发起监听。
该事件涵盖了一些旋钮所必须的数据:
- 第 0 位代表哪种旋钮硬件,由
RotaryInputType
枚举细分,包括控制焦点的旋钮 TYPE_SYSTEM_NAVIGATION 和控制音量的旋钮 TYPE_AUDIO_VOLUME - 第 1 位代表旋转计数,正数代表顺时针计数 clockwise,负数代表逆时针计数 counterclockwise
- 第 2 位代表旋钮事件的目标屏幕
VehicleDisplay
,默认是 MAIN,即 center console,中控屏幕 - 第 3 位及以后代表持续计数事件之间的时间差,单位为 ns
2. 监听和订阅
上层处理事件输入的 CarInputService
在初始化的时候,会向调度车机输入的中间层 InputHalService
注册监听。
// packages/services/Car/service/src/com/android/car/CarInputService.java
public class CarInputService ... {...@Overridepublic void init() {if (!mInputHalService.isKeyInputSupported()) {return;}mInputHalService.setInputListener(this);...}...
}
InputHalService 判断支持旋钮输入的话,向和 HAL 层交互的 VehicleHal
注册 HW_ROTARY_INPUT
Property 的订阅。
// packages/services/Car/service/src/com/android/car/hal/InputHalService.java
public class InputHalService extends HalServiceBase {...public void setInputListener(InputListener listener) {...boolean rotaryInputSupported;synchronized (mLock) {mListener = listener;...rotaryInputSupported = mRotaryInputSupported;}...if (rotaryInputSupported) {mHal.subscribeProperty(this, HW_ROTARY_INPUT);}...}public boolean isRotaryInputSupported() {synchronized (mLock) {return mRotaryInputSupported;}}...
}
3. 接收
当旋钮事件发生,将通过 HAL 层抵达上述订阅该 Property 的 VehicleHal,其将找出处理方 HalServiceBase
即 InputHalService
并继续分发。
// packages/services/Car/service/src/com/android/car/hal/VehicleHal.java
public class VehicleHal implements HalClientCallback {...@Overridepublic void onPropertyEvent(ArrayList<HalPropValue> propValues) {synchronized (mLock) {for (int i = 0; i < propValues.size(); i++) {HalPropValue v = propValues.get(i);int propId = v.getPropId();HalServiceBase service = mPropertyHandlers.get(propId);if (service == null) {continue;}service.getDispatchList().add(v);mServicesToDispatch.add(service);VehiclePropertyEventInfo info = mEventLog.get(propId);if (info == null) {info = new VehiclePropertyEventInfo(v);mEventLog.put(propId, info);} else {info.addNewEvent(v);}}}for (HalServiceBase s : mServicesToDispatch) {s.onHalEvents(s.getDispatchList());s.getDispatchList().clear();}mServicesToDispatch.clear();}...
}
InputHalService 首先确保上层的 InputListener
确实存在,此后再检查该 HalProperty 是何种类型。HW_ROTARY_INPUT 旋钮事件的话调用 dispatchRotaryInput() 继续。
public class InputHalService extends HalServiceBase {...@Overridepublic void onHalEvents(List<HalPropValue> values) {InputListener listener;synchronized (mLock) {listener = mListener;}if (listener == null) {return;}for (int i = 0; i < values.size(); i++) {HalPropValue value = values.get(i);switch (value.getPropId()) {case HW_ROTARY_INPUT:dispatchRotaryInput(listener, value);break;...}}}...
}
dispatchRotaryInput()
将执行如下步骤:
- 检查必要数据是否齐全,即起码包括旋钮硬件类型、旋钮计数、目标屏幕这 3 位
- 按照 index 取出这三位数据
- 检查旋钮计数是否为 0,因为无法判断 0 是顺时针还是逆时针
- 检查目标屏幕是否为中控屏幕 MAIN、仪表屏幕 INSTRUMENT_CLUSTER 中的一个
- 检查旋钮计数的时间差数值位数是否匹配(比如:旋转了 3 格的话,那么时间差必须要占 2 位)
- 根据旋钮硬件类型转化为
CarInputManager
中定义的事件类型- 焦点控制的话转换为 INPUT_TYPE_ROTARY_NAVIGATION
- 音量控制的话转换为 INPUT_TYPE_ROTARY_VOLUME
- 提取持续计数的时间差到 timestamps 数组中
- 根据旋钮计数方向,转换到的事件类型以及时间差数组封装
RotaryEvent
对象交由 InputListener 继续分发
public class InputHalService extends HalServiceBase {...private void dispatchRotaryInput(InputListener listener, HalPropValue value) {int timeValuesIndex = 3; // remaining values are time deltas in nanosecondsif (value.getInt32ValuesSize() < timeValuesIndex) {return;}int rotaryInputType = value.getInt32Value(0);int detentCount = value.getInt32Value(1);int vehicleDisplay = value.getInt32Value(2);long timestamp = value.getTimestamp(); // for first detent, uptime nanosecondsboolean clockwise = detentCount > 0;detentCount = Math.abs(detentCount);if (detentCount == 0) { // at least there should be one eventreturn;}if (vehicleDisplay != VehicleDisplay.MAIN&& vehicleDisplay != VehicleDisplay.INSTRUMENT_CLUSTER) {return;}if (value.getInt32ValuesSize() != (timeValuesIndex + detentCount - 1)) {return;}int carInputManagerType;switch (rotaryInputType) {case ROTARY_INPUT_TYPE_SYSTEM_NAVIGATION:carInputManagerType = CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION;break;case ROTARY_INPUT_TYPE_AUDIO_VOLUME:carInputManagerType = CarInputManager.INPUT_TYPE_ROTARY_VOLUME;break;default: ...}long[] timestamps = new long[detentCount];long uptimeToElapsedTimeDelta = CarServiceUtils.getUptimeToElapsedTimeDeltaInMillis();...RotaryEvent event = new RotaryEvent(carInputManagerType, clockwise, timestamps);listener.onRotaryEvent(event, convertDisplayType(vehicleDisplay));}...
}
4. 处理
监听章节里提到 InputListener 为 CarInputService,所以将传递到 CarInputService 的 onRotaryEvent() 进行处理。
onRotaryEvent() 先检查是否有使用 InputEventCapture 监听旋钮事件的 Service 存在:
- 如果有监听,交由 Capture 该事件的 Service 专门处理
- 如果没有,转换为 Android 标准 KeyEvent 进行处理
// packages/services/Car/service/src/com/android/car/CarInputService.java
public class CarInputService ... {...@Overridepublic void onRotaryEvent(RotaryEvent event, @DisplayTypeEnum int targetDisplay) {if (!mCaptureController.onRotaryEvent(targetDisplay, event)) {List<KeyEvent> keyEvents = rotaryEventToKeyEvents(event);for (KeyEvent keyEvent : keyEvents) {onKeyEvent(keyEvent, targetDisplay);}}}...
}
专门处理
Car App 提供了一个专门控制焦点的 RotaryService
,它在绑定时通过 CarInputManager 的 requestInputEventCapture()
申请监听了 INPUT_TYPE_ROTARY_NAVIGATION 类型的旋钮事件。
// packages/apps/Car/RotaryController/src/com/android/car/rotary/RotaryService.java
public class RotaryService ... {/** Input types to capture. */private final int[] mInputTypes = new int[]{CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION,...};...@Overridepublic void onServiceConnected() {super.onServiceConnected();mCar = Car.createCar(this, null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,(car, ready) -> {mCar = car;if (ready) {mCarInputManager =(CarInputManager) mCar.getCarManager(Car.CAR_INPUT_SERVICE);...mCarInputManager.requestInputEventCapture(CarOccupantZoneManager.DISPLAY_TYPE_MAIN,mInputTypes,CarInputManager.CAPTURE_REQ_FLAGS_ALLOW_DELAYED_GRANT,/* callback= */ this);}});...}...
}
自然的,RotaryService 的 onRotaryEvent() 会得到调用,首先将检查目标屏幕是否符合预期,必须是 MAIN 即中控屏幕。通过的话,调用 handleRotaryEvent()
继续处理。
public class RotaryService ... {...@Overridepublic void onRotaryEvents(int targetDisplayType, @NonNull List<RotaryEvent> events) {if (!isValidDisplayType(targetDisplayType)) {return;}for (RotaryEvent rotaryEvent : events) {handleRotaryEvent(rotaryEvent);}}private static boolean isValidDisplayType(int displayType) {if (displayType == CarOccupantZoneManager.DISPLAY_TYPE_MAIN) {return true;}return false;}...
}
handleRotaryEvent() 将检查 RotaryEvent 中的硬件 type,确保确实来自于焦点控制旋钮 INPUT_TYPE_ROTARY_NAVIGATION,通过的话调用 handleRotateEvent()
继续。
public class RotaryService ... {...private void handleRotaryEvent(RotaryEvent rotaryEvent) {if (rotaryEvent.getInputType() != CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION) {return;}boolean clockwise = rotaryEvent.isClockwise();int count = rotaryEvent.getNumberOfClicks();long eventTime = rotaryEvent.getUptimeMillisForClick(count - 1);handleRotateEvent(clockwise, count, eventTime);}...
}
handleRotateEvent() 主要是依据屏幕的设置和当前 focus 的 Node 情况来决定是调用 performScrollAction()
执行屏幕滚动,还是寻找到目标 Node 调用 performFocusAction()
来执行焦点的移动。
其本质上是通过 InputManager
向系统注入 SCROLL 触摸事件,或者通过 Accessibility
向上面的或下面的待 focus 的 AccessibilityNode
发送 FOCUS Action 操作。
public class RotaryService ... {...private void handleRotateEvent(boolean clockwise, int count, long eventTime) {int rotationCount = getRotateAcceleration(count, eventTime);if (mInProjectionMode) {injectMotionEvent(DEFAULT_DISPLAY, clockwise ? rotationCount : -rotationCount);return;}if (initFocus() || mFocusedNode == null) {return;}if (mInDirectManipulationMode) {if (DirectManipulationHelper.supportRotateDirectly(mFocusedNode)) {performScrollAction(mFocusedNode, clockwise);} else {AccessibilityWindowInfo window = mFocusedNode.getWindow();if (window == null) {L.w("Failed to get window of " + mFocusedNode);return;}int displayId = window.getDisplayId();window.recycle();injectMotionEvent(displayId, clockwise ? rotationCount : -rotationCount);}return;}int remainingRotationCount = rotationCount;int direction = clockwise ? View.FOCUS_FORWARD : View.FOCUS_BACKWARD;Navigator.FindRotateTargetResult result =mNavigator.findRotateTarget(mFocusedNode, direction, rotationCount);if (result != null) {if (performFocusAction(result.node)) {remainingRotationCount -= result.advancedCount;}Utils.recycleNode(result.node);} else {L.w("Failed to find rotate target from " + mFocusedNode);}if (remainingRotationCount > 0 && isInFocusedWindow(mFocusedNode)) {AccessibilityNodeInfo scrollableContainer =mNavigator.findScrollableContainer(mFocusedNode);if (scrollableContainer != null) {injectScrollEvent(scrollableContainer, clockwise, remainingRotationCount);scrollableContainer.recycle();}}}...
}
标准处理
和导航旋钮事件不同,系统没有 Capture 音量旋钮事件 INPUT_TYPE_ROTARY_VOLUME 的 Service,那么它得执行标准处理。
首先,得将 RotatryEvent 转换为标准的按键编号 Key Code,具体的执行如下逻辑:
- 焦点控制按钮的话,依据方向 mapping 顺时针为焦点前进的 KEYCODE_NAVIGATE_NEXT,逆时针为焦点后退的 KEYCODE_NAVIGATE_PREVIOUS
- 音量控制按钮的话,mapping 为音量 +/- Key Code,顺时针为 KEYCODE_VOLUME_UP,逆时针则是 KEYCODE_VOLUME_DOWN
- 按照计数次数批量调用
createKeyEvent()
创建 KeyEvent 对象,并添加到待处理 keyEvents 列表中。
public class CarInputService ... {...private static List<KeyEvent> rotaryEventToKeyEvents(RotaryEvent event) {int numClicks = event.getNumberOfClicks();int numEvents = numClicks * 2; // up / down per each clickboolean clockwise = event.isClockwise();int keyCode;switch (event.getInputType()) {case CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION:keyCode = clockwise? KeyEvent.KEYCODE_NAVIGATE_NEXT: KeyEvent.KEYCODE_NAVIGATE_PREVIOUS;break;case CarInputManager.INPUT_TYPE_ROTARY_VOLUME:keyCode = clockwise? KeyEvent.KEYCODE_VOLUME_UP: KeyEvent.KEYCODE_VOLUME_DOWN;break;...}ArrayList<KeyEvent> keyEvents = new ArrayList<>(numEvents);for (int i = 0; i < numClicks; i++) {long uptime = event.getUptimeMillisForClick(i);KeyEvent downEvent = createKeyEvent(/* down= */ true, uptime, uptime, keyCode);KeyEvent upEvent = createKeyEvent(/* down= */ false, uptime, uptime, keyCode);keyEvents.add(downEvent);keyEvents.add(upEvent);}return keyEvents;} ...
}
接着,遍历准备好的 keyEvents 列表,逐个处理。
public class CarInputService ... {...@Overridepublic void onRotaryEvent(RotaryEvent event, @DisplayTypeEnum int targetDisplay) {if (!mCaptureController.onRotaryEvent(targetDisplay, event)) {List<KeyEvent> keyEvents = rotaryEventToKeyEvents(event);// 遍历列表,逐个处理for (KeyEvent keyEvent : keyEvents) {onKeyEvent(keyEvent, targetDisplay);}}}...
}
CarInputService 的 onKeyEvent() 直接处理的 Code 只有激活语音助手的 KEYCODE_VOICE_ASSIST 和拨打电话的 KEYCODE_CALL。其他的 Key Code 执行一般处理:
- 如果目标屏幕是 INSTRUMENT_CLUSTER 即仪表屏幕的话,调用
handleInstrumentClusterKey()
让InstrumentClusterKeyListener
执行仪表上的事件,貌似是Cluster
app 完成,具体不再展开 - 检查是否有使用 InputEventCapture 监听 NAVIGATE_ 焦点控制、VOLUME_ 音量控制 KeyEvent 的 Service 存在,有的话回调
onKeyEvent()
Callback - 如果没有 Capture 处理的好,告知
KeyEventListener
进行兜底处理
public class CarInputService ... {...@Overridepublic void onKeyEvent(KeyEvent event, @DisplayTypeEnum int targetDisplayType) {// Special case key code that have special "long press" handling for automotiveswitch (event.getKeyCode()) {case KeyEvent.KEYCODE_VOICE_ASSIST:handleVoiceAssistKey(event);return;case KeyEvent.KEYCODE_CALL:handleCallKey(event);return;default:break;}assignDisplayId(event, targetDisplayType);// Allow specifically targeted keys to be routed to the clusterif (targetDisplayType == CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER&& handleInstrumentClusterKey(event)) {return;}if (mCaptureController.onKeyEvent(targetDisplayType, event)) {return;}mMainDisplayHandler.onKeyEvent(event);}...
}
KeyEventListener 在 CarInputService 初始化的时候指定,具体的就是通过 InputManagerHelper
注入 KeyEvent。
public class CarInputService ... {...private final KeyEventListener mMainDisplayHandler;public CarInputService( ... ) {this(context, inputHalService, userService, occupantZoneService, bluetoothService,new Handler(CarServiceUtils.getCommonHandlerThread().getLooper()),context.getSystemService(TelecomManager.class),event -> InputManagerHelper.injectInputEvent(context.getSystemService(InputManager.class), event),() -> Calls.getLastOutgoingCall(context),() -> getViewLongPressDelay(context),() -> context.getResources().getBoolean(R.bool.config_callButtonEndsOngoingCall),new InputCaptureClientController(context));}...
}
InputManagerHelper 没啥特别的,直接调用 InputManager
的标准方法 injectInputEvent()
完成注入,后续由 InputManagerService
开始 Dispatch、Transport 等一系列处理。
// packages/services/Car/car-builtin-lib/src/android/car/builtin/input/InputManagerHelper.java
public class InputManagerHelper {...public static boolean injectInputEvent(@NonNull InputManager inputManager,@NonNull android.view.InputEvent event) {return inputManager.injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);}
}
5. 模拟
当旋钮按键环境尚未到位的时候,我们可以使用 adb 命令模拟旋钮事件来验证代码链路。
格式:
adb shell cmd car_service inject-rotary [-d display] [-i input_type] [-c clockwise] [-dt delta_times_ms]
- display,目标屏幕:0 代表中控屏幕,1 代表仪表屏幕,默认是 0
- input_type,按钮类型: 10 代表焦点控制,11 代表音量控制,默认是 10
- clockwise,旋钮方向: true 代表顺时针方向,false 代表逆时针,默认是 false
- delta_times_ms,持续旋转计数的时间间隔:多次旋转事件和当前时刻的间隔列表,按降序排列,默认是 0,表示只有一次旋转
下面将介绍几个命令示例,帮助大家更好地理解该命令的使用。
adb shell cmd car_service inject-rotary
没有指定任何参数,全部都是默认的操作,表示针对中控屏幕发送焦点控制的旋钮事件,方向为逆时针、焦点后退 1 格。
adb shell cmd car_service inject-rotary -d 1 -i 11 -c true
表示针对仪表屏幕发送音量控制的旋钮事件,方向为顺时针、调低 1 格。
adb shell cmd car_service inject-rotary -c true -dt 100 50
表示针对中控屏幕发送焦点控制的旋钮事件,方向为顺时针、3 次计数、焦点前进 3 格。
结语
与自定义输入相比,旋钮事件的处理流程有细微差异,主要体现在 CarInputService
会针对音量、焦点两种的旋钮控制,存在特定的处理逻辑。最后,结合一张图回顾下整体流程:
-
支持音量控制和焦点控制的两种旋钮硬件产生
HW_ROTARY_INPUT
Propery 变化 -
由和 HAL 层交互的
VehicleHal
订阅到 Propery 变化,将事件提取为HalPropValue
类型 -
并发送给车机输入的中间服务
InputHalService
接收和进一步地封装为RotaryEvent
类型 -
分发到处理事件输入的专用服务
CarInputService
:a. 如果有 Capture 音量/焦点的 Rotary 事件的交由其专门处理:Car App 的
RotaryService
,其将决定通过InputManager
注入 SCROLL 滚动还是通过Accessibility
触发焦点 Focus 操作;b. 如果没有,则执行标准处理:
- 首先按照 Rotary 类型和旋钮方向、计数封装为 Android 标准
KeyEvent
列表 - 如果目标屏幕为仪表的话,列表交由
Cluster
App 处理 - 反之检查是否有 Capture 该 KeyEvent 的 Service 需要处理
- 最后交由 InputManager 逐个注入该 KeyEvent,继而由系统的
InputManagerService
进行调度
- 首先按照 Rotary 类型和旋钮方向、计数封装为 Android 标准
推荐阅读
- 从实体按键看 Android 车载的自定义事件机制
- 如何打造车载语音交互:Google Voice Interaction 给你答案
- Android 车机初体验:Auto,Automotive 傻傻分不清楚?
参考文档
- https://developer.android.google.cn/training/cars
- https://source.android.google.cn/docs/devices/automotive/hmi/rotary_controller/app_developers
相关文章:

一文了解 Android 车机如何处理中控的旋钮输入?
前言 上篇文章《从实体按键看 Android 车载的自定义事件机制》带大家了解了 Android 车机支持自定义输入的机制 CustomInputService。事实上,除了支持自定义事件,对于中控上常见的音量控制、焦点控制的旋钮事件,Android 车机也是支持的。 那…...

小红书推广 方法总结
大家好,我是网媒智星,今天跟大家分享一下小红书的推广方法和经验。 一、平台简介 1、什么是小红书? 小红书是一个消费决策/生活方式平台,用户可以通过图片、文案、视频等方式分享美好生活。 2、用户画像 - 2亿月活跃…...

通讯录的实现(超详细)——C语言(进阶)
目录 一、创建联系人信息(结构体) 二、创建通讯录(结构体) 三、define定义常量 四、打印通讯录菜单 五、枚举菜单选项 六、初始化通讯录 七、实现通讯的的功能 7.1 增加加联系人 7.2 显示所有联系人的信息 7.3 单独查…...

3D 渲染技巧-如何创建高质量写实渲染?
掌握创建高质量建筑渲染和任何 3D 渲染的艺术是一项复杂且需要技巧的工作,通常需要多年的经验和实践。实现逼真的结果需要仔细考虑众多因素,并避免可能导致缺乏真实性的假渲染效果的常见错误。 避免常见错误 - 提升渲染游戏的技巧 在追求创建真正逼真的…...
fastadmin采坑之获取当前登录admin用户的信息
在controller层里想要获取当前登录admin用户的信息 print_r($this->auth->getUserInfo());但是有个问题 我在fa_admin表中添加了新的字段,这个方法获取不到新字段的数值,具体也没有去研究估计跟方法有关 然后我直接用模型去获取数据,简…...

【Spring AOP + 自定义注解 + 动态数据源 实现主从库切换读写分离】—— 案例实战
💧 S p r i n g A O P 主从数据源切换 读写分离 自定义注解案例实战! \color{#FF1493}{Spring AOP 主从数据源切换 读写分离 自定义注解 案例实战!} SpringAOP主从数据源切换读写分离自定义注解案例实战!💧 …...

【LeetCode每日一题合集】2023.7.24-2023.7.30
文章目录 771. 宝石与石头代码1——暴力代码2——位运算集合⭐(英文字母的long集合表示) 2208. 将数组和减半的最少操作次数(贪心 优先队列)2569. 更新数组后处理求和查询⭐⭐⭐⭐⭐(线段树)TODO2500. 删除…...

《吐血整理》进阶系列教程-拿捏Fiddler抓包教程(14)-Fiddler断点(breakpoints)实战,篡改或伪造数据
1.简介 上一篇主要就讲解和分享Fiddler断点的理论和操作,今天宏哥就用具体例子,将上一篇中的理论知识实践一下。而且在实际测试过程中,有时候需要修改请求或响应数据,或者直接模拟服务器响应,此时可以使用fiddler进行…...

ELK + Fliebeat + Kafka日志系统
参考: ELKFilebeatKafka分布式日志管理平台搭建_51CTO博客_elk 搭建 ELK 日志分析系统概述及部署(上)-阿里云开发者社区 ELK是三个开源软件的缩写,分别表示:Elasticsearch , Logstash, Kibana , 它们都是开源软件。…...

Scaling Instruction-Finetuned Language Models
Paper name Scaling Instruction-Finetuned Language Models Paper Reading Note Paper URL: https://arxiv.org/pdf/2210.11416.pdf TL;DR 2022 年谷歌出的文章,对指令微调的影响因素进行分析,提出了一些提升指令微调效果的方案。与该文章一起出品…...

rust 闭包函数
函数有自己的类型,可以像使用基础类型一样使用函数,包括将函数保存在变量中、保存在 vec 中、声明在结构体成员字段中。闭包函数也是函数,也有自己的类型定义。不过,函数实际上是指针类型,在 rust 所有权中属于借用的关…...

MySQL 实现分库和分表的备份 2023.7.29
1、分库备份 [rootlocalhost mysql-backup]# cat db_bak.sh #!/bin/bash k_userroot bak_password123456 bak_path/root/mysql-backup/ bak_cmd"-u$bak_user -p$bak_password" exc_db"Database|information_schema|mysql|performance_schema|sys" dbname…...
20230728----重返学习-跨域-模块化-webpack初步
day-122-one-hundred-and-twenty-two-20230728-跨域-模块化-webpack初步 跨域 跨域 为什么要跨域? 浏览器为了安全,不能让我们的html文件可以随意引用别的服务器中的文件,只允许我们的html或js文件中,请求我们自己服务器。这个…...
[SQL挖掘机] - 多表连接: union all
介绍: sql中的union all是用于合并两个或多个select语句的结果集的操作符。与union不同的是,union all不会自动去除重复的行,它会简单地将多个查询的结果集合并在一起,包括重复的行。 用法: union all的基本语法如下: select_…...

TypeError: run() got an unexpected keyword argument ‘hide_label‘ yolov5最新版本报错
报错展示 解决方法 把detect.py中的如上部分的 --hide-label改为 --hide-labels,成功解决....
什么是Java中的集成测试?
Java中的集成测试(Integration Test)是一种测试方法,用于测试多个模块或组件之间的交互和集成。在Java中,集成测试通常使用单元测试框架(如JUnit)编写和运行。 对于初学者来说,集成测试可能有些…...
打卡力扣题目二
#左耳听风 ARST 打卡活动重启# 目录 一、问题 二、 解题方法一 三、enumerate函数介绍 关于 ARTS 的释义 —— 每周完成一个 ARTS: ● Algorithm: 每周至少做一个 LeetCode 的算法题 ● Review: 阅读并点评至少一篇英文技术文章 ● Tips: 学习至少一个技术技巧 …...

【Qt】QML-02:QQuickView用法
1、先看demo QtCreator自动生成的工程是使用QQmlApplicationEngine来加载qml文件,下面的demo将使用QQuickView来加载qml文件 #include <QGuiApplication> #include <QtQuick/QQuickView>int main(int argc, char *argv[]) {QGuiApplication app(argc,…...

【IDEA】idea不自动生成target
文章目录 1. 不生成target2. 仅部分文件不生成target2.1. 一般原因就是资源没有设置2.2. 配置编译src/main/java文件夹下的资源文件2.3. 清理缓存(王炸) 3. 参考资料 本文描述idea不生成target的几种情况以及处理方法 1. 不生成target 像下图这样根本就…...

从官网认识 JDK,JRE,JVM 三者的关系
点击下方关注我,然后右上角点击...“设为星标”,就能第一时间收到更新推送啦~~~ JVM 是一些大厂面试必问点,要想解决 OOM、性能调优方面的问题,掌握 JVM 知识必不可少,从今天开始,将为大家介绍 JVM 的常用知…...

学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...

高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

android13 app的触摸问题定位分析流程
一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...
0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化
是不是受够了安装了oracle database之后sqlplus的简陋,无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话,配置.bahs_profile后也能解决上下翻页这些,但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可,…...

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

高分辨率图像合成归一化流扩展
大家读完觉得有帮助记得关注和点赞!!! 1 摘要 我们提出了STARFlow,一种基于归一化流的可扩展生成模型,它在高分辨率图像合成方面取得了强大的性能。STARFlow的主要构建块是Transformer自回归流(TARFlow&am…...

[拓扑优化] 1.概述
常见的拓扑优化方法有:均匀化法、变密度法、渐进结构优化法、水平集法、移动可变形组件法等。 常见的数值计算方法有:有限元法、有限差分法、边界元法、离散元法、无网格法、扩展有限元法、等几何分析等。 将上述数值计算方法与拓扑优化方法结合&#…...
算法刷题-回溯
今天给大家分享的还是一道关于dfs回溯的问题,对于这类问题大家还是要多刷和总结,总体难度还是偏大。 对于回溯问题有几个关键点: 1.首先对于这类回溯可以节点可以随机选择的问题,要做mian函数中循环调用dfs(i&#x…...