アプリ内部ストレージとは、デバイス上の専用スペースで、分離ストレージを介して特定のアプリケーションのみがアクセスできます。Android Developers, 2026によると、各アプリケーションは独自のサンドボックスディレクトリを取得し、他のアプリケーションは直接アクセスできません。このアプローチにより、データを不正な読み取りから保護し、マルチタスク環境での安定した動作を確保します。
重要なポイント
Context.getFilesDir()、getCacheDir()、getDataDir()を提供NSDocumentDirectoryとNSCachesDirectoryを使用アプリ内部ストレージとは、オペレーティングシステムがインストール時に各アプリケーションに割り当てる分離ディレクトリです。他のアプリケーションやユーザーは標準ファイルマネージャーを介してこのディレクトリにアクセスできません。システムは、アプリケーションがアンインストールされると、このディレクトリ内のすべてのデータが完全に削除されることを保証します。このアプローチは、モバイルオペレーティングシステムのセキュリティモデルの基盤を形成し、プログラム間の機密情報の漏洩を防止します。
外部ストレージ(SDカード)とは異なり、内部ストレージは常に利用可能で、メディアの存在確認は不要です。最近のデバイスのNANDフラッシュメモリの読み取り・書き込み速度は、シーケンシャル読み取りで800~900 MB/s、シーケンシャル書き込みで200~300 MB/sに達し、SATA SSDに匹敵します。割り当てられる領域のサイズはデバイスの総容量とメーカーのポリシーに依存します。64 GBのフラッシュメモリを搭載したデバイスでは、アプリは必要に応じて拡張可能な16~64 MBの初期スペースを受け取ります。
内部ストレージのアーキテクチャはAndroidとiOSで異なります。Androidでは、各アプリケーションが/data/data/<package_name>/ディレクトリを受け取り、その中にシステムがfiles/、cache/、databases/のサブディレクトリを作成します。iOSでは、アプリケーションはDocuments/、Library/、tmp/ディレクトリを持つサンドボックスコンテナ内で動作し、各ディレクトリには独自の目的とバックアップポリシーがあります。
開発者はアプリ内部ストレージにデータを保存するいくつかの方法を利用できます。各方法は特定のタスクを解決し、特定のデータタイプに適しています。適切な方法の選択は、アプリケーションのパフォーマンス、開発の容易さ、ユーザーデータのセキュリティに直接影響します。
最も低レベルな方法は、ファイルディレクトリへの直接ファイル書き込みです。アプリはサンドボックス内に任意のファイルやディレクトリを作成できます。この方法は、メディアファイル、ユーザードキュメント、構造化組織を必要としないバイナリデータの保存に適しています。Androidでは、Context.getFilesDir()呼び出しによりディレクトリにアクセスし、アプリのファイルディレクトリの絶対パスを返します。iOSでは、NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)関数が同様の目的を果たします。
キーと値のペアを保存するために、AndroidはSharedPreferencesと、Kotlinコルーチンとprotobufプロトコルに基づくより近代的なDataStoreを提供します。SharedPreferencesは/data/data/<package>/shared_prefs/ディレクトリ内のXMLファイルにデータを保存します。シンプルさにもかかわらず、SharedPreferencesには欠点があります。同期書き込みはUIスレッドで遅延を引き起こす可能性があり、タイプセーフティの欠如はエラーのリスクを高めます。DataStoreはFlowベースの非同期APIとprotobufスキーマによる完全な型サポートを提供することでこれらの問題を解決します。
リレーショナル接続を持つ構造化データには、SQLiteまたはRoomラッパーが最適な選択肢です。データベースはdatabases/ディレクトリ内の単一ファイルに保存され、完全なSQL構文をサポートします。Roomは公式のJetpackライブラリで、タイプセーフなAPI、自動スキーマ移行、コルーチンサポートを提供します。適切なインデックス設定により、データベースサイズはパフォーマンスの大幅な低下なしに数ギガバイトに達する可能性があります。モバイルデバイスのSQLiteは、最新のフラッグシッププロセッサで毎秒50,000回の書き込み操作を処理します。
認証トークンや暗号化キーなどの機密データを保存するために、AndroidはEncryptedSharedPreferencesを提供します。標準のSharedPreferencesをラップするこの機能は、AES256-GCM-Noneを使用してキーと値を自動的に暗号化します。暗号化はディスクへの書き込み前にファイルレベルで実行されるため、デバイスへの物理的アクセスがあった場合でも、攻撃者は内容を読み取れません。EncryptedSharedPreferencesはAndroidX Securityライブラリの一部であり、ファイル全体を暗号化するEncryptedFileも含まれています。
Android SDKはContextクラスを通じて内部ストレージを操作するための一連のメソッドを提供します。各メソッドはアプリのサンドボックス内の特定のシステムディレクトリへのパスを返します。Kotlinの例を使って基本的なファイル書き込み・読み取り操作を見てみましょう。
内部ファイルディレクトリへのパスを取得する主なメソッドはcontext.filesDirです。これは/data/data/<package>/files/ディレクトリを指すFileオブジェクトを返します。最初のアクセス時に、システムは必要なすべての親ディレクトリを自動的に作成します。内部ストレージのファイルサイズに明示的な制限はありませんが、データ総量は/dataパーティションの利用可能なスペースを超えてはなりません。これは通常、フラッシュメモリ総容量の60~80%です。
val context = getApplicationContext()
val file = File(context.filesDir, "notes.txt")
file.writeText("ノートの内容")
val content = file.readText()
println("読みました:$content")
writeTextおよびreadTextメソッドはKotlin標準ライブラリの拡張関数です。これらはストリームのオープンとクローズを自動的に管理し、メモリリークを防止します。バイナリデータには、エンコーディング不要でByteArray配列を扱うwriteBytesとreadBytesを使用します。大きなファイルを扱う場合は、バッファリングストリームの使用をお勧めします。テキストにはBufferedReaderとBufferedWriter、バイナリデータにはBufferedInputStreamとBufferedOutputStreamを使用します。
ファイルを階層的に整理するには、filesDir内にサブディレクトリを作成します。これにより、画像、ドキュメント、エクスポートファイルなど、タイプ別にデータを構造化できます。mkdirs()メソッドは、ネストされたディレクトリを含むパス内の不足しているすべてのディレクトリを作成します。作成操作が成功したことを確認してください。メソッドは新しいディレクトリが作成された場合のみtrueを返します。作成の失敗は、ほとんどの場合/dataパーティションのスペース不足またはファイルシステムのinode枯渇に関連しています。
val imagesDir = File(context.filesDir, "images")
if (imagesDir.mkdirs()) {
println("ディレクトリが作成されました")
}
val imageFile = File(imagesDir, "photo.jpg")
imageFile.writeBytes(byteArray)
大きなファイルを書き込む前に利用可能スペースを確認するには、File.getFreeSpace()またはFile.getUsableSpace()を使用します。後者のメソッドはセキュリティクォータを考慮して現在のアプリケーションが利用可能なバイト数を返します。マルチユーザーデバイスのコンテキストではより正確です。利用可能なスペースが予想ファイルサイズより小さい場合は、ユーザーにメッセージを表示し、デバイス設定でスペースを解放するよう提案します。
iOSでは、各アプリケーションは分離されたサンドボックスコンテナ内で動作します。システムは特別なentitlementなしにその境界を越えるAPIを提供しません。ファイルシステムを操作するための主要ツールはFoundationフレームワークのFileManagerクラスです。サンドボックスコンテナには複数の標準ディレクトリが含まれ、それぞれに独自のバックアップポリシーがあります。
Documentsディレクトリは、アプリケーションの起動間で保持され、バックアップから復元されるべきユーザーデータを対象としています。iOSは自動的にこのディレクトリをiCloudおよびiTunesバックアップに含めます。urls(for:in:)メソッドは要求されたディレクトリのURL配列を返します。配列の最初の要素がプライマリです。
let fm = FileManager.default
let docs = fm.urls(
for: .documentDirectory,
in: .userDomainMask
).first!
let fileURL = docs.appendingPathComponent("data.plist")
try data.write(to: fileURL)
FileManagerはファイル操作の完全なセット(作成、コピー、移動、削除、名前変更)をサポートします。各操作はエラーをスローする可能性があるため、すべての呼び出しをdo-catch構文でラップする必要があります。ファイル削除には特に注意してください。操作は元に戻せず、removeItem(at:)後のデータ復元は事前バックアップなしでは不可能です。
サンドボックスコンテナ内のすべてのデータをiCloudバックアップに含めるべきではありません。たとえば、ダウンロードしたキャッシュ画像や一時的な処理ファイルは復元する必要がなく、次回使用時に再作成されます。ディレクトリやファイルをバックアップから除外するには、isExcludedFromBackup属性をtrueに設定します。Appleは、リモートで復元可能なデータを常にバックアップから除外することを推奨しています。これにより、iCloudストレージの使用量を最小限に抑え、復元時間を短縮できます。
var cacheURL = fm.urls(
for: .cachesDirectory,
in: .userDomainMask
).first!
cacheURL.hasExcludedFromBackupKey = true
var values = URLResourceValues()
values.isExcludedFromBackup = true
try cacheURL.setResourceValues(values)
モバイルデバイスの各ストレージタイプには、独自の目的と使用ルールがあります。これらの違いを理解することで、開発者は各データタイプに適切な場所を選択できます。以下は、アプリケーションで利用可能な3つの主要ストレージタイプの比較です。
| 特性 | Internal Storage | キャッシュディレクトリ | External Storage |
|---|---|---|---|
| 他のアプリからの可視性 | 非表示 | 非表示 | アクセス可能 |
| アプリ削除時の消去 | 完全 | 完全 | 場所による |
| バックアップ | Android — なし、iOS — あり (Documents) | なし | 同期時のみ |
| メディアなしでの可用性 | 常時 | 常時 | SDカードが必要 |
| データ損失リスク | 最小 | 高い | 中程度 |
| 推奨ファイルサイズ | 最大100 MB | 最大50 MB | 任意 |
内部ストレージは、他のプログラムからアクセスされるべきではないアプリ設定、データベースファイル、ユーザードキュメントの保存に最適です。キャッシュディレクトリは、ダウンロードした画像、APIレスポンス、中間処理データなど、次回使用時に再作成可能な一時ファイルを対象としています。外部ストレージは、大きなメディアファイル(写真、ビデオ、音楽)や、ユーザーが他のアプリケーションと共有アクセスを介して共有したいデータに最も適しています。
ストレージタイプの選択は、Google PlayおよびApp Storeでのアプリ評価にも影響します。クリーンアップなしで内部ストレージに大量のデータを保存するアプリは否定的なレビューを受けます。ユーザーはスペース不足を訴えます。App Annieの調査によると、クリーンアップオプションなしでデバイスの内部ストレージの500 MB以上を占有するアプリは、62%のユーザーに削除されます。
アプリ内部ストレージの適切な管理により、パフォーマンス、セキュリティ、ユーザーエクスペリエンスが向上します。以下の推奨事項は、公式のAndroidおよびiOSドキュメント、ならびに数百万インストール数のアプリケーション開発の実践的経験に基づいています。
エッジケースのテストに特に注意を払う必要があります。内部ストレージが満杯の場合、書き込み操作が予期せず中断された場合(アプリクラッシュ、着信)、iOSバックアップから復元する場合のアプリケーションの動作を確認します。これらのシナリオのそれぞれで、データは一貫性を保つか、最後の安定状態に復元される必要があります。トランザクションファイルを使用します。一時ファイルにデータを書き込み、それをアトミックにターゲットに名前変更します。これにより、書き込み失敗時の破損データの読み取りを防止します。
ユーザーコントロールを忘れないでください。アプリ設定に一時データをクリアするオプションと、占有されている内部ストレージ容量の表示を提供します。Google Play Consoleによると、この機能を持つアプリは「パフォーマンス」カテゴリで18%多く肯定的なレビューを獲得しています。
よくある質問
アプリの内部ストレージの全データが完全に削除されます。オペレーティングシステムは、データベース、設定、一時ファイルを含む残留ファイルがないことを保証します。外部ストレージのデータは残る可能性があります。
デバイスへのルートアクセスなしでは、他のアプリは別のアプリのInternal Storageからファイルを読み取れません。Androidではスーパーユーザー権限が必要であり、iOSではサンドボックスを介してカーネルレベルで分離が適用されます。
明示的な制限はありませんが、総量は/dataパーティションの利用可能スペースによって制約されます。アプリあたり100 MBを超えないことが推奨されます。より大きな容量は外部ストレージまたはクラウドに配置するのが適切です。
filesDirは永続的なアプリデータ用で、システムは必要な場合を除き削除しません。cacheDirは一時ファイル用で、メモリ不足時にシステムが削除する可能性があります。システムはcacheDirの永続性を保証しません。
Internal StorageからSDカードへの直接コピーはセキュリティポリシーで禁止されています。ユーザーの同意を得て共有ストレージにデータのコピーを作成するには、Android 10+のMediaStore APIまたはSAF(Storage Access Framework)を使用します。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。