diff --git a/tools/device-check.ps1 b/tools/device-check.ps1 new file mode 100644 index 0000000..dc68991 --- /dev/null +++ b/tools/device-check.ps1 @@ -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. 拖通知栏进度条,确认播放页进度跟着走'