144 lines
7.3 KiB
PowerShell
144 lines
7.3 KiB
PowerShell
#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,
|
||
# 默认校验 debug 包(_dev 后缀),与 release 包并存安装
|
||
[string]$Package = 'com.lxh.qingting_player.dev'
|
||
)
|
||
|
||
$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 = $Package
|
||
|
||
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 '冷启动并抓取日志'
|
||
$logPath = Join-Path $root 'build\device-check-last.log'
|
||
$creates = 0
|
||
# Flyme 有时不会立刻杀掉带前台服务的进程,导致抓不到启动日志:确认进程消失 + 最多重试两次
|
||
for ($attempt = 1; $attempt -le 2; $attempt++) {
|
||
& adb -s $DeviceId shell am force-stop $pkg | Out-Null
|
||
$deadline = (Get-Date).AddSeconds(10)
|
||
while ((& adb -s $DeviceId shell pidof $pkg) -and (Get-Date) -lt $deadline) {
|
||
Start-Sleep -Milliseconds 300
|
||
}
|
||
# 进程还在说明被系统冻结/保活,直接等到超时也无意义,先给它一点时间解冻
|
||
Start-Sleep -Seconds 1
|
||
& adb -s $DeviceId logcat -c
|
||
# 解析真正的启动组件再启动:applicationId 带 .dev 后缀时,
|
||
# "$pkg/.MainActivity" 会把 .MainActivity 拼到 .dev 上导致找不到类;
|
||
# 而 "am start -p $pkg" 在部分 ROM 上又解析不到,所以直接问 PackageManager。
|
||
$component = (& adb -s $DeviceId shell cmd package resolve-activity --brief $pkg 2>&1 | Select-Object -Last 1).Trim()
|
||
& adb -s $DeviceId shell am start -W -n $component 2>&1 | Out-Null
|
||
# 第一次按参数等待;第二次重试时多等一会儿(Flyme 解冻可能很慢)
|
||
$waitThisTime = if ($attempt -eq 1) { $WaitSeconds } else { [Math]::Max($WaitSeconds, 20) }
|
||
Start-Sleep -Seconds $waitThisTime
|
||
& adb -s $DeviceId logcat -d -v time > $logPath
|
||
# 注意:PS 5.1 的 > 重定向写出的是 UTF-16LE(带 BOM),读取时不要强制 utf8
|
||
$log = Get-Content $logPath
|
||
$text = $log -join "`n"
|
||
$creates = ([regex]::Matches($text, '\[PlaybackService\] 单例创建')).Count
|
||
if ($creates -ge 1) { break }
|
||
Write-Host " (第 $attempt 次没抓到冷启动日志,进程可能没退干净,重试)" -ForegroundColor DarkYellow
|
||
}
|
||
|
||
# ---------- 判定 ----------
|
||
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)
|
||
|
||
$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 " + [regex]::Escape($pkg) + "/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. 拖通知栏进度条,确认播放页进度跟着走'
|