Slide 1

Slide 1 text

Flutter DevToolsで発見! 本番アプリのパフォーマンス問題と改善の実践 FlutterKaigi 2025@Otemachi PLACE HALL & CONFERENCE ASSIGN Hall 11:30~ 後藤 孝輔

Slide 2

Slide 2 text

自己紹介 ● 名前:Kosuke Goto ● 所属:スタディプラス株式会社(2023年新卒入社) ● Flutter歴:3年 ○ FlutterKaigi 2024 ■ 気をつけたい!Desktop対応で陥りやすい罠とその対策 ■ https://2024.flutterkaigi.jp/session/6cda6895-57f8-47dc-ab 29-ebcbd9c7f95a 2

Slide 3

Slide 3 text

Flutter DevTools 3

Slide 4

Slide 4 text

Flutter DevToolsを使ったことがある方🙋 4

Slide 5

Slide 5 text

Flutter DevTools 5

Slide 6

Slide 6 text

Flutter DevTools パフォーマンス分析やデバッグのためのツールを提供 - Flutter Inspector - Performance - Memory - Network - CPU Profiler - etc.. →デバッグの機能しか使ったことないなあ... 6

Slide 7

Slide 7 text

きっかけとなる出来事 本番アプリでパフォーマンス問題が発生💥 →Flutter DevToolsを使った調査 パフォーマンス分析における使い方、便利な機能など色々な発見があった 7

Slide 8

Slide 8 text

この発表について - 事例の中でFlutter DevToolsをどのように活用できたかを話します - 皆さんがFlutter DevToolsを活用して改善を行うきっかけになればと思います 8

Slide 9

Slide 9 text

今日話すこと - パフォーマンス改善事例① - パフォーマンス改善事例② - 状況別に使える Flutter DevTools活用ガイド - まとめ 9

Slide 10

Slide 10 text

Studyplusについて - 学習を記録して管理、シェアできるアプリ - 数十万人規模のユーザーが日常的に利用 - iOS/Android/Webで提供 - 今年の3月末にフルFlutter化 10

Slide 11

Slide 11 text

今日話すこと - パフォーマンス改善事例① - パフォーマンス改善事例② - 状況別に使える Flutter DevTools活用ガイド - まとめ 11

Slide 12

Slide 12 text

事例①の概要 12 ユーザーからのお問合せ 調査 原因の判明 改善 ● Flutter版iOSアプリをリリース直後アプリ が「重い」「カクカクする」というお問い合わ せが届く ● SNS、ストアレビューにも同様の投稿あり

Slide 13

Slide 13 text

事例①の概要 13 ユーザーからのお問合せ 調査 原因の判明 改善 ● 手元での再現確認 ● ソースコードを確認 →有力な手がかりなし ● Flutter DevToolsを使った調査

Slide 14

Slide 14 text

事例①の概要 14 ユーザーからのお問合せ 調査 原因の判明 改善 ● 状態監視の方法に問題 があり、不要な buildが発生してしまっていた

Slide 15

Slide 15 text

事例①の概要 15 ユーザーからのお問合せ 調査 原因の判明 改善 ● 不要なbuildの発生を防ぐ ようにソース コードを修正 ● 修正によりパフォーマンスが改善

Slide 16

Slide 16 text

Flutter DevTools起動 17 ※Android Studioの場合

Slide 17

Slide 17 text

Flutter DevTools起動 18 ※Android Studioの場合 ブラウザで起動 Android Studio内で起動

Slide 18

Slide 18 text

Flutter DevToolsを起動 19

Slide 19

Slide 19 text

Flutter DevToolsを起動 20 →パフォーマンスを正確に測定するためにはアプリをProfileモードで実行してね

Slide 20

Slide 20 text

Flutter DevToolsを起動 22 アプリをProfileモードで実行

Slide 21

Slide 21 text

Performance view 23

Slide 22

Slide 22 text

Performance view 24

Slide 23

Slide 23 text

Frameチャートの見方 25 UI - Dartコードの実行 - Widgetの build/layout/paint(準備) Raster - GPUと通信してピクセルを画面に描 画する部分

Slide 24

Slide 24 text

Frameチャートの見方 26 ✅約16ms※のラインを超えている場合、赤い色で表示される →Jank(カクつき)が発生している ※1(s) ÷ 60(frame) = 0.0166666…(s) ≒ 16.7ms

Slide 25

Slide 25 text

実際に見てみる 27

Slide 26

Slide 26 text

タブを切り替えた時にJank(カクつき)が発生している 28

Slide 27

Slide 27 text

Frameをクリックして詳細を見る 29 2 1 3 4 5

Slide 28

Slide 28 text

Frameをクリックして詳細を見る 30

Slide 29

Slide 29 text

Frame Analysisで詳細を確認する 31

Slide 30

Slide 30 text

Frame Analysisで詳細を確認する 32 build/layout/paint時間の内訳

Slide 31

Slide 31 text

Frame Analysisで詳細を確認する 33 buildに時間がかかっている📝

Slide 32

Slide 32 text

Timeline Eventsで詳細を確認する 34

Slide 33

Slide 33 text

Timeline Eventsで詳細を確認する 36

Slide 34

Slide 34 text

Timeline Eventsで詳細を確認する 37 👈 uiと書かれたところを見にいく

Slide 35

Slide 35 text

Timeline Eventsで詳細を確認する 38

Slide 36

Slide 36 text

Timeline Eventsで詳細を確認する 40 ここから何がわかるんだ...?

Slide 37

Slide 37 text

Enhance Tracing 41

Slide 38

Slide 38 text

Enhance Tracing 42 ✅TimelineにWidgetのbuild情報が追加されるようになる

Slide 39

Slide 39 text

Frame Analysisのヒント 43 デバッグのヒントとなるテキスト💡

Slide 40

Slide 40 text

Trace widget buildsを有効にすると 46

Slide 41

Slide 41 text

Trace widget buildsを有効にすると 47 タブ2の画面 タブ4の画面 タブ3の画面 タブ1→タブ2への切り替えなのに、関係のない画面がbuildされている

Slide 42

Slide 42 text

Flutter DevToolsでの調査でわかったこと - タブを切り替えた時にカクつきが発生 - 該当のフレームではbuildに時間がかかっていた - その原因は不要なbuildが呼ばれていたこと →この情報を元にコードを確認 48

Slide 43

Slide 43 text

事例①の概要 49 ユーザーからのお問合せ 調査 原因の判明 改善 ● 状態監視の方法に問題 があることが判 明 ● 不要なrebuildが発生してしまっていた

Slide 44

Slide 44 text

問題となっていた状態の監視 50 RiverpodのNotifierProvider state: ローカルキャッシュに保存するデータクラス (ユーザー設定など全 54プロパティ) タブ1の画面 タブ2の画面 タブ3の画面 タブ4の画面 状態の変更を監視

Slide 45

Slide 45 text

問題となっていた状態の監視 51 タブ1の画面 タブ2の画面 タブ3の画面 タブ4の画面 状態を更新 下タブ RiverpodのNotifierProvider state: ローカルキャッシュに保存するデータクラス (ユーザー設定など全 54プロパティ)

Slide 46

Slide 46 text

問題となっていた状態の監視 52 タブ1の画面 タブ2の画面 タブ3の画面 タブ4の画面 状態の変更が通知 下タブ RiverpodのNotifierProvider state: ローカルキャッシュに保存するデータクラス (ユーザー設定など全 54プロパティ)

Slide 47

Slide 47 text

問題となっていた状態の監視 53 タブ1の画面 タブ2の画面 タブ3の画面 タブ4の画面 一斉にbuildメソッドが呼ばれる 下タブ RiverpodのNotifierProvider state: ローカルキャッシュに保存するデータクラス (ユーザー設定など全 54プロパティ)

Slide 48

Slide 48 text

問題となっていた実装(タブ2画面での例) 54 class RecordPage extends HookConsumerWidget { const RecordPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final hasSeenOnboardingMessage = ref.watch(sharedPreferencesPresenterProvider).hasSeenOnboardingMessage; … } } 巨大クラスを公開するRiverpodのProvider

Slide 49

Slide 49 text

問題となっていた実装(タブ2画面での例) 55 class RecordPage extends HookConsumerWidget { const RecordPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final hasSeenOnboardingMessage = ref.watch(sharedPreferencesPresenterProvider).hasSeenOnboardingMessage; … } } これだとどのプロパティが更新されてもbuildが発火する

Slide 50

Slide 50 text

改善:watchを局所化する(.select) Riverpodのselectで必要なプロパティだけ監視 56 final hasSeenOnboardingMessage = ref.watch(sharedPreferencesPresenterProvider).hasSeenOnboardingMessage; before final hasSeenOnboardingMessage = ref.watch( sharedPreferencesPresenterProvider.select( (prefs) => prefs.hasSeenOnboardingMessage, ), ); after

Slide 51

Slide 51 text

改善後の状態監視 57 タブ1の画面 タブ2の画面 タブ3の画面 タブ4の画面 状態を更新 下タブ RiverpodのNotifierProvider state: ローカルキャッシュに保存するデータクラス (ユーザー設定など全 54プロパティ)

Slide 52

Slide 52 text

改善後の状態監視 58 タブ1の画面 タブ2の画面 タブ3の画面 タブ4の画面 関係のないプロパティの変更ではbuildが呼ばれない👍 下タブ RiverpodのNotifierProvider state: ローカルキャッシュに保存するデータクラス (ユーザー設定など全 54プロパティ)

Slide 53

Slide 53 text

改善後の確認(Frameチャート) 59

Slide 54

Slide 54 text

改善後の確認(Frame Analysis) 60

Slide 55

Slide 55 text

改善後の確認(Timeline Events) 61

Slide 56

Slide 56 text

改善後の確認(Timeline Events) 62 before after

Slide 57

Slide 57 text

事例①のまとめ - アプリが全体的に「重い」、「カクつく」 との問い合わせ - FrameのJank(カクつき)を検知するためPerformance viewを確認 - Frame chart → Frame Analysis → Timeline Events(Enhance Tracing)と見てい くことで不要なbuildが呼ばれている ことを発見 - RiverpodのProviderをwatchする際は適切に 63

Slide 58

Slide 58 text

今日話すこと - パフォーマンス改善事例① - パフォーマンス改善事例② - 状況別に使える Flutter DevTools活用ガイド - まとめ 64

Slide 59

Slide 59 text

事例②の概要 65 ユーザーからのお問合せ 調査 原因(仮説) 改善 ● 特定のユーザーからお問い合わせ ○ 本棚画面※でアプリが落ちる ○ 本棚画面をしばらく触ると落ちる ※本棚は教材画像が並ぶ このような画面👉

Slide 60

Slide 60 text

事例②の概要 66 ユーザーからのお問合せ 調査 原因(仮説) 改善 ● 手元での再現確認 ○ 手元では再現しない ● ソースコードを確認 ● Flutter DevToolsでの調査

Slide 61

Slide 61 text

事例②の概要 68 ユーザーからのお問合せ 調査 原因(仮説) 改善 ● サーバーから取得する画像のサイズが非 常に大きく、それをアプリ側でサイズ指定 せずにキャッシュとして持っていることが 問題?

Slide 62

Slide 62 text

事例②の概要 69 ユーザーからのお問合せ 調査 原因(仮説) 改善 ● キャッシュサイズを指定 ● 問い合わせのあったユーザーから改善さ れたと連絡をもらった

Slide 63

Slide 63 text

Flutter DevToolsでの調査 本棚画面でアプリが落ちる - UIスレッドの処理負荷が高い? - メモリリークやメモリ膨張の可能性? →本棚画面を中心にPerformance view, Memory viewを見る 70

Slide 64

Slide 64 text

Performance view 71 特に異常は見られなかった

Slide 65

Slide 65 text

Memory view 72

Slide 66

Slide 66 text

Memory chart 73 Dart/Flutterのヒープメモリと一部ネイティブメモリの状態を 時間経過とともに視覚的に表示

Slide 67

Slide 67 text

Memory chart 74 メモリリークしているかどうか ✅GCイベントの後もDart/Flutterのヒープメモリが 継続的に増加し続けるかどうかをみる

Slide 68

Slide 68 text

Memory view 75 GCイベントの前後を比較して見たが、大きく増えているなどはなかった

Slide 69

Slide 69 text

原因の判明 Flutter DevToolsの調査に行き詰まる ↓ チームメンバーが「画像キャッシュが怪しいのでは?」と仮説を立てる 76 Image.network( item.materialImageUrl , ); Image.network( item.materialImageUrl , cacheWidth: cacheSize, cacheHeight: cacheSize, ); キャッシュサイズを指定するように変更

Slide 70

Slide 70 text

修正後のリリースで解消 - アプリが落ちることなく使えるようになったとの報告を受けた 77 考えられる原因 - 該当ユーザーの登録している画像のサイズが極端に大きかった - その画像をそのままのサイズでキャッシュとして持っていた →メモリ圧迫でアプリが落ちていた

Slide 71

Slide 71 text

なぜFlutter DevToolsでは分からなかったのか? ①該当ユーザーと同じ環境での検証ができていなかった - 検証アカウントの教材画像サイズ<<<<該当ユーザーの教材画像サイズ ②画像キャッシュはMemory Viewの可視化する領域外で膨張していた?(仮説) 78 →Flutter DevToolsを使って検知できないのか?

Slide 72

Slide 72 text

Highlight Oversized Images - Flutter Inspectorのデバッグオプションの一つ - 必要以上に大きなメモリを使用している画像を検出できる 79

Slide 73

Slide 73 text

Highlight Oversized Images 80 debugモードで実行 ↓ Flutter Inspectorを起動

Slide 74

Slide 74 text

Highlight Oversized Images 81 これをONにする

Slide 75

Slide 75 text

Highlight Oversized Images 82 ✅必要とされるメモリよりも128 KB以上多くメモリを消費している画像→色、 上下反転画像として表示される

Slide 76

Slide 76 text

Highlight Oversized Images 83 改善前の状態で確認すると、確かに右のような状態になっていた

Slide 77

Slide 77 text

事例②のまとめ - 特定のユーザーから本棚画面でアプリが落ちる とのお問い合わせ - Performance, Memory viewを確認するも問題を検知できず - 画像キャッシュサイズの指定 によって問題が解決 - 過度にメモリ消費している画像はHighlight Oversized Imagesオプションで検知 可能 84

Slide 78

Slide 78 text

今日話すこと - パフォーマンス改善事例① - パフォーマンス改善事例② - 状況別に使える Flutter DevTools活用ガイド - まとめ 85

Slide 79

Slide 79 text

状況別に使えるFlutter DevTools活用ガイド - ①全体的なパフォーマンス問題の検知 - ②特定画面や機能の深掘り 86

Slide 80

Slide 80 text

状況別に使えるFlutter DevTools活用ガイド - ①全体的なパフォーマンス問題の検知 - ②特定画面や機能の深掘り 87

Slide 81

Slide 81 text

①全体的なパフォーマンス問題の検知 88 機能・オプション名 タブ できること Performance Overlay (Frame chart) Performance カクつき(UI Jank)の箇所をざっくり把握できる Highlights Repaints Inspector 不要な再描画を検知できる Count widget builds Performance 不要なbuildを検知できる Highlight Oversized Images ※ Inspector 画像サイズ過大を検知できる ※事例②で紹介したので説明は省略

Slide 82

Slide 82 text

Performance Overlay Frame chartをアプリ上で確認できる →カクつき(UI Jank)の箇所をざっくり把握できる ✅見るポイント - max, avgが高くなる時 - バーが赤くなるとJank 89 ※Profileモードで利用

Slide 83

Slide 83 text

Highlight Repaints 再描画が起こるたびに境界値の色が変 わる →不要な再描画を検知できる ✅見るポイント - 再描画対象が固定領域まで及んで いないか - 不必要に再描画されていないか 90 ※Debugモードのみ

Slide 84

Slide 84 text

Count Widget builds 各フレームでbuildされたWidgetとその build回数がわかる →不要なbuildを検知できる ✅見るポイント - 関係のないWidgetがbuildされていな いか - 同じWidgetが複数回buildされていな いか 91 ※Debugモードのみ

Slide 85

Slide 85 text

状況別に使えるFlutter DevTools活用ガイド - ①全体的なパフォーマンス問題の検知 - ②特定画面や機能の深掘り 92

Slide 86

Slide 86 text

②特定画面や機能の深掘り 93 機能・オプション名 タブ できること Frame Analysis Timeline Events Enhance Tracing ※ Performance カクつき(UI Jank)のボトルネック特定 Diff Snapshots Memory メモリリークしている箇所の特定 CPU Profiler CPU Profiler 時間がかかっている処理の特定 ※事例①で紹介したので説明は省略

Slide 87

Slide 87 text

Diff Snapshots 94 Memory Snapshotを比較できる →メモリリークしている箇所を特定で きる

Slide 88

Slide 88 text

Diff Snapshots 96 ✅破棄されるべきインスタンスが残っていないか

Slide 89

Slide 89 text

CPU Profiler 97 アプリ実行中のCPU消費時間を記録・集計 →時間のかかっている処理を特定できる ✅見るポイント - Bottom UpタブのSelf Timeが高いメソッド - Call TreeタブのTotal Timeが高いメソッド

Slide 90

Slide 90 text

CPU Profiler 98 アプリ実行中のCPU消費時間を記録・集計 →時間のかかっている処理を特定できる ✅見るポイント - Bottom UpタブのSelf Timeが高いメソッド - Call TreeタブのTotal Timeが高いメソッド

Slide 91

Slide 91 text

今日話すこと - パフォーマンス改善事例① - パフォーマンス改善事例② - 状況別に使える Flutter DevTools活用ガイド - まとめ 99

Slide 92

Slide 92 text

まとめ - 事例とともにFlutter DevTools活用の流れを見ていった - 事例①UIのカクつき→Performance viewで原因が特定できた - 事例②アプリが落ちる→Flutter Inspectorのdebugオプションで特定可能とわかった - 状況別に使える機能について紹介した この発表がFlutter DevTools活用の参考になればと思います 100

Slide 93

Slide 93 text

参考文献 Flutter Docs - https://docs.flutter.dev/tools/devtools - https://docs.flutter.dev/perf Flutter DevTools Tutorial(Medium) - https://medium.com/@fluttergems/mastering-dart-flutter-devtools-part-1-introd uction-installation-4f703a8cfcc8 101