1page.title=作用域目录访问 2page.keywords=Preview、SDK、作用域目录访问 3page.tags=Android N 4 5@jd:body 6 7 8 9 10 11<div id="tb-wrapper"> 12<div id="tb"> 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}images/android-7.0/scoped-directory-access-framed.png" srcset="{@docRoot}images/android-7.0/scoped-directory-access-framed.png 1x, 79{@docRoot}images/android-7.0/scoped-directory-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<receiver 106 android:name=".MediaMountedReceiver" 107 android:enabled="true" 108 android:exported="true" > 109 <intent-filter> 110 <action android:name="android.intent.action.MEDIA_MOUNTED" /> 111 <data android:scheme="file" /> 112 </intent-filter> 113</receiver> 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}images/android-7.0/scoped-directory-access-dont-ask.png" srcset="{@docRoot}images/android-7.0/scoped-directory-access-dont-ask.png 1x, 146{@docRoot}images/android-7.0/scoped-directory-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