『与善仁』Appium基础 — 13、补充:Desired Capabilities参数介绍
作者:互联网
文章目录
下面介绍一下Desired capabilities的参数:
提示:
- Desired capabilities的参数非常的多,但是在我们实际的工作用,常用的也就是其中的几个,我们没有必要全部都记下来,以后我们用到哪些参数,就掌握哪些参数就可以了。
- Desired capabilities参数官方查看地址:http://appium.io/docs/en/writing-running-appium/caps/
(1)通用参数
这些功能跨越多个驱动程序。
关键字 | 描述 | 实例 |
---|---|---|
automationName | 使用哪个自动化引擎 | Appium (缺省),或UiAutomator2 , Espresso ,或UiAutomator1 对于Android,或者XCUITest 或Instruments 适用于IOS,或YouiEngine 与你一起建造的应用程序.引擎 |
platformName | 使用哪个移动操作系统平台 | iOS , Android ,或FirefoxOS |
platformVersion | 移动操作系统版本 | 例如,7.1 , 4.4 |
deviceName | 要使用的移动设备或模拟器的类型 | iPhone Simulator , iPad Simulator , iPhone Retina 4-inch , Android Emulator , Galaxy S4 等等…。在IOS上,这应该是由以下工具返回的有效设备之一instruments -s devices 。在Android上,这一功能目前被忽略了,尽管它仍然是必需的。 |
app | 绝对局部路径或远程http URL到.ipa 档案(IOS),.app 文件夹(IOS模拟器),.apk 文件(Android)或.apks 文件(AndroidAppBundle),或.zip 包含其中之一的文件。Appium将尝试首先在适当的设备上安装此应用程序二进制文件。请注意,如果您指定appPackage 和appActivity 功能(见下文)。UiAutomator2 和XCUITest 允许在没有app 或appPackage 。不相容的browserName 。看见这里关于.apks 档案。 | /abs/path/to/my.apk 或http://myapp.com/app.ipa |
otherApps | 在运行测试之前安装的应用程序或应用程序列表(作为JSON数组)。请注意,它不适用于automationName 的Espresso 和IOS实设备 | 例如,"/path/to/app.apk" , https://www.example.com/url/to/app.apk , ["http://appium.github.io/appium/assets/TestApp9.4.app.zip", "/path/to/app-b.app"] |
browserName | 移动浏览器名称自动。如果将应用程序自动化,则应该是空字符串。 | “Safari”用于iOS,“Chrome”、“Chromium”或“Browser”用于Android |
newCommandTimeout | 在假定客户端退出并结束会话之前,Appium将等待客户端的新命令多长时间(以秒为单位) | G.60 |
language | 将语言设置为IOS(仅限于XCUITest驱动程序)和Android。 | G.fr |
locale | 区域设置为IOS(仅限于XCUITest驱动程序)和Android。fr_CA IOS格式。CA Android格式(国家名称缩写) | G.fr_CA , CA |
udid | 连接物理设备的唯一设备标识符 | G.1ae203187fc012g |
orientation | (仅限SIM/Emu-)从某一方向开始 | LANDSCAPE 或PORTRAIT |
autoWebview | 直接进入WebView上下文。违约false | true , false |
noReset | 不要在此会话之前重置应用程序状态。看见这里想了解更多细节 | true , false |
fullReset | 执行完全重置。看见这里想了解更多细节 | true , false |
eventTimings | 启用或禁用各种Appium内部事件的时间报告(例如,每个命令的开始和结束等)。默认为false 。若要启用,请使用true 。然后将时间报告为events 属性在响应查询当前会话时设置。见事件定时文档对于这个反应的结构。 | 例如,true |
enablePerformanceLogging | (仅支持Web和webview)启用Chromeriver(Android)或Safari(在IOS上)性能日志(默认)false ) | true , false |
printPageSourceOnFindFailure | 当查找操作失败时,打印当前页源。默认为false . | 例如,true |
clearSystemFiles | 删除会话结束时生成的任何文件。默认为false . | true , false |
关键字 | 描述 | 实例 |
---|---|---|
settings[settingsKey] | 更新Appium设置关于会话创建。 | 例如,'settings[mjpegScalingFactor]': 10 , 'settings[shouldUseCompactResponses]': true |
(2)仅限Android
这些功能仅适用于基于android的驱动程序(如UiAutomator 2例如)。
能力 | 描述 | 价值 |
---|---|---|
appActivity | 要从包中启动的Android活动的活动名称。这通常需要在此之前加上一个. (例如,.MainActivity 而不是MainActivity )。默认情况下,此功能来自包清单(action:android.intent.action.MAIN,类别:android.intent.taxy.LAUNHER) | MainActivity , .Settings |
appPackage | 你想运行的Android应用程序的Java包。默认情况下,此功能是从包清单(@Package属性值)接收的。 | com.example.android.myApp , com.android.settings |
appWaitActivity | 活动名称/名称,逗号分隔,用于要等待的Android活动。默认情况下,此功能的值与appActivity …您必须将其设置为第一个焦点突出的应用程序活动名称,以防它与设置为appActivity 如果你的能力appActivity 和appPackage 。您也可以使用通配符(* ). | SplashActivity , SplashActivity,OtherActivity , * , *.SplashActivity |
appWaitPackage | 你想要等待的Android应用程序的Java包。默认情况下,此功能的值与appActivity | com.example.android.myApp , com.android.settings |
appWaitDuration | 用于等待appWaitActivity启动的以毫秒为单位的超时(默认)20000 ) | 30000 |
deviceReadyTimeout | 等待设备就绪时的超时时间(秒) | 5 |
allowTestPackages | 允许安装具有android:testOnly="true" 在舱单上。false 默认情况下 | true 或false |
androidCoverage | 完全合格的仪表类。传递到-w在亚行外壳是仪器-e覆盖真-w | com.my.Pkg/com.my.Pkg.instrumentation.MyInstrumentation |
androidCoverageEndIntent | 由您自己实现的一种广播操作,用于将覆盖率转储到文件系统中。转到-a在亚洲开发银行的空壳广播-a | com.example.pkg.END_EMMA |
androidDeviceReadyTimeout | 超时(以秒为单位),用于等待设备在启动后准备就绪。 | 例如,30 |
androidInstallTimeout | 超时(以毫秒为单位),用于等待apk安装到设备上。默认为90000 | 例如,90000 |
androidInstallPath | 安装前要推送apk的设备上目录的名称。默认为/data/local/tmp | G./sdcard/Downloads/ |
adbPort | 用于连接到adb服务器的端口(默认值)5037 ) | 5037 |
systemPort | systemPort 用于连接到Appium-uiAutomator2-服务器或浓缩咖啡驱动器。默认情况是8200 一般情况下,并从8200 到8299 为Appium-uiAutomator2-服务器,是的8300 从…8300 到8399 为浓缩咖啡驱动器。并行运行测试时,必须调整端口以避免冲突。朗读并行测试设置指南更多细节。 | 例如,8201 |
remoteAdbHost | 可选远程adb服务器主机 | 例如:192.168.0.101 |
androidDeviceSocket | DevTools套接字名。只有在经过测试的应用程序是ChromiumEmbedded浏览器时才需要。该套接字由浏览器打开,Chromeriver作为DevTools客户端连接到它。 | 例如,chrome_devtools_remote |
avd | AVD号即将发射 | 例如,api19 |
avdLaunchTimeout | 以毫秒为单位等待AVD启动和连接到亚行的时间(默认值)60000 ) | 300000 |
avdReadyTimeout | 以毫秒为单位等待AVD完成其引导动画的时间(默认)120000 ) | 300000 |
avdArgs | 启动AVD时使用的附加模拟器参数 | 例如,-netfast |
useKeystore | 使用自定义密钥存储库对APK进行签名,默认情况下false | true 或false |
keystorePath | 自定义密钥存储库的路径,默认为~/.android/调试器 | 例如,/path/to.keystore |
keystorePassword | 自定义密钥存储的密码 | 例如,foo |
keyAlias | 密钥别名 | 例如,androiddebugkey |
keyPassword | 密钥密码 | 例如,foo |
chromedriverExecutable | Web驱动程序可执行文件的绝对本地路径(如果ChromiumEmbedder提供了自己的Web驱动程序,则应该使用它来代替与Appium捆绑的原始色驱动程序) | /abs/path/to/webdriver |
chromedriverArgs | 在Appium运行时传递给chromeriver二进制文件的参数数组。默认情况下,除了Appium内部使用的之外,没有添加任何CLI args(例如--url-base , --port , --adb-port ,和--log-path . | 例如,["--disable-gpu", "--disable-web-security"] |
chromedriverExecutableDir | 查找Chromeriver可执行文件的目录的绝对路径,用于自动发现兼容的Chromeriver。忽略如果chromedriverUseSystemExecutable 是true | /abs/path/to/chromedriver/directory |
chromedriverChromeMappingFile | 文件的绝对路径,该文件将Chromeriver版本映射到它支持的最小Chrome。忽略如果chromedriverUseSystemExecutable 是true | /abs/path/to/mapping.json |
chromedriverUseSystemExecutable | 如果true ,绕过自动Chromeriver配置,并使用Appium下载的版本。忽略如果chromedriverExecutable 已经设定好了。默认为false | 例如,true |
autoWebviewTimeout | 等待WebView上下文活动的时间,以ms为单位。默认为2000 | G.4 |
chromedriverPort | 启动Chromeriver的数字端口。请注意,不鼓励使用此功能,因为如果存在多个Web视图,则会导致未定义的行为。默认情况下,Appium将找到一个自由端口。 | G.8000 |
chromedriverPorts | Appium用于与Chromeriver通信的有效端口列表。此功能支持多个webview场景。这种功能的形式是一个数字端口数组,其中数组项本身可以是长度为2的数组,其中第一个元素是包含范围的开始,第二个元素是结束。默认情况下,Appium将使用任何自由端口。 | G.[8000, [9000, 9005]] |
ensureWebviewsHavePages | 无论Appium是否应该用页面检测来增强其webview检测,以确保显示在上下文列表中的任何webview上下文都有活动页面。如果选择了Chromeriver找不到任何页面的上下文,这可以防止错误。默认为false | G.true |
webviewDevtoolsPort | 支持ensureWebviewsHavePages 特性,需要打开TCP端口,以便与正在测试的设备上的webview进行通信。此功能允许重写9222 ,如果多个会话同时运行(以避免端口冲突),或在默认端口不适合您的系统时。 | G.9543 |
enableWebviewDetailsCollection | 通过/json/version CDP(Chrome Developer Protocol)端点自Appium 1.18.0+。这有助于正确匹配支持给定WebView的Chromeriver版本。如果没有启用此标志,Appium将尝试根据相应的已安装包的版本(通常是该版本)猜测WebView的版本。失败(用于自定义web视图)。默认为false | true 或false |
dontStopAppOnReset | 在使用亚行启动应用程序之前,不会停止正在测试的应用程序的进程。如果被测试的应用程序是由另一个锚应用程序创建的,设置此错误,则允许锚应用程序的进程在使用亚行的测试应用程序启动时仍然存在。换句话说,与dontStopAppOnReset 设为true ,我们将不包括-S 标志在adb shell am start 打电话。将此功能省略或设置为false ,我们包括-S 旗子。违约false | true 或false |
unicodeKeyboard | 启用Unicode输入,默认值false | true 或false |
resetKeyboard | 在运行unicode测试之后,将键盘重置为原始状态。unicodeKeyboard 能力。如果单独使用会被忽略。违约false | true 或false |
noSign | 带调试键的应用程序的跳过检查和签名,只适用于UiAutomator,默认情况下false | true 或false |
ignoreUnimportantViews | 调用setCompressedLayoutHierarchy() 自动机函数此功能可以加快测试执行速度,因为可访问性命令在忽略某些元素时运行得更快。忽略的元素是不可找到的,这就是为什么此功能也被实现为可切换的原因。设置同时也是一种能力。默认为false | true 或false |
disableAndroidWatchers | 禁用用于监视应用程序没有响应和应用程序崩溃的Android观察者,这将减少Android设备/模拟器上的CPU使用。默认情况下,此功能仅适用于UiAutomator。false | true 或false |
chromeOptions | 允许ChromeDriver通过色度选项功能。有关更多信息,请参见色度选择 | chromeOptions: {args: ['--disable-popup-blocking']} |
recreateChromeDriverSessions | 当移动到非ChromeDriver Web视图时,关闭ChromeDriver会话。默认为false | true 或false |
nativeWebScreenshot | 在Web环境中,使用本地(亚行)方法来截图,而不是代理ChromeDriver。默认为false | true 或false |
androidScreenshotPath | 将屏幕截图放在设备上的目录的名称。默认为/data/local/tmp | G./sdcard/screenshots/ |
autoGrantPermissions | 让Appium自动确定您的应用程序需要哪些权限,并在安装时将它们授予应用程序。默认为false 。如果noReset 是true ,此功能不起作用。 | true 或false |
networkSpeed | 设置网络速度仿真。指定网络上传和下载的最大速度。默认为full | ['full','gsm', 'edge', 'hscsd', 'gprs', 'umts', 'hsdpa', 'lte', 'evdo'] 查帐-网速选项有关AVDS速度仿真的更多信息 |
gpsEnabled | 在开始会话之前,切换模拟器的GPS位置提供程序。默认情况下,根据设置的方式,模拟器将启用或不启用此选项。 | true 或false |
isHeadless | 将此功能设置为true 若要在不需要显示设备时运行无头仿真程序,请执行以下操作。false 默认值。无头也是对IOS的支持,请检查XCUITest的特定功能。 | 例如,true |
adbExecTimeout | 用于等待adb命令执行的超时(毫秒)。默认为20000 | 例如,50000 |
localeScript | 设置区域设置剧本 | 例如,"Cyrl" (西里尔) |
skipDeviceInitialization | 跳过设备初始化,包括I.a.:安装和运行Settings应用程序或设置权限。当设备已经用于自动化并为下一次自动化做好准备时,可以使用它来提高启动性能。默认为false | true 或false |
chromedriverDisableBuildCheck | 设置色驱动器标志--disable-build-check 用于Chrome webview测试 | true 或false |
skipUnlock | 在会话创建期间跳过解锁。默认为false | true 或false |
unlockType | 用特殊的锁定模式解锁目标设备,而不是仅仅用帮助程序唤醒设备。它适用于unlockKey 能力。默认为未定义。fingerprint 只适用于Android6.0+和模拟器。朗读解锁医生在Android驱动程序中。 | ['pin', 'password', 'pattern', 'fingerprint'] |
unlockKey | 用于解锁的关键模式unlockType . | 例如,‘1111’ |
autoLaunch | 自动初始化测试中的应用程序。如果这是false 。默认为true | true 或false |
skipLogcatCapture | 跳过以开始捕获logcat。它可以提高网络等性能。与日志相关的命令将无法工作。默认为false . | true 或false |
uninstallOtherPackages | 包裹、包裹清单或* 在安装APK进行测试之前卸载软件包/s。'*' 卸载除Appium测试所必需的软件包(如io.appium.settings 或io.appium.uiautomator2.server 因为Appium已经包含了管理它们的逻辑。 | G."io.appium.example" , ["io.appium.example1", "io.appium.example2"] , '*' |
disableWindowAnimation | 如果该值为true 。会话完成后,Appium将动画缩放恢复到它的原始值。默认为false | true , false |
remoteAppsCacheLimit | 设置被推送到测试中设备的本地存储的远程缓存APK的最大数量(默认为10)。当使用同一组APK时,远程缓存APK将加速顺序测试用例的执行,避免每次需要重新安装时将apk推送到远程文件系统。将此功能设置为0 若要禁用缓存,请执行以下操作。 | G.0 , 5 , 20 |
buildToolsVersion | 指定Androidbuild-tools 版本与默认版本不同,即使用最新版本。如果您的环境使用alpha/beta构建工具,那么使用非默认版本是有帮助的。 | G.'28.0.3' |
androidNaturalOrientation | 允许正确地处理面向景观的设备上的定位.设为true 基本上改变了…的意思PORTRAIT 和LANDSCAPE 。默认为false | true , false |
enforceAppInstall | 默认情况下,如果该应用程序的更新版本或相同版本已经出现在正在测试的设备上,则跳过应用程序安装。将此选项设置为true 将强制Appium始终独立于当前安装的版本安装当前应用程序构建。默认为false . | true , false |
ignoreHiddenApiPolicyError | 视而不见Security exception: Permission denial 警告并允许从Appium 1.18.0+开始继续会话创建过程。当Appium试图放松时,会发生错误。隐藏API策略,尽管一些具有自定义固件的设备拒绝此类请求。默认为false . | true , false |
mockLocationApp | 设置应用程序的包标识符,从Appium 1.18.0+开始用作系统模拟位置提供程序。此功能对模拟器没有影响。如果值设置为null 或空字符串,则Appium将跳过模拟的位置提供程序安装过程。默认为Appium设置包标识符(io.appium.settings ). | 例如,null , io.appium.settings , example.your.app |
LogcatFormat | 从Appium 1.18.0开始设置logcat消息的输出格式。支持的格式列在这里。请阅读Logcat#outputFormat有关每种格式的详细信息。默认为threadtime . | 例如,process |
LogcatFilterSPECS | 为Appium 1.18.0以来的logcat消息设置输出筛选规则。请阅读Logcat#过滤器输出有关该规则的更多详细信息。用logcat编写和查看日志也很有帮助。 | 例如,['*:W', 'MyActivity:D'] (MyActivity 是标签) |
UIAutomator(1和2)
这些功能可在UIA 1和2上使用。
能力 | 描述 | 价值 |
---|---|---|
intentAction | 将用于启动活动的意图操作(默认)android.intent.action.MAIN ) | G.android.intent.action.MAIN , android.intent.action.VIEW |
intentCategory | 将用于启动活动的意图类别(默认)android.intent.category.LAUNCHER ) | G.android.intent.category.LAUNCHER , android.intent.category.APP_CONTACTS |
intentFlags | 将用于启动活动的标志(默认值)0x10200000 ) | G.0x10200000 |
optionalIntentArguments | 用于启动活动的附加意图参数。看见意图论点 | G.--esn <EXTRA_KEY> , --ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> 等 |
只有UIAutomator 2
这些功能仅在UiAutomator2驱动程序
能力 | 描述 | 价值 |
---|---|---|
appWaitForLaunch | 尝试在未进行测试的情况下启动该应用程序-W选项在会话创建中。如果会话创建没有继续,则可能会有所帮助。shell am start 没有回应。默认为true . | false 或true |
disableSuppressAccessibilityService | 集标志-不抑制-可访问性服务为了允许现有的可访问性服务继续运行,可能会为Appium启动一个新的服务。它有助于测试被测试的应用程序,它具有诸如对讲机之类的可访问性功能。如果没有提供任何信息,Appium将不会指定标志。该标志需要AndroidAPI级别24+。 | false 或true |
mjpegServerPort | 如果指定,这是将绑定到appium-uiautomator2-server 的MJPEG屏幕截图流。这可以与mjpegScreenshotUrl 。它应该是范围内的有效整数。1025…65535。默认为null 。G.mjpegScreenshotUrl = 'http://localhost:9200', mjpegServerPort = 9200 | 任何Integer ,建议:9200..9299 为保持一致性w/serverPort 范围 |
skipServerInstallation | 跳过uiAutomator 2服务器安装,并从设备中使用uiAutomator 2服务器。当设备上已经安装了适当版本的uiAutomator 2服务器时,可以使用它来提高启动性能。默认为false . | false 或true |
uiautomator2ServerInstallTimeout | 用于等待安装uiAutomator 2服务器的超时(毫秒)。默认为20000 | 例如,20000 |
uiautomator2ServerLaunchTimeout | 用于等待uiAutomator 2服务器启动的超时(毫秒)。默认为20000 | 例如,20000 |
userProfile | 如果提供了该值,则强制用户配置文件作为给定参数。应该是整数。 | 例如,11 |
(3)仅限IOS
这些功能仅使用在UIAutomation驱动程序.
能力 | 描述 | 价值 |
---|---|---|
calendarFormat | (仅限SIM)为IOS模拟器设置日历格式 | G.gregorian |
bundleId | 正在测试的应用程序的包ID。对于在真正的设备上启动应用程序或使用测试启动时需要绑定ID的其他上限,都很有用。要在使用包ID的实际设备上运行测试,可以省略“app”功能,但必须提供“UDID”。 | G.io.appium.TestApp |
udid | 连接物理设备的唯一设备标识符 | G.1ae203187fc012g |
launchTimeout | 在ms中等待工具的时间,然后假设它挂起,并在会议上失败。 | G.20000 |
locationServicesEnabled | (仅限SIM)强制定位服务打开或关闭。默认值是保持当前sim设置。 | true 或false |
locationServicesAuthorized | (仅使用SIM)通过plist将位置服务设置为已授权或未授权的应用程序,这样位置服务警报就不会弹出。默认值是保持当前sim设置。注意,如果使用此设置,还必须使用bundleId 能够发送您的应用程序的捆绑ID。 | true 或false |
autoAcceptAlerts | 如果所有IOS警报弹出,则自动接受它们。这包括隐私访问权限警报(例如,位置、联系人、照片)。默认是假的。 | true 或false |
autoDismissAlerts | 如果所有IOS警报弹出,则自动关闭它们。这包括隐私访问权限警报(例如,位置、联系人、照片)。默认是假的。 | true 或false |
nativeInstrumentsLib | 使用本机工具库(即立即禁用仪器). | true 或false |
nativeWebTap | 在Safari中启用“真正”的、非基于javascript的web点击。违约:false 。警告:取决于视口大小/比率;这可能无法准确地点击元素。 | true 或false |
safariInitialUrl | 初始Safari url,默认值是本地欢迎页面。 | G.https://www.github.com |
safariAllowPopups | (仅限SIM)允许javascript在Safari中打开新窗口。默认保持当前sim设置 | true 或false |
safariIgnoreFraudWarning | (仅限SIM)防止Safari显示欺诈性网站警告。默认值保持当前sim设置。 | true 或false |
safariOpenLinksInBackground | (仅限SIM)Safari是否应该允许在新窗口中打开链接。默认值保持当前sim设置。 | true 或false |
keepKeyChains | (仅限SIM)在Appium会话启动/完成时是否保留密钥链(库/密钥链) | true 或false |
localizableStringsDir | 查找可本地化字符串的位置。违约en.lproj | en.lproj |
processArguments | 使用仪器传递给AUT的参数 | 例如,-myflag |
interKeyDelay | 输入时发送给元素的击键之间的延迟,以ms为单位。 | 例如,100 |
showIOSLog | 是否显示从Appium日志中的设备中捕获的任何日志。违约false | true 或false |
sendKeyStrategy | 用于在测试字段中键入测试的策略。模拟器默认设置:oneByOne 。真正的设备默认:grouped | oneByOne , grouped 或setValue |
screenshotWaitTimeout | 以秒为单位的最大超时时间,以等待屏幕截图的生成。违约:10 | 例如,5 |
waitForAppScript | 用于确定应用程序是否已经启动的IOS自动化脚本,默认情况下,系统会等待页面源不为空。结果必须是布尔值。 | G.true; , target.elements().length > 0; , $.delay(5000); true; |
webviewConnectRetries | 将连接消息发送到远程调试器、获取webview的次数。违约:8 | 例如,12 |
appName | 正在测试的应用程序的显示名称。用于在iOS 9+中自动回退应用程序。 | 例如,UICatalog |
customSSLCert | (仅限SIM)向IOS模拟器添加SSL证书。 | G. -----BEGIN CERTIFICATE-----MIIFWjCCBEKg... -----END CERTIFICATE----- |
webkitResponseTimeout | (仅限于真正的设备)在Safari会话中设置等待WebKit响应的时间(Ms)。默认为5000 | 例如,10000 |
remoteDebugProxy | (仅限SIM,<=11.2)如果设置,Appium将通过本地端口(仅限SIM,<=11.2)上的代理或此Unix套接字上的代理(SIM仅>=11.3)发送和接收远程调试消息,而不是直接与IOS远程调试器通信。 | G.12000 或"/tmp/my.proxy.socket" |
enableAsyncExecuteFromHttps | 允许模拟器使用HTTPS在页面上执行异步JavaScript的功能。默认为false | true 或false |
skipLogCapture | 跳过以开始捕获日志,如崩溃、系统、Safari控制台和Safari网络。它可以提高网络等性能。与日志相关的命令将无法工作。默认为false . | true 或false |
webkitDebugProxyPort | (仅限于真正的设备)端口ios-webkit-debug-proxy 在真正的设备测试中连接。默认值是27753 . | 12021 |
fullContextList | 对象的上下文的详细信息。获取可用上下文命令。如果启用了此功能,则返回上下文列表中的每个项将另外包括WebView标题、完整URL和包标识符。默认为false . | true 或false |
标签:例如,Appium,false,Capabilities,13,应用程序,默认,true 来源: https://blog.csdn.net/Liuyuelinjiayou/article/details/110498992