# APK 검사 (P0, 10/11): 플러그인 등록 (GeneratedPluginRegistrant) 이 dex 에 들어 있는지 본다. # - 이 파일이 빠지면 플러그인이 하나도 등록되지 않아 켜자마자 꺼진다 ('JNI is not initialized … too early', 묶음5 a192ef6 시험판). # - 원인: android\app\src\main\java\io\flutter\plugins\GeneratedPluginRegistrant.java (git 밖, 생성 파일) 를 지운 뒤 # flutter build apk --no-pub 로 빌드하면 다시 만들어지지 않는다. 지웠으면 --no-pub 없이 빌드하거나 먼저 flutter pub get. # - (195) third_party 의 네이티브 실행 파일: android\app\third_party_libs.txt 의 것이 모두 들어 있고, 목록에 없는 third_party 파일은 없는지. # 사용: powershell -ExecutionPolicy Bypass -File tool\check_apk.ps1 -Apk build\app\outputs\flutter-apk\app-release.apk param( [Parameter(Mandatory = $true)][string]$Apk, # 목록 파일 (기본: 이 스크립트가 있는 앱 폴더의 android\app\third_party_libs.txt) [string]$Libs = '' ) $ErrorActionPreference = 'Stop' Add-Type -AssemblyName System.IO.Compression.FileSystem $Apk = (Resolve-Path $Apk).Path $want = [System.Text.Encoding]::ASCII.GetBytes('Lio/flutter/plugins/GeneratedPluginRegistrant;') # 바이트 배열 안에 [needle] 이 있는지 function Test-Bytes([byte[]]$hay, [byte[]]$needle) { $first = $needle[0] $i = [Array]::IndexOf($hay, $first) while ($i -ge 0 -and $i -le $hay.Length - $needle.Length) { $ok = $true for ($k = 1; $k -lt $needle.Length; $k++) { if ($hay[$i + $k] -ne $needle[$k]) { $ok = $false; break } } if ($ok) { return $true } $i = [Array]::IndexOf($hay, $first, $i + 1) } return $false } $app = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path if (-not $Libs) { $Libs = Join-Path $app 'android\app\third_party_libs.txt' } $listed = @(Get-Content $Libs | ForEach-Object { $_.Trim() } | Where-Object { $_ -and -not $_.StartsWith('#') }) # third_party 에 있는 파일 (목록 밖이면 APK 에 없어야 함) $tp = @() foreach ($d in 'third_party\rsync\android', 'third_party\sdcpp\android') { $dir = Join-Path $app $d if (Test-Path $dir) { $tp += @(Get-ChildItem $dir -Recurse -Filter *.so | ForEach-Object { $_.Name }) } } $zip = [System.IO.Compression.ZipFile]::OpenRead($Apk) try { $libsIn = @($zip.Entries | Where-Object { $_.FullName -like 'lib/arm64-v8a/*.so' } | ForEach-Object { $_.Name }) $dexes = @($zip.Entries | Where-Object { $_.FullName -match '^classes\d*\.dex$' }) if ($dexes.Count -eq 0) { throw "dex 가 없습니다: $Apk" } $found = $null foreach ($e in $dexes) { $s = $e.Open() $ms = New-Object System.IO.MemoryStream try { $s.CopyTo($ms) } finally { $s.Dispose() } if (Test-Bytes $ms.ToArray() $want) { $found = $e.FullName; break } } } finally { $zip.Dispose() } $bad = @() foreach ($n in $listed) { if ($libsIn -notcontains $n) { $bad += "목록에 있는데 APK 에 없음: $n" } } foreach ($n in ($tp | Select-Object -Unique)) { if ($listed -notcontains $n -and $libsIn -contains $n) { $bad += "목록에 없는데 APK 에 들어감: $n" } } if ($bad) { $bad | ForEach-Object { Write-Host "실패: $_ ($Libs)" -ForegroundColor Red } exit 1 } if (-not $found) { Write-Host "실패: GeneratedPluginRegistrant 가 dex 에 없습니다 (켜자마자 꺼지는 APK). flutter pub get 뒤 --no-pub 없이 다시 빌드하세요." -ForegroundColor Red exit 1 } Write-Host "통과: GeneratedPluginRegistrant 있음 ($found) · third_party 실행 파일 $($listed.Count)개 목록과 같음"