Scoped Storageとは — ファイル管理とストレージアクセス

著者: IT Sectr 公開日: 2026-07-10 読了時間: 10 分

Scoped Storage は、Android 10(API 29)で導入されたファイルシステムアクセスモデルで、デバイスの共有ストレージへのアプリの無制限なアクセスを制限します。Google Android Developer Documentation(2024)によると、Scoped Storage は READ_EXTERNAL_STORAGE と WRITE_EXTERNAL_STORAGE の古い権限モデルを置き換え、アプリに分離されたサンドボックス内の自ファイルと、MediaStore を介した共有コレクションへのアクセスのみを許可します。このモデルはユーザーデータのプライバシーを強化し、アプリによる不正な情報収集を防止します。

重要なポイント

  • Scoped Storage は Android 10+ のファイルアクセスモデルで、共有ストレージの無制限な読み取りを制限します。
  • アプリには追加の権限なしで完全アクセスできる 独自のサンドボックス があります。
  • 共有ファイル(写真、動画、音声)へのアクセスには MediaStore API を使用します。
  • 任意のディレクトリやファイルへのアクセスには Storage Access Framework(SAF)を使用します。
  • READ_EXTERNAL_STORAGE と WRITE_EXTERNAL_STORAGE の権限はほとんどのシナリオで効力を失いました。

AndroidにおけるScoped Storageとは

Scoped Storage は Android 10(API 29)におけるアーキテクチャ上の変更であり、デバイスのファイルシステムへのアプリのアクセス方法を根本的に変えます。共有ストレージへの完全なアクセスの代わりに、アプリは自身のサンドボックスと、MediaStore を介した特定のメディアコレクションにのみアクセスできます。

Scoped Storage 以前は、READ_EXTERNAL_STORAGE 権限を持つアプリは共有ストレージ内のすべてのファイル(写真、ドキュメント、ダウンロード、他のアプリのデータ)を読み取ることができました。これはプライバシーリスクを生み出していました。信頼できないアプリがユーザーの知らないうちに情報を収集する可能性があったのです。

Google I/O 2019 によると、Android ユーザーの65%以上がアプリ選択時にデータプライバシーを重要な要素と考えています。Scoped Storage はこの需要に直接応えるものです。各アプリは分離された環境で動作し、ユーザーの明示的な同意がある場合にのみ他のアプリのデータにアクセスします。

重要:Android 10 の Scoped Storage はオプションでした(アプリは requestLegacyExternalStorage で無効化できました)。Android 11(API 30)以降、Scoped Storage は target SDK に関係なくすべてのアプリに必須となりました。

Scoped Storageが必要とされた理由

Google は、古いファイルアクセスモデルの3つの根本的な問題(プライバシー、権限管理、残存ファイルのクリーンアップ)を解決するために Scoped Storage を導入しました。

プライバシーの問題

古いモデルでは、READ_EXTERNAL_STORAGE を持つアプリが共有ストレージ全体をスキャンし、ユーザーファイルのメタデータ(写真の地理座標、ドキュメント名、ディレクトリ構造)を収集できました。Scoped Storage はこの可能性を排除します。権限があっても、アプリは自身が作成したファイルと、SAF を通じてユーザーが明示的に選択したファイルのみを表示します。

アプリアンインストール時のクリーンアップの問題

Scoped Storage 以前は、アプリはアンインストール後も共有ストレージにファイルを残すことができました。時間が経つにつれて、ジャンクディレクトリが蓄積されていました。Scoped Storage はこれを解決します。アプリのサンドボックス内のすべてのファイルはアプリとともに削除され、MediaStore 経由のファイルには所有者が存在し、システムがクリーンアップできます。

側面古いモデル(Legacy)Scoped Storage
共有ストレージへのアクセス完全(権限あり)自身のサンドボックスのみ
メディアファイル直接ファイルパスMediaStore URI経由
他のアプリのファイル常にアクセス可能SAF経由のみ
アンインストール時のクリーンアップファイルが残るサンドボックスが削除される
ユーザーの制御最小限ファイルごとに明示的な同意

Scoped Storage は Android における Google の全体的な プライバシー 戦略の一部であり、「使用中のみ」の権限、カメラとマイクのアクセスインジケーター、プライバシーダッシュボードも含まれます。

アプリのプライベートディレクトリ

Android アプリは、Context.getFilesDir() と context.getCacheDir() を介してアクセスできる独自のプライベートディレクトリを取得します。外部(共有)プライベートディレクトリにアクセスするには、Context.getExternalFilesDir() を使用します。

Scoped Storage では、アプリは権限なしで外部プライベートディレクトリに 完全アクセス できます。これは、他のアプリがアクセスできないようにするファイルや、アプリが自身の使用のために作成するファイルを保存するための主要な場所です。

kotlin
import android.os.Environment
import java.io.File
import java.io.IOException

class FileManager {

    fun saveToAppStorage(context: Context, fileName: String, data: ByteArray) {
        val appDir = context.getExternalFilesDir(null)
            ?: return
        val file = File(appDir, fileName)
        file.writeBytes(data)
    }

    fun readFromAppStorage(context: Context, fileName: String): ByteArray? {
        val appDir = context.getExternalFilesDir(null)
            ?: return null
        val file = File(appDir, fileName)
        return file.takeIf { it.exists() }?.readBytes()
    }
}

外部プライベートディレクトリへのパス: /storage/emulated/0/Android/data/{packageName}/files/。Android 11 以降、直接ファイルパスは利用できません(API経由のみ)。これは Scoped Storage のもう一つのセキュリティ強化です。

MediaStore — メディアファイルへのアクセス

MediaStore は、Scoped Storage において共有メディアファイル(画像、動画、音声)にアクセスするための主要なAPIです。アプリはファイルパスではなく、MediaStore プロバイダーが提供する content URI で動作します。

MediaStore は ImagesVideoAudio の3つの主要コレクションに分かれています。各コレクションは ContentResolver を介した CRUD 操作をサポートします。Images コレクションに新しいファイルを挿入するには MediaStore.Images.Media を使用し、既存ファイルをクエリするには対応する URI で query を使用します。

kotlin
import android.content.ContentValues
import android.provider.MediaStore
import android.os.Environment
import java.io.OutputStream

fun saveImageToGallery(context: Context, bitmap: Bitmap, title: String) {
    val values = ContentValues().apply {
        put(MediaStore.Images.Media.DISPLAY_NAME, "$title.jpg")
        put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
        put(
            MediaStore.Images.Media.RELATIVE_PATH,
            "${Environment.DIRECTORY_PICTURES}/MyApp"
        )
    }

    val uri = context.contentResolver
        .insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
        ?: return

    context.contentResolver.openOutputStream(uri)?.use { output: OutputStream ->
        bitmap.compress(
            Bitmap.CompressFormat.JPEG,
            95,
            output
        )
    }
}

重要:MediaStore は任意のファイルパスをサポートしません — content URI のみです。MediaStore.Files.getContentUri() を介して URI からファイルパスを取得しようとしても、Android 11+ ではファイルへの直接パスは得られません。代わりに、ContentResolver.openInputStream() と openOutputStream() を使用してコンテンツを操作してください。

Storage Access Framework(SAF)

Storage Access Framework(SAF)は、アプリのサンドボックス外の任意のファイルやディレクトリにアクセスするためのAPIです。SAF はユーザーにファイルまたはディレクトリ選択インターフェースを提供し、アプリは一時アクセス権付きの content URI を取得します。

SAF は MediaStore がカバーしないシナリオで使用されます。任意のドキュメント(PDF、ZIP、APK)の操作、SD カード上のディレクトリへのアクセス、他のアプリからのファイルのインポートとエクスポート。ユーザーはシステムファイルピッカーを介して明示的にファイルを選択します。これによりアクセスへの同意が保証されます。

kotlin
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.provider.DocumentsContract

const val REQUEST_CODE_PICK_DIR = 1001

fun pickDirectory(activity: Activity) {
    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
    activity.startActivityForResult(intent, REQUEST_CODE_PICK_DIR)
}

fun handlePickResult(requestCode: Int, data: Intent?) {
    if (requestCode == REQUEST_CODE_PICK_DIR && data != null) {
        val treeUri: Uri = data.data ?: return

        // Take persistent URI permission
        val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
                       Intent.FLAG_GRANT_WRITE_URI_PERMISSION
        contentResolver.takePersistableUriPermission(treeUri, flags)
    }
}

SAF は、takePersistableUriPermission を使用すると、デバイス再起動までユーザーが選択したディレクトリへのアクセスをアプリに提供します。これは Android 11+ の共有ストレージで任意のファイルへのアクセスを取得する唯一の方法です。

Scoped Storageへのアプリ移行

既存の Android アプリを Scoped Storage に移行するには、いくつかの主要領域での変更が必要です。Google は Android 11+ でのテストを含む段階的アプローチを推奨しています。

ファイルパスを content URI に置き換える

共有ストレージ内のすべての直接的な File 操作を ContentResolver.openInputStream / openOutputStream に置き換えます。アプリのサンドボックス(getExternalFilesDir)内のファイルについては、ファイルパスは引き続き機能します。

AndroidManifest を更新する

特定のシナリオ(ファイル管理など — これには特別な権限 MANAGE_EXTERNAL_STORAGE があります)で必要ない場合は、READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE を削除します。

シナリオ古いアプローチScoped Storageのアプローチ
写真の保存File(path).writeBytes()MediaStore + ContentResolver
PDFの読み取りFile(path).inputStream()SAF ACTION_OPEN_DOCUMENT
自ファイルEnvironment.getExternalStorageDirectory()context.getExternalFilesDir()
キャッシュFile(cacheDir).writeBytes()context.cacheDir(変更なし)

移行後、build.gradle で targetSdk = 30 以上に設定して Android 11+(API 30) でアプリをテストします。共有ストレージ内のすべてのファイル操作が、直接ファイルパスではなく MediaStore または SAF を介して機能することを確認してください。

kotlin
import android.os.Build

fun isScopedStorage(): Boolean {
    return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
}

fun compatReadFile(context: Context, uri: Uri): ByteArray? {
    return if (isScopedStorage()) {
        context.contentResolver
            .openInputStream(uri)?.readBytes()
    } else {
        File(uri.path ?: return null)
            .takeIf { it.exists() }
            ?.readBytes()
    }
}

Google Play Console(2024) によると、アクティブな Android デバイスの78%以上が Scoped Storage 対応の Android 10+ で動作しています。Google Play でアップデートを公開するには移行が必須です。新しいアプリは API 31+、アップデートは API 30+ をターゲットとする必要があります。

よくある質問

Android 11でScoped Storageを無効にできますか?

いいえ。Android 11(API 30)以降、Scoped Storage はすべてのアプリに必須です。Android 10 で利用可能だった requestLegacyExternalStorage フラグは API 30+ では機能しません。ファイルシステムへの広範なアクセスを取得する唯一の方法は MANAGE_EXTERNAL_STORAGE 権限ですが、これはファイルマネージャーとウイルス対策アプリ専用です。

MANAGE_EXTERNAL_STORAGEとは何ですか?どうやって取得しますか?

MANAGE_EXTERNAL_STORAGE は、完全なファイルシステムアクセスを必要とするアプリ(ファイルマネージャー、バックアップツール)向けの特別な権限です。権限をリクエストすると、ユーザーに警告付きのシステム画面が表示されます。Google Play で公開する際は、この権限の必要性に関する宣言フォームに記入する必要があります。

Scoped Storageでcontent URIからファイルパスを取得するには?

Android 11+ の Scoped Storage では、content URI から直接ファイルパスを取得できません。代わりに、読み取りには ContentResolver.openInputStream()、書き込みには openOutputStream() を使用します。サードパーティライブラリとの互換性のためにファイルパスが必要な場合は、getCacheDir() にファイルのコピーを作成してコピーを操作します。

Scoped StorageはFileProviderにどのように影響しますか?

FileProvider は変更なしで動作し続けます。アプリのサンドボックスから他のアプリへの content URI を介したファイルアクセスを提供するために使用されます。FileProvider は content URI レベルで動作し、直接ファイルパスではないため、Scoped Storage の影響を受けません。

デバイスがScoped Storageをサポートしているか確認するには?

Build.VERSION.SDK_INT を確認します。>= Build.VERSION_CODES.Q(29)の場合、デバイスは Scoped Storage をサポートしています。ただし、Android 10 では requestLegacyExternalStorage を介して Scoped Storage が無効化されている可能性があります。Android 11+ では、マニフェストフラグ requestLegacyExternalStorage が設定されているかどうかを確認します。設定されていない場合、Scoped Storage が有効です。

まとめ

  • Scoped Storage は Android 10+ のファイルアクセスモデルで、共有ストレージの無制限な読み取りを制限します。
  • アプリは追加の権限なしでプライベートサンドボックスに 完全アクセス できます。
  • 共有メディアファイルへのアクセスには content URI を使用した MediaStore API を使用します。
  • 任意のファイルやディレクトリへのアクセスには Storage Access Framework(SAF)を使用します。
  • Android 11 以降、Scoped Storage はすべてのアプリに 必須 です。
  • READ_EXTERNAL_STORAGE と WRITE_EXTERNAL_STORAGE の権限は Android 11+ のほとんどのシナリオで機能しません。
  • 移行時には、直接 ファイルパス を ContentResolver.openInputStream / openOutputStream に置き換えます。

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください