Contents
画像リソースの適切な扱い方
React Native アプリにおけるロード遅延の主な原因は、画像処理の非効率性です。特にネットワーク環境が不安定な際には、画像のダウンロードやキャッシュ戦略がパフォーマンスに直結します。
サブタイトル: リサイズとキャッシュ戦略
画像の適切な扱い方は、アプリ全体のレスポンス性を大きく左右します。以下に具体的な対応策を紹介します。
- Image コンポーネントの prop 活用:
resizeModeで画像の表示サイズを指定し、不要なリサイズ処理を避ける - FastImage ライブラリ導入: キャッシュ機能とプログレッシブロードによりロード時間を短縮(例:
source={{ uri: 'https://example.com/image.jpg', priority: FastImage.priority.normal }}) - ネットワーク状態に応じた処理: 状況によっては低解像度画像を優先表示し、後で高解像度を読み込む戦略も有効です
重要なポイントは、「不要なリサイズ」と「適切なキャッシュ」 の2点に集約されます。これにより、ユーザーが感じる遅延感を大幅に軽減できます。
FastImageの導入方法:
npm install fast-imageを実行- Androidの場合、
android/app/src/main/assets/fonts/に必要ファイルを配置 - iOSでは自動的に処理される
| 対応策 | 説明 | メリット |
|---|---|---|
resizeMode 指定 |
画像の表示サイズを直接指定し、リサイズ処理を回避 | CPU負荷低減 |
| FastImage 導入 | キャッシュとプログレッシブロード機能付き | ネットワーク遅延時のロード改善 |
| ネットワークリミット対策 | 画像の品質/サイズを状況に応じて変更 | 初回表示スピード向上 |
FlatList の最適化ポイント
リストコンポーネントは、React Native アプリで最も多く使用されるUI要素です。しかし、データ量が増えると描画パフォーマンスが急激に低下する傾向があります。
keyExtractor の正しい実装
FlatList で差分更新を効かせるには、keyExtractor プロパティの正しくない使用が原因となるケースが多いです。以下の例のように、一意なIDを必ず指定しましょう:
|
1 2 3 4 5 6 7 8 |
const keyExtractor = (item, index) => item.id; <FlatList data={items} keyExtractor={keyExtractor} renderItem={({ item }) => <ItemComponent item={item} />} /> |
windowSize と initialNumToRender の調整
windowSize(表示領域の範囲)や initialNumToRender(初期描画数)を適切に設定することで、スクロール時の描画遅延を抑制できます。例として、以下のようなパラメータが効果的です:
|
1 2 3 4 5 6 |
<FlatList data={items} initialNumToRender={10} // 初期表示数: ユーザーの目に入る範囲をカバー windowSize={25} // スクロール時の描画領域: 大きすぎるとメモリ使用量が増加する可能性あり /> |
windowSizeの推奨値は20〜30ですが、25が一般的な最適範囲とされています。具体的な値はアプリの特性に応じて調整してください。
| パラメータ | 推奨値 | 説明 |
|---|---|---|
initialNumToRender |
10〜20 | 初期描画数を多めに設定し、スクロール開始時のストレスを減らす |
windowSize |
20〜30 | スクロール中はこの範囲のアイテムを常に描画状態に保つ |
FlatList は「差分更新」機能により、再描画の処理を最小限に抑えることができます。しかし、keyExtractorが不適切だとその効果が発揮されないため注意が必要です。
JavaScript ブロッキング処理の回避方法
UIスレッドで実行されるJavaScriptは、長時間の同期処理により画面がフリーズしてしまうことがあります。特に画像圧縮やデータ解析などでは顕著です。
非同期処理の設計パターン
-
async/await: 複数の非同期処理を並列に実行することで、UIスレッドをブロッキングしない
javascript
async function processImages() {
await Promise.all([
loadImage1(),
loadImage2()
]);
} -
Web Worker の活用: CPU負荷が高そうな処理は別スレッドで実行(例: カメラ画像のリアルタイム処理など)
javascript
// worker.js
self.onmessage = function(e) {
const result = processHeavyTask(e.data);
self.postMessage(result);
};
Web Worker 内では React の State にアクセスできません。結果は
postMessage経由でメインスレッドに返却し、イベントリスナーで受信してください。
Native Module 活用戦略
JavaScript では非効率なタスク(例: 大規模な画像処理、音声解析)をネイティブコードへ移行することで、パフォーマンスの飛躍的な改善が見込めます。
重い処理のネイティブ移行判定基準
以下のケースではネイティブ実装を検討するべきです:
- 画像処理: 結構なサイズの画像に対してのフィルタ適用やコンバート
- データ解析: 大量のJSONデータを処理する際のスクリプト
- 音声認識: 実時間での音声処理が求められる場合
ネイティブモジュール作成例(Android):
|
1 2 3 4 5 6 7 8 9 10 11 |
class MyNativeModule(context: Context) : ReactContextBaseJavaModule(context) { override fun getName(): String = "MyNativeModule" @ReactMethod fun heavyTask(input: String, promise: Promise) { // 重い処理をここに実装 val result = processHeavily(input) promise.resolve(result) } } |
Java8対応には
build.gradleのcompileOptionsをJavaVersion.VERSION_1_8に設定し、@JvmFieldや@JvmStaticアノテーションを追加してください。
メモリリークの特定手順
React Native アプリでは、Subscriptionやイベントリスナーを忘れるとメモリリークが発生しやすいという特徴があります。これを防ぐには、以下の手順で対応します。
React Native 特有のリークパターン
- subscription の忘れ:
ComponentWillUnmount内で unsubscribe しないとリスナーが残る - イベントハンドラの不適切な登録: 毎回新しい関数を作成してイベントに登録すると、参照カウントが正しく計算されない
特定ツールとして、Reactotron や Flipper はメモリリークの検出に有効です。以下のようなコードレビュー時のチェックポイントを意識しましょう:
- プロパティで受け取った関数やオブジェクトのライフサイクル管理
useEffect内での cleanup 関数の実装(React Hooks による場合)
CodePush はアプリ配信ツールであり、メモリリーク検証には不向きです。Systrace や Android Studio の Memory Profiler を活用してください。
実践への導入とパフォーマンス検証
前述の技術ポイントを組み合わせたプロジェクト構成例として、以下のようなステップが考えられます:
- 画像リソースの管理: FastImage + キャッシュポリシー
- リストコンポーネント最適化: FlatList に
keyExtractorとwindowSizeを導入 - 非同期処理対応: async/await + Web Worker 実装
- ネイティブモジュール活用: 重い画像処理をネイティブに移行
- メモリリーク検証: Reactotron でリーク発生確認
パフォーマンス改善効果の測定には、Systrace や Android Profiler の導入が有効です。アプリが実際に動作する環境での測定結果をもとに、最適化策の妥当性を検証しましょう。
今すぐ試してみましょう!記事内で紹介した技術をプロジェクトに適用し、コードレベルの改善効果を実感してください。