车载音视频App框架设计
简介
统一播放器提供媒体播放一致性的交互和视觉体验,减少各个媒体应用和场景独自开发的重复工作量,实现媒体播放链路的一致性,减少碎片化的Bug。本文面向应用开发者介绍如何快速接入媒体播放器。
主要功能:
- 新设计的统一播放UI组件,视频支持的手势操作,包括左右滑拖动进度、左上下滑调节亮度、右上下滑调节音量、双击暂停/播放,同时对外暴露了长按手势接口应用可以实现类似快进快退功能;
- 支持方控、Mini播放器、PSD的播控和状态显示,应用只需要申请媒体中心权限的license即可,支持媒体中心的状态保持和语音控制通道,需要APP中接入对应的接口;
- 支持视频行车娱乐限制,非P档播放视频会暂停播放弹框提示;
- 使用原生的的media3 1.3.0版本,支持原生的androidx 和framework media session;
- 支持音频焦点自管理,包括电话状态的播控也基于音频焦点统一处理;
整体架构图如下
- 应用通过配置UI,然后创建MediaController播放媒体;
- controler端接入了行车娱乐限制的检测和提示;
- service端实现了google原生的media3 session service;
- 媒体中心通过mediasession和播放器连接;
- 底层使用media3 exoplayer播放和音频焦点管理;
接入流程
1、配置依赖
implementation 'com.max.mediaplayer:uniteplayer:1.x.x
备注:uniteplayer中包含了视频控制UI组件,当只需要修复控制UI组件的bug时,可以更新整体uniteplayer播放器组件,也可以只增加UI组件的依赖,比如:implementation (‘com.max.media:media-video:1.2.0’)。
2、初始化
建议在application创建的时候调用初始化接口:
init(context: Context, isVideo: Boolean, enableMediaCenter: Boolean = false)
参数 | 说明 |
---|---|
context | 应用context |
isVideo | 是否视频应用,视频会初始化车机相关的adapterapi,音乐目前不会初始化 |
enableMediaCenter | 需要远程方控、语音控制、mini播放器控制这些能力时设置为true,否则为false,默认为false |
3、接入 UI 组件 (视频应用)
这里的UI组件只针对视频应用,音频应用的UI组件在媒体组件库中,参考媒体视频组件。
1)配置PlayerView
在布局中增加FlexPlayerView,视频内容显示和播控UI都在此view中,一般默认配置如下即可:
<com.max.uniteplayer.ui.FlexPlayerViewandroid:id="@+id/player_view"android:layout_width="match_parent"android:layout_height="match_parent"/>
其中播控UI是可单独接入的,若需定制,接入见视频组件接入文档。
2)配置视频渲染view
一般情况下不需要特别配置,支持的定制视频显示view参数如下:
比如在上面FlexPlayerView xml中用app:surface_type=“surface_view”
surface_type:surface类型,取值如下,默认是surface_view类型:
<attr name="surface_type" format="enum"> <enum name="none" value="0"/> <enum name="surface_view" value="1"/> <enum name="texture_view" value="2"/> <enum name="spherical_gl_surface_view" value="3"/> <enum name="video_decoder_gl_surface_view" value="4"/></attr>
resize_mode,取值如下,默认fit模式:
<attr name="resize_mode" format="enum"> <enum name="fit" value="0"/> <enum name="fixed_width" value="1"/><enum name="fixed_height" value="2"/> <enum name="fill" value="3"/> <enum name="zoom" value="4"/> </attr>
first_render_delay,首帧显示延迟时长,用于规避首帧渲染慢闪拉伸的问题,int型,单位ms,一舨不需要设置,默认0;
3)配置视频播控View
FlexPlayerView本身对外暴露了两个接口:
接口 | 说明 |
---|---|
fun setTitleBarVisible(visible: Boolean) | 因此视频播放器中顶部title和关闭按钮,一般应用自己实现的可以通过此接口隐藏 |
fun enableUpDownGesture(enable: Boolean) | 使能上下滑动,默认播放器是支持的,可以设置false关闭 |
bindControlView(listener: FlexPlayerView.ControlViewListener) | 获取视频播控view控制器,提供更多配置能力,比如增加手势监听回调、添加自定义的播控按钮等,具体可以参考视频组件接入文档。 |
4、使用播放器播放
有3种播放接口,选择其中一个:
1)FlexSimplePlayer
封装的最高层播放接口,内部封装好了原生MediaItem对象,简单的视频播放场景推荐使用此接口:
class FlexSimplePlayer(context: Context?,isVideo: Boolean,enableParkDetect: Boolean = true,enableParkDialog: Boolean = true,controllerCallback: FlexControllerCallback? = null)
参数说明:
参数 | 说明 |
---|---|
context | 上下文; |
enableParkDetect | 只针对视频生效,是否启用行车娱乐限制,非驻车档会自动暂停视频播放(默认是强制要求的); |
enableParkDialog | 只针对视频生效,是否弹出行车娱乐限制框,设置为disable后需要应用可以自己实现提示界面; |
开始播放:
player.startPlay(context: Context,mediaData: List<MediaData>,view: FlexPlayerView?,listener: FlexMediaListener?,mediaCenterData: MediaCenterData? = null)
参数说明:
参数 | 说明 |
---|---|
context | 上下文,注意视频播放要使用activity的context,因为行车娱乐限制需要弹框 |
mediaData | 媒体列表,每个媒体item包括url、metadata、mimetiype,其中url是媒体地址,可以是本地、在线点播或直播地址,medadata是media3的原始类型,title和artist字段会显示在mini播放器和PSD上,mimeTypes指定媒体类型,url是对应后缀的无需设置,有些比如优酷投屏中有一个直播url是/m3u8结尾 而不是.m3u8结尾 需要设置mimeTypes = MimeTypes.APPLICATION_M3U8; |
view | 类型为FlexPlayerView,自实现UI的此参数设置为null |
listener | 媒体播放监听回调,具体回调接口后面详细展开,不需要监听设置为null |
mediaCenterData | 媒体中心中需要用的数据,比如应用包名、图标等,具体类型后面详细展开,这里需要注意的是sourceType要设置为6,在媒体中心中对应SourceType.SOURCE_TYPE_ONLINE。 |
controllerCallback | 媒体中心回调的方法,包括播放/暂停、上/下曲切换,在此回调中可以实现自定义处理逻辑,并屏蔽底层播放器的响应; |
退出界面停止播放,停止播放后会释放所有播放资源:
player.stopPlay()
页面切换的处理建议:
视频应用界面退到后台(onPuse)暂停: player.pause()
视频应用从后台回到前台(onResume): player.play()
更新播放列表:
fun updateMediaItems(mediaData: List<MediaData>)
在已经进入播放状态下,更换播放的视频建议使用该接口,避免重新startPlay()会更慢。
2)FlexMediaController
音频类应用复杂场景建议使用该接口。
FlexSimplePlayer下层的接口,使用该接口需要自己创建MediaItem,mimeType需要调用util接口设置。适合需要自己构建比较复杂的mediaitem场景,另外没有直接提供暂停和播放接口,需要调用成员player的暂停和播放接口。
构造方法解释同FlexSimplePlayer。
开始播放:
fun startPlay(context: Context,mediaItems: List<MediaItem>,view: FlexPlayerView?,listener: FlexMediaListener?,customData: MediaCenterData?,controllerCallback: FlexControllerCallback? = null)
参数说明:
参数 | 说明 |
---|---|
context | 上下文,注意视频播放要使用activity的context,因为行车娱乐限制需要弹框 |
mediaItems | 媒体列表,原生类型,包括媒体url,metadata,mimetype等 |
view | 类型为FlexPlayerView,自实现UI的此参数设置为null |
listener | 媒体播放监听回调,具体回调接口后面详细展开,不需要监听设置为null |
mediaCenterData | 媒体中心中需要用的数据,比如应用包名、图标等,具体类型后面详细展开,这里需要注意的是sourceType要设置为6,在媒体中心中对应SourceType.SOURCE_TYPE_ONLINE。 |
controllerCallback | 媒体中心回调的方法,包括播放/暂停、上/下曲切换,在此回调中可以实现自定义处理逻辑,并屏蔽底层播放器的响应; |
FlexControllerCallback接口:
interface FlexControllerCallback {companion object {const val KEY_ACTION_TYPE = "actionType"const val KEY_CALLBACK_RESULT = "callbackResult"const val TYPE_PLAY = 1const val TYPE_NEXT = 2const val TYPE_PREVIOUS = 3const val RESULT_OK = 0const val RESULT_BLOCK = 1}fun onDefaultCallback(bundle: Bundle): Bundle?{return Bundle.EMPTY}fun onPlayAction(): Int?{return RESULT_OK}fun onSeekToNext(): Int?{return RESULT_OK}fun onSeekToPrevious(): Int?{return RESULT_OK}
}
接口描述:
接口 | 说明 |
---|---|
onPlayAction(): Int? | 媒体中心调用过来的播放/暂停接口 |
onSeekToNext(): Int? | 媒体中心调用过来的下一首接口 |
onSeekToPrevious(): Int? | 媒体中心调用过来的上一首接口 |
退出界面停止播放,停止播放后会释放所有播放资源:
player.stopPlay()
页面切换的处理建议:
视频应用界面退到后台(onPuse)暂停: player.player?.pause()
视频应用从后台回到前台(onResume): player.player?.play()
更新播放列表:
fun updateMediaItems(mediaItems: List<MediaItem>)
在已经进入播放状态下,更换播放的视频建议使用该接口,避免重新startPlay()会更慢。
3)FlexExoPlayerController
此接口一般不会用到,前面两个接口默认会启动sessionservice,支持媒体中心的连接,此接口直接返回exoplayer播放器,不会创建mediasession,不支持媒体中心连接,支持视频行车娱乐限制。使用媒体UI组件和播放器需要在应用中自行对接,适用于定制化比较多,不需要远程播放支持的视频播放场景,目前只用在赛道模式。建议优先选用前面两种方式播放。
构造方法解释同FlexSimplePlayer。
fun getExoPlayer(audioFocus: Boolean = false,mediaListener: FlexMediaListener? = null): ExoPlayer?
参数和返回值:
参数 | 说明 |
---|---|
audioFocus | 是否打开音频焦点自管理,默认是false。在赛道模式情况存在两个视频同时播的情况,需要设置为false否则因为音频焦点抢占不能同时播放,其他场景建议设置为true; |
mediaListener | 状态回调,同前面两个接口,具体参数后面详细描述; |
返回值 | media3 ExoPlayer对象。 |
5、状态回调接口
自定义的播放回调接口:
interface FlexMediaListener{//player加载完成fun onLoadPlayerFinished(player: Player?) {}//返回true不会自动播放,需要调用play()后才能播放,默认falsefun pauseWhenStart(): Boolean {return false}//直接返回player的状态接口,更多复杂场景建议拿player对象处理fun onPlaybackStateChange(state: Int){}fun onPlayWhenReadyChanged(ready: Boolean, reason: Int) {}fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {}//播放异常fun onPlayerError(errorCode: Int, errorData: Bundle?) {}//只针对视频,播放视频时检测到非驻车状态回调,会自动暂停播放fun onStartVideoWhenNoPark() {}//只针对视频,非驻车播放视频,弹框点击关闭按钮后的回调fun onStopVideoWhenNoPark() {}//关闭播放页面fun onClose(){}//退出应用fun onExitApp() {}//Mote: This is callback from media center, not from playerfun onSeekToNext(): Boolean {return false}fun onSeekToPrevious(): Boolean {return false}
}
接口描述:
接口 | 说明 |
---|---|
onLoadPlayerFinished(player: Player?) | 开始播放是异步的调用,此回调表示已完成了到服务端的连接返回了有效的player接口 |
fun pauseWhenStart(): Boolean | 开始播放后是否自动暂停,需要用户手动点击播放,默认false |
fun onPlaybackStateChange(state: Int) fun onPlayWhenReadyChanged(ready: Boolean, reason: Int) fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) | 原生Player接口播放状态变化的透传,state取值:Player.STATE_IDLE:还未开始播放STATE_BUFFERING:缓冲中STATE_READY:准备好播放,结合PlayWhenReady状态来判断,true为播放中,false为暂停STATE_ENDED:播放结束onPlayWhenReadyChanged取值: true为播放中,false为暂停onMediaItemTransition:切歌可以通过这里判断 |
fun onPlayerError(errorCode: Int, errorData: Bundle?) | 播放异常,errorCode时原生的错误码,errorData暂时没有用到 |
fun onStartVideoWhenNoPark() | 只针对视频,档检测到非P档播放视频时会回调此接口 |
fun onStopVideoWhenNoPark() | 只针对视频,非P播放视频弹框后,点击关闭按钮或弹框自动退出时的回调 |
fun onClose() | 用户点击左上角关闭按钮 |
fun onExitApp() | 退出应用,暂未用到 |
fun onSeekToNext() | 方控或PSD切换下一曲操作会回调,返回true表示应用来接管下一曲操作不会调用底层播放器的下一曲接口,返回false底层会调用播放器的下一曲接口, 目前投屏用到 |
fun onSeekToPrevious() | 方控或PSD切换上一曲操作会回调,返回true表示应用来接管上一曲操作不会调用底层播放器的上一曲接口,返回false底层会调用播放器的上一曲接口,,投屏用到 |
6、语音控制
媒体中心语音控制接口,声明支持的语音语义,以及处理语音控制的回调
fun declareVrSemanticsCapability(channelInfo: MCVrChannelInfo?,vrSemanticCallback: VrSemanticCallback)
参数说明:
参数 | 说明 |
---|---|
channelInfo | MCVrChannelInfo类型,具体字段: mediaPackageName: 应用包名 mediaVersion: 应用版本 mediaDescription: 应用描述 channelDataType: 通道类型,一般设置为0 semantics: IntArray 支持的语义数组,MCSemanticsType.CONTROL_PLAY等 |
vrSemanticCallback | 语音的回调,具体的实现可参考媒体中心接入文档 |
7、播放状态保持
媒体中心的状态保持功能可实现应用的自启动(通过媒体中心拉起),首先在开始播放的时候需要在mediacenterdata中设置recoveryIntent;
示例:
val intent = Intent()intent.setPackage("com.max.example")intent.action = "com.max.example.action.RecoverService"intent.putExtra("type", "StateRecover")
后面在车机重启后,通过如下接口获取之前的播放状态:
fun getRecoveryPlaybackInfo (infoCallback: Consumer<MCPlaybackInfo?>)
其中MCPlaybackInfo
类型和媒体中心中的MusicPlaybackInfo
对应,通过其中的包名可以判断上一次是否自己播放然后更新媒体中心到迷你播放器,实现重启播放恢复的功能。
8、自定义通道
1)媒体中心通道
媒体中心通道用于更新Mini播放器和PSD状态
接口:FlexMediaController.sendCustomAction(action: Int, bundle: Bundle? = null)
- 更新歌词:
val bd = Bundle()bd.putString(Constants.KEY_LYRIC_STRING, "hello lyric")mediaController?.sendCustomAction(Constants.ACTION_UPDATE_LYRIC, bd)
- 更新播放状态:
用于在非播放状态下强制更新媒体信息到媒体中心。
mediaController?.sendCustomAction(Constants.ACTION_UPDATE_MEDIACENTER, null)
2)远程MediaSession通道
远程Mediasession主要用于更新RSD的信息。
Androidx原生接口:MediaController.sendCustomCommand(command: SessionCommand, bundle: Bundle)
- 设置歌词:
val bd = Bundle()bd.putString("lyrics", lyric)bd.putString("lyrics_tr", lyricTr) //英文歌词it.sendCustomCommand(SessionCommand(Constants.COMMAND_SET_SESSION_EXTRA,Bundle.EMPTY), bd)
- 设置试看点:
val bd = Bundle()bd.putBoolean("isCanTrail", isCanTrail)//true试听歌曲,false非试听歌曲bd.putLong("trail_start", start)bd.putLong("trail_end", end)it.sendCustomCommand(SessionCommand(Constants.COMMAND_SET_SESSION_EXTRA,Bundle.EMPTY), bd)
9、更多功能使用原生Player/MediaController接口
有其他更多播放场景的需求,可以通过FlexSimplePlayer/FlexMediaController/player
直接拿到media3的原始player对象实现。
注意事项
- Media3要求工程的compileSDK>=34,这个改动会影响编译过程,不影响运行时。可能会涉及少量系统接口参数适配否则编译会报错,targetSDK建议不动;
- Player的接口调用在主线程进行,对应的状态listener回调也会在主线程中(需要在同一个线程中,否则会报异常);
- startPlay()和stopPlay()调用时机需要匹配,比如在onCreateView中调用startPlay() 在onDestoryView中调用stopPlay(),或者在startPlay()之前先调用stopPlay();
- 全屏的视频播放,activity的
decorView.windowSystemUiVisibility
需要设置View.SYSTEM_UI_FLAG_FULLSCREEN
,这样行车娱乐限制的弹框会根据全屏的状态隐藏状态栏和docker栏; - 当前版本还不支持mediasession service多进程;
参考Demo
uniteplayerdemo(视频)
音频类可参考杜比播放器和网易云音乐APP等应用,附网易云APP的播放架构:
遗留问题
1、电话中播放控制还未在框架中实现,需要应用来处理;
相关文章:

车载音视频App框架设计
简介 统一播放器提供媒体播放一致性的交互和视觉体验,减少各个媒体应用和场景独自开发的重复工作量,实现媒体播放链路的一致性,减少碎片化的Bug。本文面向应用开发者介绍如何快速接入媒体播放器。 主要功能: 新设计的统一播放U…...

StarRocks on AWS Graviton3,实现 50% 以上性价比提升
在数据时代,企业拥有前所未有的大量数据资产,但如何从海量数据中发掘价值成为挑战。数据分析凭借强大的分析能力,可从不同维度挖掘数据中蕴含的见解和规律,为企业战略决策提供依据。数据分析在营销、风险管控、产品优化等领域发挥…...
VUE中setup()
在Vue中,setup() 函数是Vue 3.0及更高版本引入的一个重要特性,它是Composition API的入口点。setup() 函数用于初始化组件的状态和逻辑,包括定义响应式数据、方法和生命周期钩子。以下是关于setup() 函数的详细解释: 1. 作用与特…...

【单元测试】SpringBoot
【单元测试】SpringBoot 1. 为什么单元测试很重要?‼️ 从前,有一个名叫小明的程序员,他非常聪明,但有一个致命的缺点:懒惰。小明的代码写得又快又好,但他总觉得单元测试是一件麻烦事,觉得代码…...

分布式搜索引擎ES-elasticsearch入门
1.分布式搜索引擎:luceneVS Solr VS Elasticsearch 什么是分布式搜索引擎 搜索引擎:数据源:数据库或者爬虫资源 分布式存储与搜索:多个节点组成的服务,提高扩展性(扩展成集群) 使用搜索引擎为搜索提供服务。可以从海量…...

TCP三次握手与四次挥手详解
1.什么是TCP TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的通信协议,属于互联网协议族(TCP/IP)的一部分。TCP 提供可靠的、顺序的、无差错的数据传输服务&…...
【Windows】操作系统之任务管理器(第一篇)
一、操作系统简介 Windows操作系统是由微软公司(Microsoft)开发的一款图形操作系统,它以其强大的功能和广泛的用户基础,成为了目前世界上用户使用最多、兼容性最强的操作系统之一。以下是关于Windows操作系统的详细介绍ÿ…...

图同构的必要条件
来源:离散数学...
Django获取request请求中的参数
支持 post put json_str request.body # 属性获取最原始的请求体数据 json_dict json.loads(json_str)# 将原始数据转成字典格式 json_dict.get("key", "默认值") # 获取数据参考 https://blog.csdn.net/user_san/article/details/109654028...

kotlin compose 实现应用内多语言切换(不重新打开App)
1. 示例图 2.具体实现 如何实现上述示例,且不需要重新打开App ①自定义 MainApplication 实现 Application ,定义两个变量: class MainApplication : Application() { object GlobalDpData { var language: String = "" var defaultLanguage: Strin…...

记录些MySQL题集(16)
MySQL 存储过程与触发器 一、初识MySQL的存储过程 Stored Procedure存储过程是数据库系统中一个十分重要的功能,使用存储过程可以大幅度缩短大SQL的响应时间,同时也可以提高数据库编程的灵活性。 存储过程是一组为了完成特定功能的SQL语句集合&#x…...
【算法基础】Dijkstra 算法
定义: g [ i ] [ j ] g[i][j] g[i][j] 表示 v i v_i vi 到 $v_j $的边权重,如果没有连接,则 g [ i ] [ j ] ∞ g[i][j] \infty g[i][j]∞ d i s [ i ] dis[i] dis[i] 表示 v k v_k vk 到节点 v i v_i vi 的最短长度, …...

使用 Flask 3 搭建问答平台(三):注册页面模板渲染
前言 前端文件下载 链接https://pan.baidu.com/s/1Ju5hhhhy5pcUMM7VS3S5YA?pwd6666%C2%A0 知识点 1. 在路由中渲染前端页面 2. 使用 JinJa 2 模板实现前端代码复用 一、auth.py from flask import render_templatebp.route(/register, methods[GET]) def register():re…...

pycharm如何debug for循环里面的错误值
一般debug时,在for循环里面的话,需要自己一步一步点。如果循环几百次那种就比较麻烦。此时可以采用try except的方式来解决 例子如下 #ptyhon debug for循环的代码 num[1,2,3,s,4] ans0 for i in num:try:ansiexcept:print(错误) print(ans) 结果如下&a…...

解决网页中的 video 标签在移动端浏览器(如百度访问网页)视频脱离文档流播放问题
问题现象 部分浏览器视频脱离文档流,滚动时,视频是悬浮出来,在顶部播放 解决方案 添加下列属性,可解决大部分浏览器的脱离文档流的问题 <videowebkit-playsinline""playsInlinex5-playsinlinet7-video-player-t…...
.Net--CLS,CTS,CLI,BCL,FCL
1.什么是CLS? 所以.NET专门为此参考每种语言(例如C# ,VB,F#)并找出了语言间的共性,然后定义了一组规则,开发者都遵守这个规则来编码,那么代码就能被任意.NET平台支持的语言所通用。 而与其说是规则&#x…...

Stable Diffusion:质量高画风清新细节丰富的二次元大模型二次元插图
今天和大家分享一个基于Pony模型训练的二次元模型:二次元插图。关于该模型有4个不同的分支版本。 1.5版本:loar模型,推荐底模型niji-动漫二次元4.5。 xl版本:SDXL模型版本 mix版本:光影减弱,减少SDXL版本…...
数读MEME之争:以太坊获更高价值共识,抢占热点成Solana流量密码
在当前显著的加密牛市中,以太坊和Solana之间的竞争不仅在币价表现上显而易见,生态发展方面也备受关注。特别是在这轮MEME行情中,双方阵营的MEME代币呈现出不同的特点和趋势。 市场表现对比 以太坊的优势: 市场份额和认可度更高&…...
python的with语句
1.with语句的作用 在 Python 中,with 语句用于创建一个上下文管理器,以更简洁和安全的方式管理资源。 其主要优点是可以确保在代码块执行完毕后,相关资源能够被正确释放或清理,即使在代码块内部发生了异常。 以下是一个使用 with…...

Selenium原理深度解析
在自动化测试领域,Selenium无疑是最受欢迎和广泛使用的工具之一。它支持多种浏览器和操作系统,为开发人员和测试人员提供了强大的自动化测试解决方案。本文将深入探讨Selenium的工作原理,包括其架构、核心组件、执行流程以及它在自动化测试中…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
如何配置一个sql server使得其它用户可以通过excel odbc获取数据
要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据,你需要完成以下配置步骤: ✅ 一、在 SQL Server 端配置(服务器设置) 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到:SQL Server 网络配…...
TCP/IP 网络编程 | 服务端 客户端的封装
设计模式 文章目录 设计模式一、socket.h 接口(interface)二、socket.cpp 实现(implementation)三、server.cpp 使用封装(main 函数)四、client.cpp 使用封装(main 函数)五、退出方法…...

门静脉高压——表现
一、门静脉高压表现 00:01 1. 门静脉构成 00:13 组成结构:由肠系膜上静脉和脾静脉汇合构成,是肝脏血液供应的主要来源。淤血后果:门静脉淤血会同时导致脾静脉和肠系膜上静脉淤血,引发后续系列症状。 2. 脾大和脾功能亢进 00:46 …...

【Java多线程从青铜到王者】单例设计模式(八)
wait和sleep的区别 我们的wait也是提供了一个还有超时时间的版本,sleep也是可以指定时间的,也就是说时间一到就会解除阻塞,继续执行 wait和sleep都能被提前唤醒(虽然时间还没有到也可以提前唤醒),wait能被notify提前唤醒…...
【题解-洛谷】P10480 可达性统计
题目:P10480 可达性统计 题目描述 给定一张 N N N 个点 M M M 条边的有向无环图,分别统计从每个点出发能够到达的点的数量。 输入格式 第一行两个整数 N , M N,M N,M,接下来 M M M 行每行两个整数 x , y x,y x,y,表示从 …...
关于疲劳分析的各种方法
疲劳寿命预测方法很多。按疲劳裂纹形成寿命预测的基本假定和控制参数,可分为名义应力法、局部应力一应变法、能量法、场强法等。 1名义应力法 名义应力法是以结构的名义应力为试验和寿命估算的基础,采用雨流法取出一个个相互独立、互不相关的应力循环&…...