summaryrefslogtreecommitdiff
path: root/docs/html-intl/intl/zh-cn/preview/features/scoped-folder-access.jd
blob: eb6db76a12476f2d00d3d8c061437a3617fc2d4a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
page.title=作用域目录访问
page.keywords=preview,sdk,scoped directory access
page.tags=androidn

@jd:body

<div id="qv-wrapper">
<div id="qv">
  <h2>本文内容</h2>
  <ol>
    <li><a href="#accessing">访问外部存储目录</a></li>
    <li><a href="#removable">访问可移动介质上的目录</a></li>
    <li><a href="#best">最佳做法</a></li>
  </ol>
</div>
</div>

<p>应用(如照片应用)通常只需要访问外部存储中的特定目录,例如 <code>Pictures</code> 目录。
现有的外部存储访问方法未经专门设计,无法轻松地为这些类型的应用提供目标目录访问。

例如:</p>

<ul>
<li>在您的清单文件中请求 {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}
或 {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} 将允许访问外部存储上的所有公共目录,这可能导致访问的内容超出应用需要的内容。

</li>
<li>使用<a href="{@docRoot}guide/topics/providers/document-provider.html">存储访问框架</a>通常会让您的用户通过一个系统 UI 选取目录,如果应用始终访问同一个外部目录,则该操作没有任何必要。



</li>
</ul>

<p>Android N 提供简化的全新 API 以访问通用外部存储目录。
 </p>

<h2 id="accessing">访问外部存储目录</h2>

<p>使用 <code>StorageManager</code> 类获取适当的
<code>StorageVolume</code> 实例。然后,通过调用该实例的
<code>StorageVolume.createAccessIntent()</code> 方法创建一个 Intent。使用此 Intent 访问外部存储目录。
若要获取所有可用卷的列表,包括可移动介质卷,请使用
<code>StorageManager.getVolumesList()</code>。
</p>

<p>以下代码段展示如何在主要共享存储中打开
<code>Pictures</code> 目录:</p>

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

<p>系统尝试授予对外部目录的访问权限,并使用一个简化的 UI 向用户确认访问权限(如果需要):
</p>

<img src="{@docRoot}preview/images/scoped-folder-access-framed.png" srcset="{@docRoot}preview/images/scoped-folder-access-framed.png 1x,
{@docRoot}preview/images/scoped-folder-access-framed_2x.png 2x" />
<p class="img-caption"><strong>图 1.</strong> 一个请求访问 Pictures 目录的应用。
</p>

<p>如果用户授予访问权限,则系统会调用
<code>onActivityResult()</code> 重写方法,且结果代码为
<code>Activity.RESULT_OK</code>,Intent 数据包含 URI。使用提供的 URI 访问目录信息,与使用<a href="{@docRoot}guide/topics/providers/document-provider.html">存储访问框架</a>返回的 URI 类似。



</p>

<p>如果用户不授予访问权限,则系统会调用
<code>onActivityResult()</code> 重写方法,且结果代码为
<code>Activity.RESULT_CANCELED</code>,Intent 数据为 null。</p>

<p class="note"><b>注</b>:获得特定外部目录的访问权限也会获得该目录中子目录的访问权限。
</p>

<h2 id="removable">访问可移动介质上的目录</h2>

<p>若要使用作用域目录访问来访问可移动介质上的目录,首先要添加一个用于侦听
{@link android.os.Environment#MEDIA_MOUNTED} 通知的 {@link android.content.BroadcastReceiver},例如:
</p>

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

<p>当用户装载可移动介质时,如 SD 卡,系统将发送一则
{@link android.os.Environment#MEDIA_MOUNTED} 通知。此通知在 Intent 数据中提供一个 <code>StorageVolume</code> 对象,您可用它访问可移动介质上的目录。

以下示例访问可移动介质上的 <code>Pictures</code> 目录:
</p>

<pre>
// 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);
</pre>

<h2 id="best">最佳做法</h2>

<p>请尽可能保留外部目录访问 URI,这样即不必重复要求用户授予访问权限。
在用户授予访问权限后,使用目录访问 URI 调用
<code>getContentResolver().takePersistableUriPermssion()</code>。
系统将保留此 URI,后续的访问请求将返回 <code>RESULT_OK</code>,且不会向用户显示确认 UI。

</p>

<p>如果用户拒绝授予外部目录访问权限,请勿立即再次请求访问权限。
一再不停地请求访问权限会导致非常差的用户体验。
</p>