chore(tools): 增加真机自检脚本 device-check.ps1

一键完成:安装 debug APK → 冷启动 → 抓 logcat → 校验单实例
(PlaybackService / serviceId / Player / MediaSession 各只允许 1 个),
并检查崩溃与 libmediakitandroidhelper.so 加载失败。
This commit is contained in:
2026-10-22 22:46:51 +08:00
parent 2252b72a14
commit 6f9fe6262d
+124
View File
@@ -0,0 +1,124 @@
#Requires -Version 5.1
<#
.SYNOPSIS
清听真机自检:安装最新 debug APK,冷启动,抓 logcat,校验 libmpv 是否只有单实例。
.DESCRIPTION
判定依据:
- 冷启动后 "PlaybackService 单例创建" 只出现一次;
- 日志里出现过的 serviceId 只有 1 个;
- media_kit Player 的 hashCode 只有 1 个。
.EXAMPLE
powershell -ExecutionPolicy Bypass -File tools\device-check.ps1
powershell -ExecutionPolicy Bypass -File tools\device-check.ps1 -DeviceId 192.168.3.2:36569
#>
[CmdletBinding()]
param(
[string]$DeviceId = '',
[string]$Apk = '',
[int]$WaitSeconds = 12
)
$ErrorActionPreference = 'Continue' # adb 会往 stderr 写正常提示,用 Stop 会误判
$root = Split-Path -Parent $PSScriptRoot
if (-not $Apk) { $Apk = Join-Path $root 'build\app\outputs\flutter-apk\app-debug.apk' }
$pkg = 'com.lxh.qingting_player'
function Write-Step($msg) { Write-Host "==> $msg" -ForegroundColor Cyan }
function Write-Ok($msg) { Write-Host " OK $msg" -ForegroundColor Green }
function Write-Bad($msg) { Write-Host " !! $msg" -ForegroundColor Red }
if (-not (Get-Command adb -ErrorAction SilentlyContinue)) {
throw '未找到 adb,请把 platform-tools 加入 PATH'
}
if (-not (Test-Path $Apk)) {
throw "APK 不存在:$Apk(先执行 gradlew assembleDebug)"
}
# ---------- 选择设备 ----------
# 同一台手机可能同时存在多条 adb 通道(如无线调试 + mDNS),按 serialno 去重
$devices = @(adb devices | Select-Object -Skip 1 | Where-Object { $_ -match '\sdevice$' } | ForEach-Object { ($_ -split '\s+')[0] })
if ($DeviceId) {
if ($devices -notcontains $DeviceId) { throw "设备 $DeviceId 不在线,当前:$($devices -join ', ')" }
} elseif ($devices.Count -eq 0) {
throw '没有在线设备,请先连接手机并授权 USB/无线调试'
} elseif ($devices.Count -eq 1) {
$DeviceId = $devices[0]
} else {
$bySerial = @{}
foreach ($entry in $devices) {
$serial = (& adb -s $entry shell getprop ro.serialno 2>$null | Out-String).Trim()
if ($serial) { $bySerial[$serial] = $entry }
}
if ($bySerial.Count -eq 1) {
$DeviceId = @($bySerial.Values)[0]
Write-Host " (同一设备的多条 adb 通道,使用 $DeviceId)" -ForegroundColor DarkGray
} else {
throw "检测到多台设备,请用 -DeviceId 指定:$($devices -join ', ')"
}
}
$adb = { param($args) & adb -s $DeviceId @args }
$model = (& adb -s $DeviceId shell getprop ro.product.model).Trim()
$android = (& adb -s $DeviceId shell getprop ro.build.version.release).Trim()
Write-Step "设备 $model (Android $android) [$DeviceId]"
# ---------- 安装 ----------
Write-Step "安装 $([IO.Path]::GetFileName($Apk))"
$install = & adb -s $DeviceId install -r $Apk 2>&1
if (($install -join "`n") -notmatch 'Success') { Write-Bad ($install -join "`n"); exit 1 }
Write-Ok '安装成功'
# ---------- 冷启动 ----------
Write-Step '冷启动并抓取日志'
& adb -s $DeviceId shell am force-stop $pkg | Out-Null
& adb -s $DeviceId logcat -c
& adb -s $DeviceId shell monkey -p $pkg -c android.intent.category.LAUNCHER 1 2>&1 | Out-Null
Start-Sleep -Seconds $WaitSeconds
& adb -s $DeviceId logcat -d -v time > (Join-Path $root 'build\device-check-last.log')
$logPath = Join-Path $root 'build\device-check-last.log'
# 注意:PS 5.1 的 > 重定向写出的是 UTF-16LE(带 BOM),这里不要强制 utf8
$log = Get-Content $logPath
$text = $log -join "`n"
# ---------- 判定 ----------
Write-Step '实例检查'
$serviceIds = @([regex]::Matches($text, 'serviceId=(\d+)') | ForEach-Object { $_.Groups[1].Value } | Sort-Object -Unique)
$players = @([regex]::Matches($text, 'Player 已创建.*?player=(\d+)') | ForEach-Object { $_.Groups[1].Value } | Sort-Object -Unique)
$creates = ([regex]::Matches($text, '\[PlaybackService\] 单例创建')).Count
$failed = $false
if ($creates -eq 1) { Write-Ok "PlaybackService 单例创建 x$creates" } else { Write-Bad "PlaybackService 单例创建 x$creates(应为 1)"; $failed = $true }
if ($serviceIds.Count -eq 1) { Write-Ok "serviceId 唯一:$($serviceIds[0])" } else { Write-Bad "出现 $($serviceIds.Count) 个 serviceId:$($serviceIds -join ', ')"; $failed = $true }
if ($players.Count -eq 1) { Write-Ok "Player 唯一:$($players[0])" } else { Write-Bad "出现 $($players.Count) 个 Player:$($players -join ', ')"; $failed = $true }
# ---------- 系统侧 ----------
Write-Step '系统 MediaSession'
$sessionIds = @(& adb -s $DeviceId shell dumpsys media_session 2>&1 |
Select-String 'media-session com\.lxh\.qingting_player/media-session/(\d+)' |
ForEach-Object { $_.Matches[0].Groups[1].Value } | Sort-Object -Unique)
if ($sessionIds.Count -eq 1) { Write-Ok "MediaSession 唯一:$($sessionIds[0])" }
elseif ($sessionIds.Count -eq 0) { Write-Host ' -- 还没有 MediaSession(未开始播放,属正常)' -ForegroundColor DarkGray }
else { Write-Bad "出现 $($sessionIds.Count) 个 MediaSession:$($sessionIds -join ', ')"; $failed = $true }
# ---------- 崩溃 / 加载失败 ----------
Write-Step '崩溃与原生库'
$bad = $log | Select-String 'FATAL EXCEPTION|UnsatisfiedLinkError|Cannot load MediaKitAndroidHelper|Could not load'
if ($bad) { $bad | Select-Object -First 5 | ForEach-Object { Write-Bad $_.Line.Trim() }; $failed = $true } else { Write-Ok '无崩溃、无 so 加载失败' }
# ---------- 结果 ----------
Write-Host ''
if ($failed) {
Write-Host 'RESULT: FAIL —— 仍存在多实例或异常,完整日志:' -ForegroundColor Red
Write-Host " $logPath"
exit 1
}
Write-Host 'RESULT: PASS —— 单引擎单实例' -ForegroundColor Green
Write-Host "日志:$logPath"
Write-Host ''
Write-Host '还需要你手动确认(脚本无法代替耳朵和眼睛):' -ForegroundColor Yellow
Write-Host ' 1. 放一首歌,确认只有一路声音(没有重影/叠音)'
Write-Host ' 2. 按通知栏暂停/播放,确认播放页状态同步变化'
Write-Host ' 3. 拖通知栏进度条,确认播放页进度跟着走'