android系统提供了Environment.getExternalStorageDirectory()接口获得存储器的路径,但是这个接口往往给的结果并不是我们想要的,在某些设备上它返回的是手机内部存储,某些设备它返回的手机外部存储。还有就是某些Android设备支持扩展多个sdcard,这个时候想要获得所有存储器的挂载路径,这个接口是没有办法办到的。
怎么获取Android设备所有存储器的位置呢?或者说获得所有的挂载点
系统提供了一个StorageManager,它有一个方法叫getVolumeList,这个方法的返回值是一个StorageVolume数组,StorageVolume类中封装了挂载路径,挂载状态,以及是否可以移除等等信息。但是很可惜,这个方法是隐藏的api,所以我们只能通过反射来调用这个方法了,下面是这个方法的源码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public
StorageVolume
[
]
getVolumeList
(
)
{
if
(
mMountService
==
null
)
return
new
StorageVolume
[
0
]
;
try
{
Parcelable
[
]
list
=
mMountService
.
getVolumeList
(
)
;
if
(
list
==
null
)
return
new
StorageVolume
[
0
]
;
int
length
=
list
.
length
;
StorageVolume
[
]
result
=
new
StorageVolume
[
length
]
;
for
(
int
i
=
0
;
i
<
length
;
i
++
)
{
result
[
i
]
=
(
StorageVolume
)
list
[
i
]
;
}
return
result
;
}
catch
(
RemoteException
e
)
{
Log
.
e
(
TAG
,
"Failed to get volume list"
,
e
)
;
return
null
;
}
}
|
通过反射,获取到Android设备所有存储器。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public
class
StorageInfo
{
public
String
path
;
public
String
state
;
public
boolean
isRemoveable
;
public
StorageInfo
(
String
path
)
{
this
.
path
=
path
;
}
public
boolean
isMounted
(
)
{
return
"mounted"
.
equals
(
state
)
;
}
}
|
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
|
public
static
List
listAvaliableStorage
(
Context
context
)
{
ArrayList
storagges
=
new
ArrayList
(
)
;
StorageManager
storageManager
=
(
StorageManager
)
context
.
getSystemService
(
Context
.
STORAGE_SERVICE
)
;
try
{
Class
<
?
>
[
]
paramClasses
=
{
}
;
Method
getVolumeList
=
StorageManager
.
class
.
getMethod
(
"getVolumeList"
,
paramClasses
)
;
getVolumeList
.
setAccessible
(
true
)
;
Object
[
]
params
=
{
}
;
Object
[
]
invokes
=
(
Object
[
]
)
getVolumeList
.
invoke
(
storageManager
,
params
)
;
if
(
invokes
!=
null
)
{
StorageInfo
info
=
null
;
for
(
int
i
=
0
;
i
<
invokes
.
length
;
i
++
)
{
Object
obj
=
invokes
[
i
]
;
Method
getPath
=
obj
.
getClass
(
)
.
getMethod
(
"getPath"
,
new
Class
[
0
]
)
;
String
path
=
(
String
)
getPath
.
invoke
(
obj
,
new
Object
[
0
]
)
;
info
=
new
StorageInfo
(
path
)
;
File
file
=
new
File
(
info
.
path
)
;
if
(
(
file
.
exists
(
)
)
&&
(
file
.
isDirectory
(
)
)
&&
(
file
.
canWrite
(
)
)
)
{
Method
isRemovable
=
obj
.
getClass
(
)
.
getMethod
(
"isRemovable"
,
new
Class
[
0
]
)
;
String
state
=
null
;
try
{
Method
getVolumeState
=
StorageManager
.
class
.
getMethod
(
"getVolumeState"
,
String
.
class
)
;
state
=
(
String
)
getVolumeState
.
invoke
(
storageManager
,
info
.
path
)
;
info
.
state
=
state
;
}
catch
(
Exception
e
)
{
e
.
printStackTrace
(
)
;
}
if
(
info
.
isMounted
(
)
)
{
info
.
isRemoveable
=
(
(
Boolean
)
isRemovable
.
invoke
(
obj
,
new
Object
[
0
]
)
)
.
booleanValue
(
)
;
storagges
.
add
(
info
)
;
}
}
}
}
}
catch
(
NoSuchMethodException
e1
)
{
e1
.
printStackTrace
(
)
;
}
catch
(
IllegalArgumentException
e
)
{
e
.
printStackTrace
(
)
;
}
catch
(
IllegalAccessException
e
)
{
e
.
printStackTrace
(
)
;
}
catch
(
InvocationTargetException
e
)
{
e
.
printStackTrace
(
)
;
}
storagges
.
trimToSize
(
)
;
return
storagges
;
}
|
如何判断存储器是内置存储还是外置存储呢?
StorageVolume这个类中提供了一个isRemovable()接口,通过反射调用它就可以知道存储器是否可以移除。把可以移除的存储器认定为外置sdcard,不可移除的存储器认定为内置存储器。
1
|
Method
isRemovable
=
obj
.
getClass
(
)
.
getMethod
(
"isRemovable"
,
new
Class
[
0
]
)
;
|
如何判断存储器的挂载状态呢?
同上面一样,需要反射系统接口才可以获取到挂载状态。下面是代码片段
1
2
3
|
Method
getVolumeState
=
StorageManager
.
class
.
getMethod
(
"getVolumeState"
,
String
.
class
)
;
state
=
(
String
)
getVolumeState
.
invoke
(
storageManager
,
info
.
path
)
;
info
.
state
=
state
;
|
总结
通过反射系统的StorageManager以及StorageVolume类提供的接口,就可以拿到Android设备挂载的所有存储器路径,以及存储器类型(内置存储还是外置存储),还有存储器的挂载状态等信息。