page.title=特定のディレクトリへのアクセス page.keywords=preview,sdk,scoped directory access page.tags=androidn @jd:body

このドキュメントの内容

  1. 外部ストレージのディレクトリへのアクセス
  2. リムーバブル メディアのディレクトリへのアクセス
  3. ベスト プラクティス

写真アプリなどは通常、外部ストレージの特定のディレクトリ(Pictures ディレクトリなど)のみにアクセスする必要があります。 外部ストレージへのアクセスに関する従来のアプローチでは、このようなアプリに目的のディレクトリへのアクセスを容易に提供できる設計にはなっていませんでした。 次に例を示します。

Android N では、一般的な外部ストレージ ディレクトリにアクセスできる、新しいシンプルな API を提供します。

外部ストレージのディレクトリへのアクセス

StorageManager クラスを使用して、適切な StorageVolume インスタンスを取得します。次に、そのインスタンスの StorageVolume.createAccessIntent() メソッドを呼び出して、インテントを作成します。このインテントを使用して、外部ストレージのディレクトリにアクセスします。 リムーバブル メディア ボリュームなど、使用できるすべてのボリュームのリストを取得するには、StorageManager.getVolumesList() を使用します。

特定のファイルに関する情報がある場合は、 StorageManager.getStorageVolume(File) を使用して、そのファイルを含む StorageVolume を取得します。この StorageVolumecreateAccessIntent() を呼び出し、このファイルの外部ストレージ ディレクトリにアクセスします。

外部 SD カードなどのセカンダリ ボリュームで、 StorageVolume.createAccessIntent() を呼び出すときに null を渡し、特定のディレクトリではなくボリューム全体へのアクセスをリクエストします。プライマリ ボリュームに null を渡すか、無効なディレクトリ名を渡すと、 StorageVolume.createAccessIntent() は null を返します。

次のコード スニペットは、プライマリ共有ストレージの Pictures ディレクトリを開く方法の例を示しています。

StorageManager sm = (StorageManager)getSystemService(Context.STORAGE_SERVICE);
StorageVolume volume = sm.getPrimaryVolume();
Intent intent = volume.createAccessIntent(Environment.DIRECTORY_PICTURES);
startActivityForResult(intent, request_code);

システムは外部ディレクトリへのアクセスの付与を試行し、必要に応じてシンプルな UI で、ユーザーにアクセスを確認します。

図 1. Pictures ディレクトリへのアクセスを要求するアプリ

ユーザーがアクセスを付与すると、 Activity.RESULT_OK の結果コードと、URI を含むインテント データを指定して、 onActivityResult() のオーバーライドを呼び出します。提供された URI を使用して、ディレクトリの情報にアクセスします。これは、ストレージ アクセス フレームワークで返された URI を使用する場合と同様です。

ユーザーがアクセスを付与しなかった場合は、 Activity.RESULT_CANCELED の結果コードと、null のインテント データを指定して、 onActivityResult() のオーバーライドを呼び出します。

:特定の外部ディレクトリへのアクセスを取得すると、そのディレクトリ内のサブディレクトリへのアクセスも取得します。

リムーバブル メディアのディレクトリへのアクセス

特定のディレクトリへのアクセスを使用してリムーバブル メディア上のディレクトリにアクセスするには、まず {@link android.os.Environment#MEDIA_MOUNTED} 通知をリッスンする {@link android.content.BroadcastReceiver} を追加します。次に例を示します。

<receiver
    android:name=".MediaMountedReceiver"
    android:enabled="true"
    android:exported="true" >
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_MOUNTED" />
        <data android:scheme="file" />
    </intent-filter>
</receiver>

ユーザーが SD カードなどのリムーバブル メディアをマウントすると、システムは {@link android.os.Environment#MEDIA_MOUNTED} 通知を送信します。この通知は、インテント データ内の StorageVolume オブジェクトを提供します。このオブジェクトを使用して、リムーバブル メディア上のディレクトリにアクセスできます。 次の例では、リムーバブル メディア上の Pictures ディレクトリにアクセスします。

// BroadcastReceiver has already cached the MEDIA_MOUNTED
// notification Intent in mediaMountedIntent
StorageVolume volume = (StorageVolume)
    mediaMountedIntent.getParcelableExtra(StorageVolume.EXTRA_STORAGE_VOLUME);
volume.createAccessIntent(Environment.DIRECTORY_PICTURES);
startActivityForResult(intent, request_code);

ベスト プラクティス

外部ディレクトリのアクセス URI はできる限り保持してください。そうすれば、ユーザーに何度もアクセス要求をする必要がなくなります。 ユーザーがアクセスを付与したら、ディレクトリのアクセス URI を指定して getContentResolver().takePersistableUriPermssion() を呼び出します。 システムが URI を保持し、以降のアクセス要求では RESULT_OK を返して、ユーザーに確認の UI を表示しません。

ユーザーが外部ディレクトリへのアクセスを拒否した直後に、またアクセスをリクエストしないようにしてください。 何度もアクセスを要求すると、ユーザー エクスペリエンスが低下します。 リクエストがユーザーにより拒否され、アプリが再度アクセスをリクエストすると、UI に [Don't ask again] チェックボックスが表示されます。

図 1. リムーバブル メディアへのアクセスに対して 2 回目のリクエストを行うアプリ。

ユーザーが [Don't ask again] を選択してリクエストを拒否すると、特定のディレクトリに対するアプリからの今後のすべてのリクエストは自動的に拒否され、リクエストに関する UI は表示されなくなります。