一文了解 Android Auto 车载开发~

作者:牛蛙点点申请出战

背景

我的的产品作为一个海外音乐播放器,在车载场景听歌是一个很普遍的需求。在用户反馈中,也有很多用户提到希望能在车上播放音乐。同时车载音乐也可以作为提升用户消费时长一个抓手。

出海产品,主要服务于海外用户。不同于国内的 Android 车载系统,往往是定制的 ROM,国外的 Android 车载也是 Google 一家独大,主要可以分为 Android Auto、Automotive OS。Android Auto 可以理解为是一套将手机应用「投屏」在车载屏幕上的方案,和 iOS 的 CarPlay 类似。而 Automotive OS 则类似于国内的定制 ROM。如果要支持 Automotive OS,那我们可能要重头开发一个适配大屏的 mini 版 app,那样工作量很大。所以我们第一步先从支持 Android Auto 开始

准备知识

Android Auto 支持多种类型的应用,包括导航、媒体、消息应用等。音乐播放器属于媒体应用,因此下面给出的参考仅限于媒体应用。

Android Auto 的适配需要做提前了解做一些准备知识,具体如下:

  • 官方文档

    • 构建车载媒体应用
      教你明白 Android Auto 开发媒体应用的各种概念以及如何开发。这个网址的中文翻译可读性很差,建议直接看英文。另外,如果要开发的是导航类应用而不是媒体应用,那可以直接使用 androidX 的 Car Library
    • 测试 Android Automotive OS 应用
      教你如何使用车机模拟器(DHU)配合手机上的 Android Auto app 来测试 Auto 应用。但是文档里有个坑点,文档里默认你的手机是 Pxiel 系列,Pxiel 系列是自带 Android Auto app 的,但是国产手机基本都没有这个 app,需要自己去下载然后安装在手机上。可以去 这里下载。另外 Android Auto app 第一次启动的时候会安装好几个谷歌服务,华为手机因为 ban 掉了google,这一步在华为手机上会失败。所以华为手机没法用来测试 Android Auto。推荐尽量使用 Pixel 系列手机来测试,避免测试过程中遇到奇奇怪怪的问题。
  • 车载设备

    • 业务功能的测试,一般使用模拟器 DHU 就可以。但是在应用发布之前,我们肯定要在真实设备上充分测试。如果你有带 Android Auto 的真车的话,直接用真车测试就行。但是国内带 Android Auto 的车比较少,大概率是没有真车可供测试的。可以去闲鱼上单独购买一个车载主机(闲鱼搜索「187b主机」即可)。
    • 还有一个坑点,应用没有经过 google play store 分发的话,在真实车机设备上是不会显示的。这个问题官方文档没有明确提到,导致当时我们 debug 的时候莫名其妙的花了很久的时间。
    • 这里顺带提一下,谷歌分发渠道有四个,internal、closed testing、open testing、production,分别对应内部、内测、公测、产品。当然,我们不可能为了测试直接提 play store 的 production 渠道,用 internal 分发渠道用作真机测试就可以了。closed testing 和 open testing 渠道也可以。
  • 质量规范

    • developer.android.google.cn/docs/qualit…
      因为车载场景事关驾驶员生命安全,所以 google 对 Android Auto 应用审核很严格。所有支持 Android Auto 的应用,必须满足上面链接中的质量规范才可能通过 Play Store 的审核。这个规范要求的点很多,坑也不少,后面还会说到。这里面很重要的一点是必须支持语音搜索歌曲起播,这是审核强卡的一个点。
  • 官方 demo

    • 因为文档讲得很晦涩,照着文档大概率是依然很难上手开发的。可以参考 google 官方的音乐播放器 uamp,虽然这个 demo 很简单,但是它也支持 Android Auto,有不懂的开发细节都可以参考这个 demo。
  • Android Auto app(包名为 com.google.android.projection.gearhead

    • 想要把 android auto 连上车机跑起来,必须要在手机上安装 Android Auto app,在 这里 下载。大部分国内的 rom 是不带这个 app 的,要自己手动安装。pixel 系列手机是自带的,可以在设置中搜索到这个应用。

如何开发

基本概念

首先,Android Auto 是不支持自定义 UI 的,你的应用投屏到车机上,UI 展示已经在车机内部写死了,你能做的只是把数据传到车机上。所以支持 Auto 的应用在车机上看起来都差不多。Auto 只允许你在播控界面的自定义操作里添加自定义图标。

其次,整个车机的播放流程涉及到三个部分,播放器应用、android auto app、车机。

另外还有几个概念我们要了解

  • MediaBrowserService
    • 指的就是音乐播放器的 Service。在 Service 里通过覆写 onGetRootonLoadChildren 等方法对外提供车机所需要展示的数据。相当于「媒体生产者」,该生产者由你的 app 的提供。
  • MediaBrowser
    • 展示、消费你从上面的 MediaBrowserService 拿到的数据。手机上的 Android Auto app 内部包含了这个类。这个类会来主动 binds 上面提到的的 MediaBrowserService。相当于「媒体消费者」,就是 Android Auto app。
  • MediaSession
    • MediaSession 就是 app 和车机之间进行交互的桥梁。实际上 app 和车机不是直接连接进行通信。app 是和 Android Auto app 进行 IPC 通信,Android Auto 再和车机进行通信。MediaSession 本质上就是一个对 IPC 的封装。这个封装不仅能和车机通信,还可以和耳机线控等外部设备进行通信。这些外部设备共用一个 MediaSession。

  • MediaItem、MediaMetadata、MediaDescription
    • MediaItem 代表了车机屏幕上的一个媒体元素,比如某一个页面,一首歌,一张专辑等等,在这个类中对应的属性设置成什么,车机上屏幕上就显示什么。
    • MediaMetadata 代表 MediaItem 中各个属性对应的值,用来构造 MediaDescription。MediaItem 持有 MediaDescription 用以描述该媒体元素该如何在车机上展示。
  • BROWSABLE 和 PLAYABLE
    • 这个一个枚举标记,车机上每一个 MediaItem 要么是 BROWSABLE 的,要么是 PLAYABLE 的。
    • PLAYABLE 意味着该 MediaItem 可播,这样当你在车机屏幕上点击该 MediaItem 时,会直接进行播放。这种 MediaItem 一般是歌曲,可直接起播。
    • BROWSABLE 意味着该 MediaItem 是可浏览的,也就是说点击该 MediaItem 时,会加载一个新的媒体集合用于展示。比如专辑、歌单等,点击的时候会进入一个新的页面展示歌曲列表。

上面就是做 Android Auto 开发需要理解的基本的概念,可以理解为一套车机的框架,把手机和车机交互中的各个角色都定义好了。我们的工作就是在这套框架上开发我们的业务。

车机连接手机

  • 真车上和模拟器上都可以参考官方文档 developer.android.com/training/ca…
  • 或者可以下载这个压缩包,运行 main.py 即可。该脚本把上述文档里的操作步骤都集成进去了。
  • 连上以后,音乐自动会从车机上播放,像耳机连上手机之后音频自动从耳机播放一样。不需要额外在代码中设置。

检测连接

  • 这是 auto 开发中的一个坑点。以往有一个方法可以检测手机是否处在车载模式,但是该方法只对 android 12 以下的系统生效,android 12 上无效:
public static boolean isCarUiMode(Context c) {
UiModeManager uiModeManager = (UiModeManager) c.getSystemService(Context.UI_MODE_SERVICE);if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR) {LogHelper.d(TAG, "Running in Car mode");return true;} else {LogHelper.d(TAG, "Running on a non-Car mode");return false;}
}
  • 官方有一个新的 CarLibrary 「androidx.car.app:app」,这个库中使用 CarConnection 类来检测车机和手机的连接。
CarConnection(carContext).type.observe(this, ::onConnectionStateUpdated)

但是该方法只适合于 android 6-12 的系统,并且我们只想使用连接检测,导入整个库的话未免显得太笨重。

  • 基于上述 CarLibrary 「androidx.car.app:app」的方法,我们把核心代码抽出来即可。具体代码我在 stackoverflow 上贴出来作为一个回答,也被采纳了。对于 android 5 的系统,使用前述的 isCarUiMode 方法即可。

工作原理

如前面 MediaSession 的介绍所述,这个工作过程涉及到车机、android auto app、应用三方。为了叙述方便,后面提到的「车机回调客户端方法」实际上均是中介作用的 Android Auto App 通过 IPC 完成的。

下文中的 PlayerService 指的是应用中用于播放音乐的 Service。

整体的工作流程如下面的时序图所示,后面会逐一解释。

bindService
  • 当我们的车机和手机连接上之后,Android Auto app 就会主动地 bind 我们的 PlayerService。因此 Service 必须声明 android:exported="true" ,这样才可以被外部拉起。同时整个 app 进程也被拉起了。

页面树
  • 整个车机屏幕的 UI 可以看做一个页面树,客户端应用负责把定义各个页面树的节点 ID 和传输节点数据对应的 MediaItem。而车机在拿到这些数据以后就可以渲染屏幕 UI。
  • 车机通过 IPC 回调到客户端代码中的 onGetRoot() 方法和 onLoadChildren() 方法来获取页面 ID 和数据。接下来详细说下这两个方法。
  • 页面树的节点 ID 可以自己定义,只要保证每个节点对应唯一的 ID 即可。
  • 一般来说,叶子节点是 PLAYABLE 类型的 MediaItem,而非叶子节点对应 BROWSABLE 类型的 MediaItem。
onGetRoot()
override fun onGetRoot(clientPackageName: String,clientUid: Int,rootHints: Bundle?
): MediaBrowserServiceCompat.BrowserRoot
  • onGetRoot 是车机和手机交互的入口方法,只要车机和手机连接上了,就会回调这个方法。在这个方法中,需要构造一个 BrowserRoot 返回,作为车机页面树的根节点。整个页面树构造 BrowserRoot 需要传入 rootId。
  • clientPackageName – 是唤起客户端是包名,一般来说是 Android Auto App,包名是 com.google.android.projection.gearhead。但是有时候通过语音搜索播歌唤起客户端,包名就是 com.google.android.googlequicksearchbox,是 google 语音助手。通过这个参数可以过滤你认为有效的包名。
  • 其他两个参数一般用不上。
onLoadChildren()
override fun onLoadChildren(parentMediaId: String,result: Result<List<MediaItem>>
)
  • onLoadChildren 方法一旦被回调,说明在车机上真正开始加载手机客户端对应的车机页面树了。所以这个方法可以认为是车机端的 launch 点,可以用这个方法统计车机 DAU。
  • parentMediaId – 之前我们会在 onGetRoot 中返回了 rootId,接下来车机会回调 onLoadChildren, rootId 就会作为该方法的 parentMediaId 传入,代表在此次 onLoadChildren 方法回调中我们需要加载 parentMediaId 对应的页面树节点对应的 List,也就是页面树中该节点的子节点。
  • 加载好了子节点后,以 List 的形式,通过 result.sendResult(List) 方法将结果返回给车机。

整体的页面树如上所示。当在车机上点击蓝色子节点时,会回调 onLoadChildren 加载下一级子节点。当点击绿色叶子节点时,会回调 onPlayFromMediaId 方法进行起播。下面详细介绍。

MediaSession.Callback
  • MediaSession.Callback 是用户在车机上进行起播、播控以及语音搜索等操作的回调。该抽象类中包含了一系列的回调方法。下面介绍一些重要的回调方法。
  • 通过 mediaSession.setCallback 方法设置回调。
public abstract static class Callback { public boolean onMediaButtonEvent(Intent mediaButtonEvent) {}// 线控耳机回调public void onPlay() {} // 播放public void onPause() {} // 暂停public void onPlayFromMediaId(String mediaId, Bundle extras) {} // 起播public void onPlayFromSearch(String query, Bundle extras) {} // 语音搜索public void onSkipToQueueItem(long id) {} // 切到播放队列中的某一首歌public void onSkipToNext() {} // 切到下一首public void onSkipToPrevious() {} // 切到上一首public void onCustomAction(String action, Bundle extras) {} // 自定义操作....
}
MediaSession.setPlaybackState
  • 当我们执行了上述回调后,如何通知车机进行播放页的 UI 更新呢?这时候就需要调用 MediaSession.setPlaybackState 方法来通知车机更新 playback 的状态和 UI。PlaybackState,顾名思义,就是车机播放状态,所以改变这个状态意味着车机播控页 UI 也会更新。
  • 该方法具体如何使用可以参考 UMAP demo 和官方文档,这里不展开了。
onPlayFromMediaId
  • 上面我们说到,当用户在车机屏幕上点击歌曲起播时,就会回调该方法,并将构建页面树时赋予的 id 作为 mediaId 参数传入。因此,在该方法中我们需要调用客户端的起播歌曲的方法来起播。
onPlay 和 onPause
  • 这两个方法对应车机上的播放和暂停操作。
onSkipToNext 和 onSkipToPrevious
  • 这两个方法对应车机上的切下一首和切上一首操作。
onSkipToQueueItem
  • 在车机上切换到当前队列里的某一首歌,会回调该方法。传入对应的歌曲 id。
  • 在队列切歌之前,需要先给车机构造队列。调用 mediaSessionCompat.setQueue(List) 即可。
onPlayFromSearch(String query, Bundle extras)
  • 当我们在车内使用语音助手起播歌曲时,比如说 「播放周杰伦的夜曲」,会回调该方法。并且将语音内容识别成文字,分词后将 「周杰伦 夜曲」 通过入参 query 传入。这样我们拿到 query 字符串后,调用客户端的搜索服务就可以获得搜索的歌曲结果,并将该结果起播即可。
onCustomAction(String action, Bundle extras)
  • 当你想在车机播控界面添加其他自定义的操作,比如收藏歌曲、单曲循环等,就会用到这个方法。
  • 首先需要在构造 PlaybackState 的时候传入你定义的自定义操作,通过 PlaybackStateBuilder.addCustomAction(CustomAction action) 方法完成。构造 aciton 时需要传入唯一的标识字符串。在刷新了 PlaybackState 之后,在车机的播控界面就会出现自定义的操作按钮。
  • 当在车机屏幕点击了自定义操作按钮后,会回调 onCustomAction 方法,入参 action 就是唯一标识字符串,根据该字符串来区分不同的自定义操作。

开发中的坑

Android Auto 在国内渗透率不高,所以大部分开发者对这个东西很陌生,我也是。并且很多国产 ROM 系统层就不支持 Android Auto。作为国内业界少数 Android Auto 的应用,在开发过程中经历了资料匮乏、机型兼容、审核被拒等很多坑。这里把之前开发踩坑的经历分享出来。

图标缓存

车机界面每个 tab 的 icon 在设置完之后是会有在车机里缓存的。如果修改了 icon 样式,一定要改掉对应的 drawable 的 id,不然车机会从缓存中取图片,icon 修改不生效。

机型不兼容

很多国产的 rom 对 Android Auto 的支持有问题。具体表现有:

  • 无法安装 GMS 导致无法使用 Android Auto App,以华为系手机为代表。
  • 可以安装并且运行 Android Auto App,但是一旦连上 DHU 测试的时候,DHU 就一直黑屏,无法正常运行。亲测小米 11 、vivo S15 机型有该问题。
  • 可以连接 DHU,可以正常运行,但是使用 debug 包在测试的时候,DHU 上不会显示应用。只有使用 GP 商店分发的包才能显示出来。 部分 vivo 手机有这个问题。

所以,最好使用 pixel 这样的原生系统进行测试。

GP 分发

当我们在 DHU 上测完,想使用真实车机进行测试的时候,却发现真车上不显示我们的应用。正如之前所述,在真车上测试,需要经过 GP 分发的包才行,没有经过 GP 分发过的包,即使是 release 包也不行。这个坑当时困扰了我们很久,最后我们也是靠猜测才猜出原因。后来我们和 google 官方进行沟通,也确认了这一点。但是坑爹的是,google 文档里完全没有提及这一点。

语音搜索

语音搜索这个功能,在 DHU 上经常莫名其妙地不好用。具体有

  • 识别不出语音,语音助手回复 「对不起,我没有听懂」
  • 识别成别的应用,比如无论你怎么说 「使用 xxx 播放音乐」,它都回复 「好的,我来让 youtube music 播放音乐」,然后打开 youtube music 播放音乐
  • 语音说完没有任何反应。比如你说 「播放音乐」,在语音助手的对话框消失后就没有任何反应了,也没有回复,也不会打开应用播放。
  • 识别率差。我们的应用名,经常会识别错。但是像 Spotify、微信等应用名,识别率很高。怀疑是 google 语音助手对特定应用名识别做了优化。 上面这些坑是在做语音搜索功能时经常遇到的。QA 在测的过程中也会经常遇到并且反馈 bug 给我。但大部分时候都是语音助手抽风。

如何判断到底是 bug 还是语音助手抽风呢,可以用同样的语音去试下其他应用,比如 spotify 和 YT music。如果也有同样的问题,那么可以认为是语音助手又抽风了。

审核

Google 商店对车载应用的审核标准很高。详见 质量规范,其中对车载应用需要满足什么样的条件做了严格的要求。对于音乐类应用,有几点容易忽视的需要格外关注:

  • 必须支持语音搜索播歌功能。google 认为,用户在开车时不能分散注意力,所以必须提供语音搜索播歌的功能,让用户可以开车的同时按下方向盘上的麦克风按钮,直接语音控制歌曲的播放。如果这个功能没满足,应用不能过审。

  • 歌曲播放时,如果手机上碰到阻塞,比如出需要手动关闭的广告、出弹窗、请求权限,必须让用户转到手机上处理时,这时候必须要在车机屏幕上提示用户。这时可以使用错误提示的 API 来做提示。

  • 添加特定的 Intent Filter
<activity ...><intent-filter><action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" /><category android:name="android.intent.category.DEFAULT" /></intent-filter>
</activity>

一定要在启动 Acitivity 里面添加这个 intent-filter,用来兼容古老版本的 android 手机语音播歌。可以参考

这个点其实在 google 官方文档里有提,但是没有明确说必须要有,只是建议添加。但是如果不加的话,应用审核会被拒绝。所以这里也是个坑点。

总之,成为一名Android车载开发工程师需要深厚的技术功力,同时他们需要不断学习并更新专业知识和技能,以应对未来Android车载应用市场的不断变化,并更好地实现车载应用的创新和发展。

针对车载Android 开发所需要学习的内容,整理了以下的知识路线

路线有了,那自然参考的学习文档笔录也不会少,大家可以参考:https://qr18.cn/F05ZCM

Android 车载学习手册:

  • 第一章——Android Auto概述
  • 第二章——开发汽车媒体应用
  • 第三章——构建 Android Auto即时通信应用
  • 第四章——构建车载导航和地图的注点应用
  • 第五章——构建 Android Automotive OS 视频应用

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

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

相关文章

【单片机】51单片机,晨启科技,板子引脚对应关系

一般引脚: sbit beepP2^4; //将单片机的P2.4端口定义为beep.本口用于屏蔽上电后蜂鸣器响 sbit ledP1^0; //将单片机的P1.0端口定义为led&#xff0c;用于点亮LED-D1 sbit DIG1P0^0; //数码管位选1 sbit DIG2P0^1; //数码管位选2P10xFF;//初始化P1引脚全部置高&a…

Linux中singal信号的作用

void&#xff08;* signal&#xff08;int sig&#xff0c;void&#xff08;* func&#xff09;&#xff08;int&#xff09;&#xff09;&#xff09;&#xff08;int&#xff09;;设置处理信号的功能 头文件为&#xff1a;#include <signal.h> 指定使用sig指定的信号…

时序预测 | MATLAB实现BO-GRU贝叶斯优化门控循环单元时间序列预测

时序预测 | MATLAB实现BO-GRU贝叶斯优化门控循环单元时间序列预测 目录 时序预测 | MATLAB实现BO-GRU贝叶斯优化门控循环单元时间序列预测效果一览基本介绍模型搭建程序设计参考资料 效果一览 基本介绍 MATLAB实现BO-GRU贝叶斯优化门控循环单元时间序列预测。基于贝叶斯(bayes)…

Spring MVC

hi,今天为大家带来Spring MVC相关知识 文章目录 &#x1f33b;1.什么是Spring MVC?&#x1f36c;1.1什么是MVC?&#x1f36c;1.2MVC和Spring MVC的关系 &#x1f33b;2.Spring MVC的意义&#x1f36c;2.1Spring MVC和Spring Boot区别 &#x1f33b;3.Spring MVC的三大要点&a…

用PointNet分类3D点云

在本教程中&#xff0c;我们将学习如何训练PointNet进行分类。 我们将主要关注数据和训练过程&#xff1b; 展示如何从头开始编码 Point Net 的教程位于此处。 本教程的代码位于这个Github库中&#xff0c;我们将使用的笔记本位于这个Github库中。 一些代码的灵感来自于这个Git…

wordpress 打开缓慢处理

gravatar.com 头像网站被墙 追踪发现请求头像时长为21秒 解决方案一 不推荐&#xff0c;容易失效&#xff0c;网址要是要稳定为主&#xff0c;宁愿头像显示异常&#xff0c;也不能网址打不开 网上大部分搜索到的替换的CDN网址都过期了&#xff0c;例如&#xff1a;gravatar.du…

知识付费系统开发:构建高效智能的付费内容平台

随着数字化时代的来临&#xff0c;知识付费正迅速崭露头角&#xff0c;为知识创作者和求知者带来了全新的商机。在这个背景下&#xff0c;开发一款高效智能的知识付费系统成为了一项重要的任务。本文将深入探讨如何基于Python编程语言和相关技术构建一个智能的知识付费内容平台…

备份容灾哪家好怎么样

数字化时代&#xff0c;数据安全是我们不容忽视的问题。云呐容灾备份系统不仅提供了强大的数据保护功能&#xff0c;而且操作简单&#xff0c;使用方便。无论你是企业管理员&#xff0c;还是个人用户&#xff0c;都可以轻松上手。它还提供了丰富的报告和监控功能&#xff0c;让…

Unity 实现字幕打字效果

Text文本打字效果&#xff0c;TextMeshPro可以对应参考&#xff0c;差距不大&#xff0c;改改参数名就能用。改脚本原本被我集成到其他的程序集中&#xff0c;现在已经分离。 效果 实现功能 1.能够设置每行能够容纳的字数和允许的冗余 2.打字效果 3.每行打完上移 4.开头进入&…

springboot(1)

精要&#xff1a; 自动配置&#xff1a;针对很多Spring应用程序常见的应用功能&#xff0c;Spring Boot能自动提供相关配置。 起步依赖&#xff1a;告诉Spring Boot需要什么功能&#xff0c;它就能引入需要的库。 命令行界面&#xff1a;这是Spring Boot的可选特性&#xff0…

嵌入式开发学习(STC51-18-LCD液晶显示)

内容 在LCD1602液晶上显示字符信息&#xff1b; LCD1602介绍 简介 1602液晶也叫1602字符型液晶&#xff0c;它能显示2行字符信息&#xff0c;每行又能显示16个字符&#xff1b; 它是一种专门用来显示字母、数字、符号的点阵型液晶模块&#xff1b; 它是由若干个5x7或者5x…

座舱开发的“道”与“术”

前言&#xff1a; 近年来&#xff0c;随着汽车“新四化”浪潮的兴起&#xff0c;软件定义已成为产业共识&#xff0c;将深度参与到整个汽车的定义、开发验证销售以及服务全过程。一方面确保软件可升级&#xff0c;跨车型、软件甚至跨车企软件重用。另一方面对于硬来讲&#xf…

任务 13、MidJourney种子激发极致创作,绘制震撼连贯画作

13.1 任务概述 通过本次实验任务&#xff0c;学员将深入了解Midjourney种子的概念和重要性&#xff0c;以及种子对生成图像的影响。他们将学会在Midjourney平台中设置种子值并调整其参数&#xff0c;以达到所需的效果。此外&#xff0c;任务还详细介绍了Midjourney V4.0版本中…

UNIX网络编程——UDP协议,CS架构

目录 一.socket创建通信的套接字 二.IPv4地址结构 三.通用地址结构 四. 两种地址结构的使用场合 五.sendto发送数据 六.bind固定地址信息​编辑 七.recvfrom接受UDP的消息​编辑 一.socket创建通信的套接字 二.IPv4地址结构 三.通用地址结构 四. 两种地址结构的使用场合…

【Linux】结合Python 简易实现监控公司网站,邮件发送异常

目录 背景 实现思路 邮件4小时内只会发送一次&#xff0c;如果执行了发送邮件的脚本&#xff0c;就使用sed命令将对应的调用代码置为无效 请求脚本 Python邮件发送脚本 定时任务设置 恢复邮件发送能力脚本 资料获取方法 背景 由于一些原因&#xff0c;博主负责测试的网…

入门Echarts数据可视化:从基础到实践

目录 引言数据可视化的重要性Echarts资源与拓展 Echarts简介及开发准备什么是EchartsEcharts的特点与优势安装Echarts引入Echarts库 第一个图表使用Echarts绘制一个简单的柱状图数据准备与图表配置数据格式要求图表标题与标签设置 实践与性能优化提升图表渲染性能的技巧响应式设…

多态总结

什么是多态&#xff1f; 所谓多态&#xff0c;就是同一个操作&#xff0c;作用在了不同的对象上&#xff0c;就会有不同的解释&#xff0c;进而产生不同的执行结果。使用时&#xff0c;是采用父类指针指向子类对象的方法。其中&#xff0c;重载和重写是常见的实现多态的手段。…

深入理解机器学习与极大似然之间的联系

似然函数&#xff1a;事件A的发生含着有许多其它事件的发生。所以我就把这些其它事件发生的联合概率来作为事件A的概率&#xff0c;也就是似然函数。数据类型的不同&#xff08;离散型和连续性&#xff09;就有不同的似然函数 极大似然极大似然估计方法&#xff08;Maximum Li…

sql server 删除指定字符串

replace方法 update #test set FIVCODEreplace(FIVCODE,440,) WHERE SOURCEFENTRYID140728

无数资深果粉称之为 Mac 装机必备软件的 ——CleanMyMac X

它就是被无数资深果粉称之为 Mac 装机必备软件的 ——CleanMyMac X。或许你没用过它&#xff0c;但是大概率你身边一定有它的资深用户&#xff0c;作为 MacPaw 旗下的老牌清理软件&#xff0c;在全球已经拥有超过 2500 万次的下载量。 它有着五大强悍的功能&#xff0c;可以帮…