背景1枚
前章[Android フレームワーク シリーズ] 第 16 章 ストレージ アクセス フレームワーク (SAF)では主に Android 4.4 で導入されたストレージ アクセス フレームワーク (SAF) を分析しましたが、存储访问框架(SAF)
この章ではAndroid10(Q)
ストレージ関連を分析し、制限されたストレージ メソッドを理解します。
Google
ユーザーが自分のファイルをより詳細に制御し、ファイルの乱雑さを制限できるように、デバイスのストレージへのアクセス方法がAndroid Q
変更されました。App
1.1 Android Qより前
プログラムは、許可を取得している限りREAD_EXTERNAL_STORAGE
、外部ストレージのパブリック ディレクトリを自由に読み取ることができ、
プログラムが許可を取得している限りWRITE_EXTERNAL_STORAGE
、外部ストレージに書き込まれたパブリック ディレクトリ上に新しいファイルやフォルダを自由に作成することができます。
1.2 Android Q以降
そこで Google は、プログラムによる外部ストレージ内のパブリック ディレクトリの使用を制限する分区存储
ことを目的として、Android Q でこれを提案しました。パーティション化されたストレージは、内部ストレージのプライベート ディレクトリと外部ストレージのプライベート ディレクトリの両方に影響を与えません。
2 サンドボックスモード
2.1 Android Q では、アプリにレガシー ビューとフィルター ビューという 2 つのストレージ スペース モード ビューがあることが規定されています。
2.1.1 レガシービュー (互換モード)
Android Q
プロジェクト targetSdkVersion <= 28 の場合、AndroidQ デバイスではデフォルトで互換モードが使用され、保存方法は以前と同じで、完全なアクセス許可でApp
アクセスSdcard
が完了します。
2.1.2 フィルターされたビュー (サンドボックス モード)
プロジェクトの targetSdkVersion > 28 の場合、AndroidQ デバイスはディレクトリ ファイルにApp
直接アクセスすることしかできず、外部ファイルにアクセスする権限がありません。他のディレクトリへのアクセスは、、、または他のアプリを通じてのみ提供できます。App-specific
App-specific
MediaStore
SAF
ContentProvider
2.2 スコープドストレージはストレージスペースを 2 つの部分に分割します
2.2.1 SD カードのパブリック ディレクトリ: ダウンロード、ドキュメント、写真、DCIM、映画、音楽、着信音
- パブリックディレクトリ内のファイルは
App
アンインストール後に削除されません SAF
インターフェース経由MediaStore
でアクセス可能
2.2…2 data/data プライベート ディレクトリ (アプリ固有のディレクトリ)
- の場合
Filtered View App
、App-specific
ディレクトリには自分自身のみが直接アクセスできます App
アンインストールするとデータは消去されます。
2.3 互換性への影響
Scoped Storage
App访问存储方式
、App数据存放
、に大きな影響を与えますApp间数据共享
。
2.4 適応
具体的な適応については、公式ドキュメントを参照してください。
2.4.1 アプリ実行ビュー
システムは、以下を通じてアプリの実行モードを決定します。
App TargetSDK > 28
、デフォルトFiltered View(沙箱模式)
App TargetSDK <= 28
、READ_EXTERNAL_STORAGE または WRITE_EXTERNAL_STORAGE 権限を宣言します、デフォルトLegacy View(兼容模式)
- アプリケーションはAndroidManifest.xml を通じて requestLegacyExternalStorage を設定し、対応するメソッドを選択できます
:宣言されたREAD_EXTERNAL_STORAGE
、WRITE_EXTERNAL_STORAGE
許可 (宣言されていない場合は無視されます):
requestLegacyExternalStorage=true
を意味します。Legacy View(兼容模式)
requestLegacyExternalStorage=false
Filtered View(沙箱模式)
(注意:该方式Android11后失效了)
- システム アプリケーションは、システム権限を申請でき
android.permission.WRITE_MEDIA_STORAGE
、ストレージ領域全体の権限も持ち、すべてのファイルにアクセスできます。ただし、CTS テストでは、ユーザー操作がなく、表示されるアプリのみが適用できます。具体的な参考資料《Android Bootcamp 2019 - Privacy Overview.pdf》
。
- アプリは次の条件が満たされた場合に所有されます
: ① 声明INSTALL_PACKAGES
または动态申请INSTALL_PACKAGES
許可
② 所有権WRITE_EXTERNAL_STORAGE权限
③ アプリの所有権外置存储空间Read、Write权限
。
ただし、Environment.isExternalStorageLegacy
インターフェイスから判断すると、返されるのは必ずしもレガシー ビューであるとは限りません。
2.4.2 現在のアプリ実行モードを確認する
現在のアプリがどのモードで実行されているかを判断するには、この API を使用して次のことを判断できます。
Environment.isExternalStorageLegacy();
2.5 パブリックディレクトリの読み取りと書き込み
アプリが開始された後はFiltered View
、アプリ自体に直接アクセスすることしかできないApp-specific目录
ため、Android Q
パブリック ディレクトリにアクセスする 2 つの方法が提供されています。
2.5.1 MediaStore によって定義される Uri
MediaStore
アクセスには以下の種類が用意されておりUri
、該当する Uri データを検索することでアクセスの目的を達成します。
以下のそれぞれのタイプはUri
、 、Internal
、External
、の 3 つのタイプに分類されます可移动存储
。
オーディオ
- 内部: MediaStore.Audio.Media.INTERNAL_CONTENT_URI
content://media/internal/audio/media。- 外部: MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
content://media/external/audio/media。- リムーバブル ストレージ: MediaStore.Audio.Media.getContentUri
content://media//audio/media。
ビデオ
- 内部: MediaStore.Video.Media.INTERNAL_CONTENT_URI
content://media/internal/video/media。- 外部: MediaStore.Video.Media.EXTERNAL_CONTENT_URI
content://media/external/video/media。- リムーバブル ストレージ: MediaStore.Video.Media.getContentUri
content://media//video/media。
画像
- 内部: MediaStore.Images.Media.INTERNAL_CONTENT_URI
content://media/internal/images/media。- 外部: MediaStore.Images.Media.EXTERNAL_CONTENT_URI
content://media/external/images/media。- リムーバブル ストレージ: MediaStore.Images.Media.getContentUri
content://media//images/media。
ファイル
- メディアストア。Files.Media.getContentUri
content://media//file。
ダウンロード
- 内部: MediaStore.Downloads.INTERNAL_CONTENT_URI
content://media/internal/downloads。- 外部: MediaStore.Downloads.EXTERNAL_CONTENT_URI
content://media/external/downloads。- リムーバブル ストレージ: MediaStore.Downloads.getContentUri
content://media//downloads。
2.5.1.1 すべてのボリュームの取得
前に説明した Uri の場合、getContentUri
すべての Uri を取得する方法は次の方法で実行できます。
for(String volume:MediaStore.getExternalVolumeNames(this)){
MediaStore.Audio.Media.getContentUri(volume);
}
2.5.1.2 Uriとパブリックディレクトリの関係
MediaProvider
アプリをパブリック ディレクトリ ファイルに保存するには、メソッド内の Uri によって決定されます。相対パスはContentResolver insert
次の表に示されており、完全なファイルはcontent://media//<Uri path> です。<Uri路径>
2.5.1.3 権限
MediaStore
ユーザーはさまざまな方法でUri
、追加、削除 (ファイル Uri を使用してファイルを削除できない場合は、SAF インターフェイスを使用する必要があります)、および変更を行うことができます。
アプリの対応する権限は次のとおりです。
2.5.1.4 クエリファイル
ContentResolver
異なる URI に基づいて異なるコンテンツをクエリすることにより、次のようになります。
try (Cursor c = getContentResolver()
.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, PROJECTION, null, null, null)) {
while (c.moveToNext()) {
Uri contentUri =
ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, c.getLong(0));
}
}
PS:MediaStore.Files
プロセス中はQuery
、写真、ビデオ、オーディオ ファイルのみが表示されます。
2.5.1.5 ファイルの読み込み
ContentResolver query
インターフェイスを介して、ファイルを見つけた後にそれを読み取る方法は次の方法で実行できます。
ContentResolver openFileDescriptor
インターフェイスを通じて、対応する開始メソッドを選択します。
たとえば、「r」は読み取りを意味し、「w」は書き込みを意味し、戻り値のParcelFileDescriptor
型は ですFD
。- インターフェース
Thumbnail
経由でContentResolver loadThumbnail
アクセス
サイズを渡すと、MediaProvider
指定されたサイズが返されますThumbnail
。 - ネイティブ コードがファイルにアクセスする
場合、ネイティブ コードがファイルにアクセスする必要がある場合は、次の方法を参照できます:
①openFileDescriptor
返すことによってParcelFileDescriptor
②ParcelFileDescriptor.detachFd()
読み取りによってFD
③レイヤー コードFD
に渡す④インターフェイスを介して閉じる責任が必要Native
App
close
FD
ParcelFileDescriptor parcelFd = resolver.openFileDescriptor(uri, file0penMode);
if (parcelFd != null) {
int fd = parcelFd.detachFd();
// Pass the integer value "fd" into your native code. Remember to call
// close(2) on the file descriptor when you're done using it.
}
2.5.1.6 新しいファイルの作成
新しいファイルをパブリック ディレクトリに保存する必要がある場合は、ContentResolver insert
別のインターフェイスを使用しUri
、別のディレクトリに保存することを選択する必要があります。
2.5.1.7 ファイルの変更
マルチメディア ファイルを変更する必要がある場合は、ContentResolver query
インターフェイスを通じて対応するファイルの URI を見つける必要があります。
新しいファイルを自分で作成したわけではない場合は、「2.5.1.3 権限」の説明に注意して適用する必要があります。適用しない場合は、ポップアップ ボックスが表示されWRITE_EXTERNAL_STORAGE权限
、ユーザーに選択が表示されます。次のインターフェイスを介して、変更する必要があるファイルを取得するか、次の手順を実行します。catch RecoverableSecurityException
FD
OutputStream
- getContentResolver().openOutputStream(contentUri) は、
対応するファイルの OutputStream を取得します。 - getContentResolver().openFile または getContentResolver().openFileDescriptor は、
openFile または openFileDescriptor を通じてファイルを開きます。書き込み許可を示す「w」をモードとして選択する必要があります。これらのインターフェイスは ParcelFileDescriptor を返します。
getContentResolver().openFileDescriptor(contentUri,“w”);
getContentResolver().openFile(contentUri,“w”,null);
2.5.1.8 ファイルの削除
ContentResolver インターフェイスを介してファイルを削除します。Uri はクエリの Uri です。
getContentResolver().delete(contentUri,null,null);
2.5.2 SAF インターフェース経由
2.5.2.1 SAF の概要
SAF
つまり、Storage Access Framework
別のファイルを選択してDocumentsProvider
ファイルを開いたり参照したりできる機能をユーザーに提供します。前の章を振り返ることができます
Android ではデフォルトで次のものが提供されますDocumentsProvider
:
MediaDocumentsProvider
、ExternalStorageProvider
、DownloadStorageProvider
。
それらの違いは次のとおりです。
この図には、次の 3 つの領域があります。
- メディアドキュメント提供
- ダウンロードストレージプロバイダー
- 外部ストレージプロバイダー
- サードパーティのドキュメントプロバイダー
2.5.2.2 使用方法
詳細については公式ドキュメントを参照してください
一般的な方法は次のとおりです。
- 単一のファイルを選択します
- ディレクトリ、
ファイル管理プログラム、およびクリーンアップ プログラムを選択します。この方法を使用すると、対応するディレクトリとサブディレクトリに対するすべての管理権限を取得できます。 - 新しいファイルを作成する
- 消去
DocumentsContract.deleteDocument(getContentResolver(),uri);
- 改訂
①OutputStreamを取得する
getContentResolver().openOutputStream(uri);
②書き込み可能なParcelFileDescriptorを取得する
getContentResolver().openFileDescriptor(contentUri,"w");
getContentResolver().openFile (contentUri,"w",null);
2.6 アプリ固有のディレクトリへのアクセス
アクセスには2 つの状況がありApp-specific
、1 つ目はアクセスApp自身App-specific目录
、2 つ目はアクセスです其他App目录文件
。
2.6.1 アプリ自身のアプリ固有のディレクトリ
Android Q、アプリが開始された場合Filtered View
、アプリは独自のディレクトリ内のファイルにのみ直接アクセスできます。
- Android Q では、 Environment.getExternalStorageDirectory および getExternalStoragePublicDirectoryインターフェイス
が廃止されました。アプリはフィルターされたビューであり、このディレクトリに直接アクセスできません。
ファイル (「/sdcard/」) を介したアプリへのアクセスはフィルターされたビューであり、このディレクトリに直接アクセスすることはできません。- アプリ固有のディレクトリを取得します
Media インターフェイスを取得します: getExternalMediaDirs
Cache インターフェイスを取得します: getExternalCacheDirs
Obb インターフェイスを取得します: getObbDirs
Data インターフェイスを取得します: getExternalFilesDirs
2.6.2 アプリ固有のディレクトリ内のマルチメディア ファイル
アプリ固有のディレクトリ内のマルチメディア ファイル:
- アプリ自身のアクセスは
App-specific
2.6.1 アプリ自身のディレクトリと同じです - 他のアプリからのアクセス
① デフォルトでは内部のマルチメディアファイルはスキャンされません スキャンが必要な場合はデータベースに追加する必要Media Scanner
がありますアクセス方法は2.5パブリックディレクトリの読み書きと同じです ②シェアリングでアプリを共有App-specific
MediaScannerConnection.scanFile
MediaProvider
ContentProvider
2.6.3 その他のアプリディレクトリファイル
アプリはフィルターされたビューです。他のアプリは現在のアプリのプライベート ディレクトリに直接アクセスできません。次の方法を使用する必要があります:
2.6.3.1 SAF ファイル経由
- アプリのカスタマイズを共有する
DocumentsProvider
アプリのカスタマイズにDocumentsProvider
は次の手順が必要です。
a) 指定するDocumentsProvider
b)DocumentsProvider
基本インターフェイスを実装します。
- アプリにアクセスし
ACTION_OPEN_DOCUMENT
てブラウジングを開始する
2.6.3.2 FileProviderを実装する共有アプリ
一般的な手順の概要は次のとおりです。
-
指定App FileProvider
-
ファイルパスを指定します。構成ファイルはres/xmlに配置する必要があります。
-
共有 URI を取得する
-
権限を設定して URI を送信する
-
アプリを受信し、受け付けたインターフィルターを設定する
-
Uriの受信と処理
2.6.3.3 アプリのカスタムプライベートプロバイダー
ContentProvider
アプリは特にカスタマイズできます内部文件共享
が、UI の操作は期待されていません。
2.7 メディアストア
2.7.1 MediaStore_data フィールド
MediaStore
では、DATA即(_data)字段
でAndroid Q
放棄され始めました。ファイルの読み取りと書き込みには を渡す必要がありますopenFileDescriptor
。
2.7.2 MediaStore ファイルの保留ステータス
Android Q では、現在のファイルのステータスをマークするために がMediaStore
追加されました。他のアプリはファイルをクエリします。インターフェイスが設定されていない場合、保留ステータスに設定されているファイルはクエリできません。これにより、アプリにはこのファイルへの排他的アクセスが与えられます。ダウンロード時など、いくつかの状況で使用されます。ダウンロード中、ファイルは保留中ステータスでダウンロードが完了し、ファイルの保留中ステータスは 0 に設定されます。IS_PENDING Flag
Pending
MediaStore
setIncludePending
2.7.3 MediaColumns.RELATIVE_PATH はストレージ パスを設定します
Android Qでは、MediaStore
パブリックディレクトリにファイルを保存することで、2.5.1.2項のUriとパブリックディレクトリの関係で指定した各記憶領域の第1階層ディレクトリに加え、MediaColumnsを通じて保存先の第2ディレクトリを指定することができます。 RELATIVE_PATH. このディレクトリ 複数のレベルを作成できます。具体的なコードは次のとおりです。
- ContentResolver の挿入メソッドは、
values.put(Media.RELATIVE_PATH,"Pictures/album/family ") を通じて保存ディレクトリを指定します。このうち、Pictures は第 1 レベルのディレクトリであり、album/family はサブディレクトリです。 - ContentResolver の更新メソッドは、
values.put(Media.RELATIVE_PATH,"Pictures/album/family ") を通じて保存ディレクトリを指定します。update メソッドを使用すると、保存場所を移動できます。
2.7.4 画像の Exif メタデータへのアクセス
Android Q では、アプリが画像の Exif メタデータにアクセスする必要がある場合、次の操作を行う必要があります。
- 適用する
ACCESS_MEDIA_LOCATION权限
- デモコードは次のとおりです
MediaStore.setRequireOriginal返回新Uri
。
2.7.5 アプリのフィルター ビュー、アクセス許可の概要
さまざまなディレクトリにアクセスするためのアプリの権限は次のように要約されます。
2.7.6 アプリケーションのアンインストール
アプリがAndroidManifest.xml
次のように宣言している場合:android:hasFragileUserData="true"
アプリをアンインストールするときに、アプリ データを保持するかどうかを尋ねるメッセージが表示されます。
2.7.7 アプリデータの移行
Android Q では、App TargetSDK>=Q
デフォルトは ですFiltered View
。アプリがフィルター ビューの場合、データの移行が必要になります。そうでない場合、古いデータは使用できなくなります。データ移行は次の側面から開始できます。
- ストレージを操作するための完全な権限を得るには、アプリ
Legacy View
をダウンロードする - 非公開領域に保存されているアプリ ファイルには、
SAF访问
SAF を通じてディレクトリ ファイルを選択することでアクセスでき、ユーザーはアプリ ファイルへのアクセスを選択できます。
Images
アプリは、保存する必要があるファイルを対応するパブリック ディレクトリに配置できます。その他のファイルは、アンインストール後にファイルを削除せずにその下にVideo
配置Audio
できます。Downloads
2.7.8 MediaStore クエリ
MediaStore
アクションを実行するために使用する場合query
、Projection
使用する場合は、で定義するColumn Name
必要がありますMediaStore
。
2.8 WRITE_MEDIA_STORAGE権限
2.8.1 背景
WRITE_MEDIA_STORAGE
これは、アプリがすべてのストレージ デバイスにアクセスできるようにする非常に強力な権限です。すべてのストレージ デバイスへのアクセス許可。これはメディア スタックにのみ与えられる必要があります。
2.8.2 互換性への影響
Android システムでは、ユーザー グループWRITE_MEDIA_STORAGE
を取得できることが規定されていますmedia_rw
。
- T カードや U ディスクなどのすべてのリムーバブル ストレージ デバイスについて、Android にマウントされている場合、通常のアプリには読み取り権限のみがあり、書き込み権限はありません。リムーバブル ストレージ デバイスに書き込むことができるのは、media_rw ユーザー グループ アプリのみです。
- Android Q のスコープ付きストレージの場合、この権限を使用してアプリが互換モードで実行されるように設定できます。
- Android CTS がテストを実施します。ユーザーが起動可能なアプリはこの許可を申請できません。
詳細については、「Android Bootcamp 2019 - Privacy About.pdf」を参照してください。
2.8.3 適応
アプリがMedia
またはにアクセスする必要がある場合は、または を外置存储设备
使用できます。MediaStore
Storage Access Framework(SAF)接口
3 まとめ
さて、要約は次のとおりです。
App TargetSDK > 28 即 Android10(Q)及以上
プロジェクト、Google はストレージ サンドボックス モードを制限しました。Android10(Q)以上的设备
推奨される使用法では私有目录data/data
、直接アクセスできなくなりました外部SD卡存储目录
。使用する必要がある場合は、インターフェース外部SD卡存储目录
を介してアクセスする必要があり、アクセスできるのは、、、、、、など。_ _ これらの外部 SD カード ストレージ ディレクトリ (パブリック ディレクトリ) はすべてのアプリからアクセスできるため、あまり安全ではありません。SAF
MediaStore
特定的外部SD卡存储目录
Downloads
Documents
Pictures
DCIM
Movies
Music
Ringtones
App TargetSDK <= 28 即 Android10(Q)以下
WRITE_MEDIA_STORAGE
プロジェクトは、権限がある限り無制限ですREAD_MEDIA_STORAGE
。したがって、外部 SD ストレージを変更したくない場合は、プロジェクトの targetSdk を <=28 に変更してください。SAF
インターフェイスMediaStore
へのアクセス方法については外部SD卡存储特定目录
、上記の説明を参照するか、詳細は公式ドキュメントを参照してください。ここではMediaStore
参考用に使用方法のデモを示します外部SD卡存储特定目录
。[スター] をクリックすることを忘れないでください。