capacitor-performance について
Capacitor アプリの性能調査と改善を支援するスキルです。起動の遅さ、カクつき、メモリ増加、ネイティブ呼び出しの遅延を調べる開発者向けに、WebView、ブリッジ、ネイティブ処理、ネットワークを分けて計測します。
主な機能
- 01WebView、ブリッジ、ネイティブ、通信のボトルネック分類
- 02iOS と Android のプロファイリング資料
- 03ブリッジ転送量とメモリ使用の指針
- 04起動順序とリスナー解放の確認
- 05変更前後の性能検証
活用シーン
- 01実機でのコールドスタート遅延の調査
- 02カクつきやメモリ増加の原因特定
- 03画像やファイル処理の遅延削減
- 04ネイティブ呼び出しと通信待ちの比較
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ cap-go/ capgo-skills --skill capacitor-performanceインストール後、Claude Code で/capacitor-performanceを入力して読み込まれたか確認します。
同梱ファイルmetadata.jsonreferences/profiling.md
詳細
Capacitor のパフォーマンス
まずボトルネック(WebView の JS、ブリッジ、ネイティブ、ネットワーク)を特定し、原因に合わせて修正します。コード分割、画像圧縮、仮想リストなどの一般的なウェブ向けのアドバイスも引き続き有効です。このスキルでは、Capacitor 内で異なる点に焦点を当てます。
使用するタイミング
次の場合に使用します:
- コールドスタートが遅い、スプラッシュ画面が長く表示される、または最初の画面が数秒間真っ白になる。
- 写真を撮った後、大きなファイルを開いた後、またはバックグラウンドから復帰した後に、アプリが「再起動」する、あるいは白い画面が表示される。
- ネイティブ呼び出しが遅く感じる。特に画像、ファイル、大きな HTTP レスポンスを扱う場合。
- スクロールやアニメーションがデバイス上でのみカクつく(古い Android WebView でよく発生します)。
- メモリ使用量が時間とともに増える、リスナーが蓄積する。
次の場合は使用しないでください:
- クラッシュ、ネイティブのスタックトレース →
debugging-capacitor、capacitor-native-observability。 - デバイスからログを取得する →
ios-android-logs。 - オフラインストレージの設計/SQLite の移行 →
capacitor-offline-first、sqlite-to-fast-sql。 - スプラッシュ画面の設定 →
capacitor-splash-screen、キーボードによるリサイズのカクつき →capacitor-keyboard。 - OTA バンドルのサイズや差分更新 →
capgo-live-updates。
手順
- 実機でリリースに近いビルドを使って再現します。 デバッグビルドではブリッジ呼び出しごとにログが出力され、最適化が無効になります。シミュレーターではメモリや GPU の制限が表面化しません。
- 以下のプロファイリング表を使って分類します。 JS のメインスレッドがビジー、ブリッジのペイロードが大きい、ネイティブスレッドがビジー、またはネットワーク待ちのいずれかです。
- 落とし穴の一覧を確認します。 Capacitor 固有のリグレッションの大半は、このいずれかに該当します。
- 一度に一つ修正し、同じコマンド/トレースを使って再度測定します。 修正前後の数値をユーザーに報告します。
プロファイリングのクイックリファレンス
| 確認事項 | ツール |
|---|---|
| JS の長時間タスク、レイアウト、描画(iOS) | Safari > 開発 > デバイス > アプリ > タイムライン。リリースビルドでは ios.webContentsDebuggingEnabled: true が必要です(iOS 16.4 以降では、デバッグビルドはデフォルトで検査できます)。 |
| JS の長時間タスク、メモリヒープ(Android) | chrome://inspect > パフォーマンス/メモリ。 |
| ネイティブの CPU、割り当て、リーク(iOS) | Xcode > Product > Profile > Time Profiler/Allocations/Leaks。実環境のデータは Xcode Organizer > Metrics で確認できます。 |
| ネイティブの CPU/メモリ(Android) | Android Studio > Profiler。 |
| Android のコールドスタート時間 | adb shell am force-stop <appId> && adb shell am start -W -n <appId>/.MainActivity(TotalTime を確認します)。 |
| Android のフレーム統計 | adb shell dumpsys gfxinfo <appId>(カクついたフレームの割合)。 |
| Android WebView のバージョン | adb shell dumpsys webviewupdate(現在のプロバイダーとバージョン)。 |
詳細と手順:references/profiling.md。
Capacitor 固有の落とし穴
- ブリッジ経由の Base64。 すべてのプラグイン呼び出し/応答は、ブリッジを介して JSON にシリアライズされます。数 MB の Base64 文字列は両方のスレッドを停滞させ、WebView プロセスを終了させる可能性があります。
- カメラ(
@capacitor/camera8.1+ API):takePhoto/chooseFromGalleryを使い、result.webPathを表示します。targetWidth/targetHeight/qualityを設定します。アップロードする場合を除き、フル解像度の Base64 を読み込まないでください。以前のgetPhoto/pickImagesは非推奨です。 - ファイル:パスを渡し、
<img>/<video>の src にはCapacitor.convertFileSrc(uri)を使います。大きなファイルの読み込みにはFilesystem.readFileInChunksを、アップロード/ダウンロードには@capacitor/file-transferを使います。
- カメラ(
- メモリ逼迫時に WebView プロセスが終了する。 iOS では
WebView process terminatedがログに記録されます。Capacitor はブリッジをリセットしてページを再読み込みするため、メモリ上の JS 状態が失われます(再起動したように見えます)。Android ではonRenderProcessGoneが呼び出されます。処理しない場合、アプリは終了します。ピーク時のメモリ使用量を減らし(落とし穴 1、大きなキャンバス、デコード済み画像)、重要な状態を永続化して、読み込み時に復元してください。 - 大きな応答に対する CapacitorHttp。
plugins.CapacitorHttp.enabled: trueの場合、fetch/XMLHttpRequestはネイティブ経由で処理され、ボディはブリッジを通過します。CORS/Cookie には適していますが、大きなダウンロードやストリーミングでは遅くなります。大きなファイルには file-transfer を使ってください。 - データベースとしての Preferences。
@capacitor/preferences(UserDefaults / SharedPreferences)は、小さなキー/値データ用です。大きな JSON ブロブは全体を読み書きするため、起動が遅くなります。SQLite(たとえば@capgo/capacitor-fast-sql)を使ってください。 - リリース時のログ。
loggingBehaviorのデフォルトはdebug(デバッグビルドでのみログを出力)です。productionに設定されていると、リリース時にすべてのブリッジ呼び出しがログに記録されます。debugまたはnoneに戻してください。 - 設定に残った開発サーバー。 リリースビルドで
server.urlが LAN/ライブリロードサーバーを指していると、ネットワーク経由で読み込まれるため、オフラインでは失敗します。リリース前に削除してください。Capacitor 9 では、設定を編集する代わりにライブリロードにnpx cap run <platform> --urlを使います。 - 古い Android System WebView。 パフォーマンスと CSS のサポートは OS のバージョンではなく、更新可能な WebView に依存します。
android.minWebViewVersion(デフォルトは 60)はエラー/server.errorPathを表示するだけで、何も更新しません。高コストな CSS(backdrop-filter、スクロールするコンテンツ上の大きなbox-shadow/filter)は、低性能な WebView でカクつきます。 - リスナーのリーク。
addListenerはPromise<PluginListenerHandle>を返すため、これを await し、アンマウント時にremove()を呼び出してください。ルート変更のたびに登録し直すと、ネイティブコールバックが増えていきます(appStateChange、keyboardWillShow、位置情報のwatchPosition—clearWatchで解除します)。 - 起動の順序。 最初の意味のある描画が完了してからスプラッシュ画面を非表示にします(
launchAutoHide: false+SplashScreen.hide())。分析の初期化や権限チェックなど、重要でないプラグイン呼び出しは、初回描画の後まで遅らせてください。Capacitor 9 では Android のlaunchFadeOutDurationのデフォルトが 200 から 0 に変更されています。 - プラグインのインポートは起動コストではありません。
@capacitor/*パッケージをインポートすると軽量なプロキシが作成されますが、Web 実装はregisterPluginを介してすでに遅延読み込みされます。代わりに、重い JS ライブラリ(チャート、PDF、マップ SDK の JS)やルートを遅延読み込みしてください。
検証
- 同じデバイス、同じビルドタイプで、変更前後の数値を比較します。コールドスタートの
TotalTime(Android)または Instruments App Launch / Organizer の起動時間(iOS)、カクついたフレームの割合、Allocations/Profiler でのピークメモリを確認します。 - リリース設定を grep します:
grep -nE '"url"|loggingBehavior|webContentsDebuggingEnabled' capacitor.config.*を実行し、開発用のserver.urlがないことを確認します。 - 落とし穴 1 について、フルサイズの Base64 の使用箇所を grep します:
grep -rnE "resultType: *CameraResultType.Base64|readFile\(|getPhoto\(" src/。 - アプリをバックグラウンドにし、カメラを 10 回開いてから戻ります。ページが再読み込みされない(iOS)、またはクラッシュしない(Android)ことを確認します。
エラー処理
| メッセージ/症状 | 意味 | 対処方法 |
|---|---|---|
WebView process terminated(Xcode コンソール) | WKWebView のコンテンツプロセスが強制終了されました(メモリ不足) | 対策 1/2:ピークメモリを抑え、状態を永続化します。 |
Android の logcat に Render process ... gone と表示される/重いページの後にアプリが終了する | レンダラがクラッシュしたか、強制終了されました | メモリ使用量を減らします。復旧が必要な場合は、WebViewListener の onRenderProcessGone を使って処理します。 |
ANR in <appId>(logcat) | メインスレッドが 5 秒を超えてブロックされています。UI スレッドでの重いプラグイン処理が原因であることがよくあります | カスタムプラグインのネイティブ処理をメインスレッドから移し、Android Studio でプロファイリングします。 |
Message from debugger: Terminated due to memory issue | iOS の jetsam によりアプリ全体が強制終了されました | Allocations インストゥルメントを使い、画像のデコードとキャッシュを減らします。 |
WebView のバージョンが minWebViewVersion 未満であることを示す logcat エラー | デバイスの WebView が古すぎます | Android System WebView を更新するようユーザーに案内し、server.errorPath ページを表示します。 |
参考資料
必要な場合にのみ読み込んでください。
references/profiling.md:手順ごとのトレース(Safari Timelines、Chrome Performance、Instruments のテンプレート、Android Profiler、adb コマンド)と、その読み方。
コメント
Markdown 対応