1page.title=作用域目录访问
2page.keywords=Preview、SDK、作用域目录访问
3page.tags=Android N
4
5@jd:body
6
7
8
9
10
11<div id="qv-wrapper">
12<div id="qv">
13  <h2>本文内容</h2>
14  <ol>
15    <li><a href="#accessing">访问外部存储目录</a></li>
16    <li><a href="#removable">访问可移动介质上的目录</a></li>
17    <li><a href="#best">最佳做法</a></li>
18  </ol>
19</div>
20</div>
21
22<p>应用(如照片应用)通常只需要访问外部存储中的特定目录,例如 <code>Pictures</code> 目录。
23现有的外部存储访问方法未经专门设计,无法轻松地为这些类型的应用提供目标目录访问。
24
25例如:</p>
26
27<ul>
28<li>在您的清单中请求 {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}
29或 {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} 将允许访问外部存储上的所有公共目录,这可能导致访问的内容超出应用需要的内容。
30
31</li>
32<li>使用<a href="{@docRoot}guide/topics/providers/document-provider.html">存储访问框架</a>通常会让您的用户通过一个系统 UI 选取目录,如果应用始终访问同一个外部目录,则该操作没有任何必要。
33
34
35
36</li>
37</ul>
38
39<p>Android N 提供简化的全新 API 以访问通用外部存储目录。
40 </p>
41
42<h2 id="accessing">访问外部存储目录</h2>
43
44<p>使用 <code>StorageManager</code> 类获取适当的
45<code>StorageVolume</code> 实例。然后,通过调用该实例的
46<code>StorageVolume.createAccessIntent()</code> 方法创建一个 Intent。使用此 Intent 访问外部存储目录。
47若要获取所有可用卷的列表,包括可移动介质卷,请使用
48<code>StorageManager.getVolumesList()</code>。
49</p>
50
51<p>如果您有关于特定文件的信息,使用 <code>StorageManager.getStorageVolume(File)</code> 来获得包含该文件的 <code>StorageVolume</code>。
52
53调用在 <code>StorageVolume</code> 上的 <code>createAccessIntent()</code> 以访问文件的外部存储目录。
54
55</p>
56
57<p>
58在二级卷(例如外部 SD 卡)上,当调用 <code>StorageVolume.createAccessIntent()</code> 以请求访问整个卷,而不是特定目录时,传入“null”。如果您向主要卷传入“null”,或者如果您传入无效的目录名,<code>StorageVolume.createAccessIntent()</code> 将返回“null”。
59
60
61
62
63</p>
64
65<p>以下代码段展示如何在主要共享存储中打开<code>Pictures</code> 目录:
66</p>
67
68<pre>
69StorageManager sm = (StorageManager)getSystemService(Context.STORAGE_SERVICE);
70StorageVolume volume = sm.getPrimaryVolume();
71Intent intent = volume.createAccessIntent(Environment.DIRECTORY_PICTURES);
72startActivityForResult(intent, request_code);
73</pre>
74
75<p>系统尝试授予对外部目录的访问权限,并使用一个简化的 UI 向用户确认访问权限(如果需要):
76</p>
77
78<img src="{@docRoot}preview/images/scoped-folder-access-framed.png" srcset="{@docRoot}preview/images/scoped-folder-access-framed.png 1x,
79{@docRoot}preview/images/scoped-folder-access-framed_2x.png 2x" />
80<p class="img-caption"><strong>图 1.</strong> 一个请求访问 Pictures 目录的应用。
81</p>
82
83<p>如果用户授予访问权限,则系统会调用
84<code>onActivityResult()</code> 重写方法,且结果代码为
85<code>Activity.RESULT_OK</code>,Intent 数据包含 URI。使用提供的 URI 访问目录信息,与使用<a href="{@docRoot}guide/topics/providers/document-provider.html">存储访问框架</a>返回的 URI 类似。
86
87
88
89</p>
90
91<p>如果用户不授予访问权限,则系统会调用
92<code>onActivityResult()</code> 重写方法,且结果代码为
93<code>Activity.RESULT_CANCELED</code>,Intent 数据为 null。</p>
94
95<p class="note"><b>注</b>:获得特定外部目录的访问权限也会获得该目录中子目录的访问权限。
96</p>
97
98<h2 id="removable">访问可移动介质上的目录</h2>
99
100<p>若要使用作用域目录访问来访问可移动介质上的目录,首先要添加一个用于侦听
101{@link android.os.Environment#MEDIA_MOUNTED} 通知的 {@link android.content.BroadcastReceiver},例如:
102</p>
103
104<pre>
105&lt;receiver
106    android:name=".MediaMountedReceiver"
107    android:enabled="true"
108    android:exported="true" &gt;
109    &lt;intent-filter&gt;
110        &lt;action android:name="android.intent.action.MEDIA_MOUNTED" /&gt;
111        &lt;data android:scheme="file" /&gt;
112    &lt;/intent-filter&gt;
113&lt;/receiver&gt;
114</pre>
115
116<p>当用户装载可移动介质时,如 SD 卡,系统将发送一则
117{@link android.os.Environment#MEDIA_MOUNTED} 通知。此通知在 Intent 数据中提供一个 <code>StorageVolume</code> 对象,您可用它访问可移动介质上的目录。
118
119以下示例访问可移动介质上的 <code>Pictures</code> 目录:
120</p>
121
122<pre>
123// BroadcastReceiver has already cached the MEDIA_MOUNTED
124// notification Intent in mediaMountedIntent
125StorageVolume volume = (StorageVolume)
126    mediaMountedIntent.getParcelableExtra(StorageVolume.EXTRA_STORAGE_VOLUME);
127volume.createAccessIntent(Environment.DIRECTORY_PICTURES);
128startActivityForResult(intent, request_code);
129</pre>
130
131<h2 id="best">最佳做法</h2>
132
133<p>请尽可能保留外部目录访问 URI,这样即不必重复要求用户授予访问权限。
134在用户授予访问权限后,使用目录访问 URI 调用
135<code>getContentResolver().takePersistableUriPermssion()</code>。
136系统将保留此 URI,后续的访问请求将返回 <code>RESULT_OK</code>,且不会向用户显示确认 UI。
137
138</p>
139
140<p>如果用户拒绝授予外部目录访问权限,请勿立即再次请求访问权限。
141一再不停地请求访问权限会导致非常差的用户体验。
142如果用户拒绝了一项请求,而应用再次请求访问,UI 会显示一个 <b>Don't ask again</b> 复选框:
143</p>
144
145<img src="{@docRoot}preview/images/scoped-folder-access-dont-ask.png" srcset="{@docRoot}preview/images/scoped-folder-access-dont-ask.png 1x,
146{@docRoot}preview/images/scoped-folder-access-dont-ask_2x.png 2x" />
147<p class="img-caption"><strong>图 1.</strong> 应用第二次请求访问可移动介质。
148</p>
149
150<p>如果用户选择 <b>Don't ask again</b> 并拒绝请求,您的应用向指定目录提出的所有未来请求都将被自动拒绝,并且将不会有请求 UI 呈现给用户。
151
152</p>
153