2025年8月10日·1分で読めます

オフラインと端末機能で比較する:Jetpack Compose vs React Native

Jetpack ComposeとReact Nativeを、デバイス機能、オフライン対応、バックグラウンド同期の信頼性、複雑なフォームや長いリストの滑らかさで比較します。

オフラインと端末機能で比較する:Jetpack Compose vs React Native

本当に比較していること

「デバイス機能」と言ったとき、人々が通常意味するのはカメラ撮影、GPS、Bluetoothスキャン、プッシュ通知、ファイルアクセス(ダウンロード、PDF、添付ファイル)、歩数計やネットワーク状態のようなバックグラウンドタスクなど、アプリを端末に結びつける部分です。重要なのは「それができるか」ではなく、「ハードウェアへの経路がどれだけ直接的か、そして端末やOSのバージョン間でどれだけ予測可能か」です。

オフラインモードが入ると話が全く変わります。ただ「インターネットなしで動く」スイッチではありません。ローカルストレージが必要で、どのデータが古くてもいいのか、変更が衝突したときにどう処理するかのルールが必要です(例:ユーザーがオフラインで受注を編集している間にサーバー側で同じ注文が更新された場合)。同期を加えると、単なる画面設計ではなく小さなシステムを設計していることになります。

Compose と React Native はしばしばネイティブ対クロスプラットフォームという構図で語られますが、オフラインや端末機能が絡むと差は接合部に現れます:どれだけ多くのブリッジ、プラグイン、回避策に依存するか、そして特定の端末で問題が起きたときにデバッグがどれだけ簡単か、です。

「パフォーマンス」もユーザー視点で定義する必要があります:起動時間、スクロールや入力時の応答(特に長いリストやフォーム)、バッテリーと発熱(静かに動いているバックグラウンド処理が電力を食うか)、安定性(クラッシュ、フリーズ、UIの不具合)。どちらでも良いアプリは作れます。トレードオフはどこで確実性を取りたいかです:OSレベルの制御を重視するか、コードベースを1つにして端の部品を増やすか。

デバイスアクセス:配管の違い

ここで大きな違いが出るのはUIウィジェットではなく、アプリがカメラ、Bluetooth、位置情報、ファイル、バックグラウンドサービスにどう到達するかです。

Androidでは、Jetpack ComposeはUIレイヤーです。アプリは通常のAndroid SDKや従来のネイティブライブラリを使い続けます。デバイス機能へのアクセスは直接的に感じられます:AndroidのAPIを呼び、権限を扱い、SDKをラップなしで統合できます。スキャナやMDM用のAndroidライブラリがベンダーから出ていれば、すぐに追加して使えることが多いです。

React Nativeは多くのアプリロジックをJavaScriptで動かすため、デバイスアクセスはネイティブモジュールを通ります。モジュールはAndroidではKotlin/Java、iOSではSwift/Obj-Cで書かれ、JavaScriptにデバイス機能を公開する小さな部分です。多くの一般的な機能は既存のモジュールでカバーされますが、データをネイティブとJSで渡すブリッジ(または新しいJSI/TurboModulesのアプローチ)に依存します。

未対応の機能にぶつかったとき、道は分かれます。Composeではネイティブコードをより多く書きます。React Nativeではカスタムネイティブモジュールを作って両プラットフォーム分を保守します。ここで「クロスプラットフォームを選んだら実は3つのコードベースになった」ということが静かに起きがちです。

要件が増えたときのチーム適合の考え方:

  • Composeは既にAndroidのスキルが強いチーム、または深いAndroid統合が必要な場合に向きます。
  • React NativeはJavaScriptに強いチームで、デバイス要件が典型的な場合に向きます。
  • どちらでも、バックグラウンドサービス、特殊ハードウェア、厳格なオフラインルールが必要ならネイティブ作業を見込んでください。

実際のパフォーマンス:ユーザーが気づく場面

「体感」の違いはアプリが開くとき、画面間を移動するとき、UIが処理をしている最中にユーザーがタップしているときに出ます。

起動時間と画面遷移は、Composeのほうがネイティブで他のAndroidコードと同じランタイム上で動くため高速を保ちやすいことが多いです。React Nativeも非常に速くできますが、コールドスタートではJSエンジンやバンドルの読み込みなど追加のセットアップが入りがちで、重いアプリや最適化されていないビルドだと小さな遅延が出やすいです。

負荷がかかったときの応答性も重要です。大きなJSONをパースしたり、長いリストをフィルタしたり、フォームの合計を計算するとき、ComposeアプリはKotlinのコルーチンに重い仕事を押し込んでメインスレッドを空ける設計がしやすいです。React NativeではJSスレッドをブロックする処理があるとタップやアニメーションが“引っかかる”ので、高負荷処理はネイティブ側に移すか慎重にスケジューリングする必要があります。

スクロールはユーザーが最初に不満を言う場所です。ComposeはLazyColumnのようなネイティブなリストツールを持ち、正しく書けばアイテムを仮想化してメモリをうまく再利用します。React NativeはFlatListなどを使いますが、画像サイズ、アイテムキー、再レンダーに注意しないとスタッタが出ます。

バッテリーとバックグラウンド作業は多くの場合、同期アプローチに落ち着きます。AndroidではComposeアプリがWorkManagerのようなプラットフォームツールを活用して予測可能なスケジューリングがしやすいです。React Nativeではバックグラウンド同期がネイティブモジュールとOSの制限に左右されるため、信頼性は端末や設定によって差が出やすいです。両方で積極的なポーリングはバッテリーを消費します。

パフォーマンスが最大のリスクなら、まず「問題のある画面」を1つ作って測定しましょう:一番重いリストと実データ量のあるオフラインフォームを作り、ミドルレンジの端末で計測してください。フラッグシップ端末だけで判断してはいけません。

オフラインモードの基本:データ保存と状態

オフラインモードは主にデータの問題であってUIの問題ではありません。どのUIスタックを選んでも、難しいのは端末に何を保存するか、オフライン中にUIが何を見せるか、後で変更をどう和解するかを決めることです。

ローカル保存:適切なツールを選ぶ

単純なルール:重要なユーザー生成データはアドホックなキー・バリューではなく正規のデータベースに保存しましょう。

問い合わせやソートをする構造化データ(注文、明細、顧客、下書き)はデータベースで扱い、設定はキー・バリュー、写真やPDFなどの大きな添付はファイルとして扱います。

ComposeのAndroidではRoomやSQLiteベースの選択肢と小さなキー・バリューストアを組み合わせることが多いです。React NativeではSQLite/Realm系のストレージライブラリと設定用のAsyncStorageやMMKVに相当するものを追加することが一般的です。

オフラインファーストの流れ:ローカルをソース・オブ・トゥルースにする

オフラインファーストとは、作成/編集/削除をまずローカルで行い、後で同期することを意味します。実践的なパターンは:ローカルDBに書き込んで、ローカルDBからUIを更新し、背景でサーバーにプッシュする、です。例えば営業担当が飛行機で注文を編集しても、自分の一覧ですぐ見えて、アプリが同期タスクをキューに入れて後で実行します。

競合は同じレコードが二つの端末で変わったときに起きます。よくある戦略は last-write-wins(単純だがデータを失う可能性あり)、マージ(メモなど追記型フィールドに向く)、ユーザーレビュー(価格や数量のように正確性が重要な場合に最適)です。

混乱するバグを避けるには「真実」を明確に定義してください:

  • UI状態は一時的(ユーザーが今入力しているもの)。
  • 保存された状態は永続的(クラッシュ後に再読み込みできるもの)。
  • サーバー状態は共有(他の端末が最終的に見るもの)。

これらの境界を保てば、フォームやリストが増えてもオフライン動作は予測可能になります。

バックグラウンド同期の信頼性:壊れる理由

デプロイ経路を選ぶ
AppMaster Cloud、あなたのクラウド、またはコードをエクスポートしてセルフホスティングへ。どの道でも選べます。
プラットフォームを確認

バックグラウンド同期が失敗しやすいのはコードのせいというより端末側の事情が大きいです。AndroidとiOSはバッテリーやデータ、パフォーマンスを守るためにアプリのバックグラウンド活動を制限します。ユーザーが節電モードをオンにしたり、バックグラウンドデータを切ったり、強制終了したりすると、「5分ごとに同期する」という期待は「OSが都合のいいときに同期する」に変わります。

Androidでは仕事のスケジュール方法と端末メーカーの省電力ルールに依存します。安全な道はOS承認のスケジューラー(例えばWorkManagerと制約)を使うことですが、それでもブランドによっては画面オフやアイドル時にジョブを大幅に遅らせます。ほぼリアルタイム更新が必要なら、常時同期ではなく最終的整合性(eventual sync)を前提に再設計することが多いです。

ComposeとReact Nativeの違いはバックグラウンド作業がどこにあるかです。Composeアプリはネイティブコードでバックグラウンドタスクを走らせることが多く、スケジューリングやリトライのロジックがOSに近い場所にあります。React Nativeでも十分に動きますが、バックグラウンドタスクは追加のネイティブ設定やサードパーティモジュールに依存することが多く、タスク登録ミス、ヘッドレスタスクがOSに殺される、期待どおりにJSランタイムが起きない、などの落とし穴があります。

同期が動いていることを証明するには、製品機能として扱い計測してください。"実行されたか?" と "完了したか?" に答えるログを残します。同期ジョブがスケジュールされた時間、開始と終了、ネットワークと節電状態、キューにある項目、アップロード済み/失敗/再試行の件数(エラーコード付き)、最後に成功した同期からの経過時間、競合の結果などを追跡します。

簡単なテスト:携帯をポケットに入れて一晩置きます。朝になって複数端末で同期が成功していれば、かなりうまくいっています。

複雑なフォーム:検証、下書き、UXの細部

重い画面をテストする
まずは長いリストと複雑なフォームを作り、実データでパフォーマンスをチューニングします。
プロジェクトを開始

複雑なフォームはユーザーが違いを肌で感じる場所です。条件付きフィールド、マルチステップ、たくさんの検証があるフォームでは、小さな遅延やフォーカスの不具合が離脱につながります。

検証は予測可能な方が扱いやすいです。フィールドに触った後にのみエラーを出し、メッセージは短く、ルールは実際のワークフローに合わせましょう。条件付きフィールド(「配達が必要なら住所を聞く」など)はページがガタガタしないように表示されるべきです。マルチステップフォームは各ステップに明確なゴールを持たせ、戻る操作で入力が失われないようにします。

キーボードとフォーカスの挙動は静かな致命的要因です。ユーザーはNextボタンが自然な順序で次に移動すること、アクティブなフィールドが見えるように画面がスクロールすること、エラーメッセージがスクリーンリーダーでアクセス可能であることを期待します。小さな端末で片手操作を想定してテストしてください。そこに問題が出やすいです。

オフライン下書きは長いフォームでは必須です。実用的には意味のある変更時に保存し(すべてのキー入力で保存するのは避ける)、「最後に保存した」簡単なヒントを出し、部分的なデータを許容し、大きな画像は別扱いにして下書きを重くしないようにします。

例:条件付きセクションを含む40項目の検査フォーム。もし各キー入力で全ルールを検証すると入力が重く感じます。逆に提出時まで下書きを保存しないとバッテリー切れで作業が消えます。理想は素早いローカル保存、提出間近で検証を強めること、そして安定したフォーカスでキーボードがアクションボタンを隠さないことです。

長いリスト:滑らかなスクロールとメモリ管理

長いリストはユーザーが最初に問題を感じる場所です:スクロール、タップ、素早いフィルタリング。どちらの技術でも速くできますが、遅くなる理由が異なります。

Composeでは長いリストを LazyColumnLazyRow)で作ることが多いです。画面上にあるものだけを描画するのでメモリ効率は良いですが、各行の描画が重いとスタッタが出ます。アイテム内で重い処理をしない、広範囲の再コンポジションを誘発しないことが重要です。

React Nativeでは FlatListSectionList が仮想化のために設計されていますが、propsが変わって多くの行が再レンダーされるとJS側の負荷が増えます。画像、動的高さ、頻繁なフィルタ更新はJSスレッドの負担になり、フレーム落ちとして現れます。

リストの遅延を防ぐ習慣:安定したキーを使う、新しいオブジェクトやコールバックを毎回作らない、行の高さを予測可能にする、ページングを導入して読み込み中にスクロールが止まらないようにする、などです。

アプリを選ぶためのステップ・バイ・ステップ

生成されたソースを所有する
必要に応じて本番対応のGo、Vue3、KotlinやSwiftUIのソースを取得できます。
コードを生成

まず要件をフレームワーク用語ではなく平易な言葉で書きます。「暗所でバーコードをスキャンする」「注文に10枚の写真を添付する」「2日間通信なしで動く」「端末がロックされているときに静かに同期する」など、トレードオフが明確になります。

次にUIを磨く前にデータと同期ルールを固めます。何をローカルに置くか、何をキャッシュしていいか、何を暗号化するか、二重編集が起きたときにどうするかを決めてください。UIを先に作ると後でデータモデルを何度も作り直すはめになります。

その後、両方の選択肢で同じ小さなスライスを作って評価します:下書きと添付のある複雑なフォーム、検索と更新付きの長いリスト、機内モードでの基本的なオフラインフロー、アプリが殺された後に再開する同期処理。最後に実機でバックグラウンドの挙動をテストします:節電モード、バックグラウンドデータ制限、1時間のアイドルなど。多くの「自分の端末では動く」問題はここで見つかります。

ユーザーが実際に感じるものを計測してください:コールドスタート時間、スクロールの滑らかさ、クラッシュ率。完璧なベンチマークを追うより、繰り返せるシンプルなベースラインを持つ方が有益です。

よくある失敗と落とし穴

多くのチームは画面やアニメーションに注力して始めますが、痛い問題は後から出ることが多いです:オフライン挙動、バックグラウンド仕事の制限、ユーザーが期待する状態と一致しない状態管理。

よくある罠は、バックグラウンド同期が常に動くものと仮定することです。AndroidもiOSも省電力やデータ節約のために作業を遅延させます。即時アップロードを前提に設計すると「更新がない」報告を受けることになりますが、実際はOSのスケジューリングが原因です。

別の罠はUIを先に作り、後からデータモデルを追いつかせようとすることです。オフライン競合はローンチ後に直すのが難しいので、早めに「同じレコードが二度編集されたらどうするか」「アップロードされていないものをユーザーが削除したらどうなるか」を決めてください。

フォームは状態を名前付けして分けないと静かに壊れます。ユーザーが編集中なのか下書きなのかローカル保存済みなのか既に同期済みなのかを示さないと、重複送信、メモの消失、あるいは不適切なタイミングでの検証が起きます。

注意すべきパターン:

  • バックグラウンド作業がタイマーで動くと仮定してしまう(OSはベストエフォート)。
  • オフラインを単なるトグルだと扱い、データと競合モデルをコアに据えない。
  • 1つのフォームが下書き・保存済み・同期済みの3つを曖昧に扱う。
  • 速い端末と安定Wi‑Fiだけでテストして、本番で遅いリストやアップロード失敗に驚く。
  • 多くのサードパーティプラグインを入れたら、そのうち1つがメンテされておらずエッジケースで壊れる。

現実チェック:ある営業が電波のない地下で注文を作り、2回編集して外に出たとします。アプリがどちらのバージョンを同期するか説明できない、あるいはバッテリー制限で同期が止まると、ユーザーはネットワークではなくアプリを責めます。

コミットする前の簡単チェックリスト

同期ロジックをマップする
ドラッグ&ドロップで下書き、キュー、リトライ、競合ルールを組み立てます。
始めましょう

スタックを決める前に小さな「現実的スライス」を作ってスコアを付けましょう。一つでも失敗するとあとで数週間の手直しになります。

まずオフラインで完了できるかをチェック:ネットワークなしで主要な上位3タスクを終えられるか、空状態や重複項目で混乱しないか。次に同期の負荷テスト:断続的なWi‑Fiでのリトライとバックオフ、アップロード中にアプリが殺された場合、ユーザーに見せる明確な状態(「端末に保存」「送信済み」など)。長い条件付きフォームの検証:下書きが強制終了後やクラッシュ後に正確に復元されるか。数千行のリストでフィルタとインプレース更新を強制し、フレーム落ちやメモリスパイクを監視する。最後に権限や制限下でデバイス機能を試す:使用中のみの権限、節電モード、バックグラウンドデータ制限に対するフェールバック。

実用的なヒント:このテストは各アプローチ2〜3日で時間を区切って実行してください。もしその期間で「オフライン+同期+長いリスト+複雑なフォーム」のスライスが堅牢に感じられなければ、継続的な問題を覚悟してください。

例:オフライン注文を扱うフィールドセールスアプリ

リスクスライスをプロトタイプする
小さなオフラインフローを作って実機で挙動を確認しましょう。
AppMasterを試す

フィールドセールスが小売店に売りに行くシナリオを想像してください。アプリはオフライン注文、棚とレシートの写真、巨大な商品カタログ、そして本社への日次同期を必要とします。

朝:営業は電波が弱い駐車場でアプリを開きます。1万点のカタログを検索し、素早く商品を追加し、顧客詳細と長い注文フォームを行き来します。ここでUIの摩擦が露呈します。商品リストが再レンダーしすぎるとスクロールが止まります。フォームがフォーカスを失う、ドロップダウンがリセットされる、写真のためにバックグラウンドに行ったときに下書きが消えると、即座に不満になります。

昼:接続が数時間切れます。営業は5件の注文を作り、それぞれに割引、メモ、写真を添付します。オフラインモードは単に「ローカルに保存する」以上です。価格表が変わったときの競合ルール、明確なステータス(Saved, Pending Sync, Synced)、安全な下書き(一時停止や写真撮影、アプリ再起動に耐える)が必要です。

夕方:営業が帰ってきて電波が戻ります。このチームにとって「十分に信頼できる」同期とは、ネットワーク復帰後数分以内に注文が自動でアップロードされ、失敗したアップロードは重複なく再試行され、写真はキューに入り圧縮されて同期を停滞させない、そして「今すぐ同期」を押すと結果が見えることです。

ここで判断がつくことが多いです:長いリスト、カメラ+バックグラウンド、OS管理のバックグラウンド作業下でどれだけネイティブな振る舞いが必要か。リスクのある部分を先にプロトタイプしましょう:巨大な商品リスト、下書き付きの複雑な注文フォーム、ネットワーク切断後に再試行するオフラインキュー。

次のステップ:小さなビルドで選択を検証する

迷っているなら短いスパイクを回してください。目標はアプリを完成させることではなく、最初の本当の制約を見つけることです。

簡単な計画を使います:妥協できない1つのデバイス機能(例:バーコードスキャン+写真)、1つのオフラインワークフロー(作成→編集→下書き保存→端末再起動→再開→提出)、1つの同期ジョブ(オフラインでアクションをキューに入れ、断続的なネットワークで再試行し、サーバーが拒否した場合を処理し、明確なエラー状態を表示)。

ローンチ前に本番での失敗をどう検出するか決めてください。同期試行を理由コード(ネットワークなし、認証期限切れ、競合、サーバーエラー)付きでログに残し、サポートが推測せずに問題を診断できるように小さな「同期ステータス」画面を用意します。

もしモバイルアプリと同時にバックエンドや管理UIも必要なら、AppMaster (appmaster.io) はビジネスアプリのベースとして有用です:本番対応のバックエンド、Web、ネイティブモバイルコードを生成して、特定のモバイルフレームワークに大きくコミットする前にデータモデルとワークフローを早く検証できます。

よくある質問

重いデバイス機能にはどちらが向いていますか:Jetpack ComposeそれともReact Native?

Jetpack ComposeはAndroidのネイティブAPIを直接呼び出すので、ベンダー提供のSDKや特殊ハードウェアのサポートが必要な場合は通常はこちらが安全な選択です。React Nativeは一般的なデバイス機能でコード共有を重視する場合に有効ですが、端の部分でネイティブ作業が必要になると想定しておくべきです。

権限やハードウェアアクセスは両者でどう違いますか?

Composeでは標準的なAndroidの権限フローとAPIを使うので、失敗時はネイティブのログで原因が追いやすいです。React Nativeではネイティブモジュール経由でアクセスするため、問題が出たときはJavaScript側とプラットフォーム固有のモジュール両方をデバッグする必要があります。

オフラインモードのデータ保存はどうすればいいですか?

信頼できる初期設定は、重要なユーザー生成データはローカルDB、設定類は小さなキー・バリュー領域、写真やPDFなどの大きな添付はファイルとして扱うことです。スタックによって使うライブラリは変わりますが、構造化データはデータベースで扱うという考え方が重要です。

オフラインで編集されたときの同期競合はどう扱えばいいですか?

まず方針を決めましょう:ローカルでの変更は即座に書き込み、UIに反映し、可能になったらサーバーに同期します。競合戦略も事前に決めておくと良いです。単純さを優先するなら last-write-wins、追記型フィールドならマージ、正確さが重要ならユーザーにレビューさせるなど用途に応じて選びます。

バックグラウンド同期は実際どれくらい信頼できますか?

現実にはバックグラウンド同期はベストエフォートで動くものと考えてください。AndroidやiOSはバッテリーやデータを保護するために作業を遅延・停止します。ユーザーに「端末上で保存済み」「保留中」といった明示的な状態を見せ、リトライやバックオフを設計の中心に据えましょう。

ComposeとReact Nativeのどちらがバックグラウンド処理に向いていますか?

Composeアプリはネイティブ側でスケジューラーやバックグラウンドロジックを実装しやすく、Androidでは驚きが少ない傾向があります。React Nativeでも実現は可能ですが、追加のネイティブ設定やモジュールに依存することが多く、デバイスや省電力設定でのテストがより重要になります。

ユーザーはどこでパフォーマンスの違いを最も感じますか?

ユーザーが最も違いを感じるのは、コールドスタート、画面遷移、スクロールの滑らかさ、そしてアプリが忙しいときの入力の“もたつき”です。ComposeはJavaScriptランタイムが不要なのでAndroid上でのパフォーマンス調整が比較的シンプルです。React Nativeは高速になり得ますが、JSスレッドをブロックすると影響が出やすいです。

長い一覧をどちらのフレームワークでも滑らかに保つにはどうすればいいですか?

各行を描画するコストを下げ、広範囲の再レンダーを引き起こさないようにし、ページングで読み込みがスクロールをブロックしない設計にすることです。実データ量とミドルレンジ端末でのテストも必須です。LazyColumnFlatList といった仕組みを正しく使いましょう。

複雑なオフラインフォームと下書きにはどう対処すべきですか?

下書きは必須と考えてください。意味のあるタイミングで自動保存し(すべての打鍵で保存するのは避ける)、アプリが殺された後でも復帰できるようにします。検証は入力後に表示するなど予測可能にし、提出時に厳密なチェックを強めると入力中のもたつきを避けられます。

ComposeとReact Nativeのどちらにするか決める実用的な方法は?

リスクの高い機能を含む小さな“スライス”を両方で作って比較するのが現実的です。最も厳しいリスト、添付と下書きを含む複雑なフォーム、アプリ再起動に耐えるオフライン→同期フローを実装して評価しましょう。バックエンドや管理UIも早めに検証するなら、AppMaster (appmaster.io) がプロトタイプを速く回す助けになります。

始めやすい
何かを作成する 素晴らしい

無料プランで AppMaster を試してみてください。
準備が整ったら、適切なサブスクリプションを選択できます。

始める