Android 获取内外SD卡路径几种方法总结
Android获取SD卡路径:
外置sd卡路径,也许很多同学在平时的工作中并不会用到,因为现在很多机型都不支持外置sd卡(这也是Google目标),所以并不用考虑外置sd卡的路径问题。除了开发文件管理类的应用之外,其他应用使用Enviroment这个类中的一些静态方法就能满足需要。但也有一些特殊需求需要用到外置sd卡路径,那怎么才能准确获得外置sd卡的路径呢?
方法一
//内置sd卡路径
StringsdcardPath=System.getenv("EXTERNAL_STORAGE");
//内置sd卡路径
StringsdcardPath=Environment.getExternalStorageDirectory().getAbsolutePath();
//外置置sd卡路径
StringextSdcardPath=System.getenv("SECONDARY_STORAGE");
在Enviroment类的源码中获得sd卡路径其实也是通过System.getnv()方法来实现的,如隐藏的方法:
/**{@hide}*/
publicstaticFilegetLegacyExternalStorageDirectory(){
returnnewFile(System.getenv(ENV_EXTERNAL_STORAGE));
}
注:更详细的内容还是去看Enviroment源码。
另外要注意的是,在API23版本中SECONDARY_STORAGE被移除。
方法二
privatestaticStringgetStoragePath(ContextmContext,booleanis_removale){
StorageManagermStorageManager=(StorageManager)mContext.getSystemService(Context.STORAGE_SERVICE);
Class<?>storageVolumeClazz=null;
try{
storageVolumeClazz=Class.forName("android.os.storage.StorageVolume");
MethodgetVolumeList=mStorageManager.getClass().getMethod("getVolumeList");
MethodgetPath=storageVolumeClazz.getMethod("getPath");
MethodisRemovable=storageVolumeClazz.getMethod("isRemovable");
Objectresult=getVolumeList.invoke(mStorageManager);
finalintlength=Array.getLength(result);
for(inti=0;i<length;i++){
ObjectstorageVolumeElement=Array.get(result,i);
Stringpath=(String)getPath.invoke(storageVolumeElement);
booleanremovable=(Boolean)isRemovable.invoke(storageVolumeElement);
if(is_removale==removable){
returnpath;
}
}
}catch(ClassNotFoundExceptione){
e.printStackTrace();
}catch(InvocationTargetExceptione){
e.printStackTrace();
}catch(NoSuchMethodExceptione){
e.printStackTrace();
}catch(IllegalAccessExceptione){
e.printStackTrace();
}
returnnull;
}
通过反射的方式使用在sdk中被隐藏的类StroageVolume中的方法getVolumeList(),获取所有的存储空间(StroageVolume),然后通过参数is_removable控制,来获取内部存储和外部存储(内外sd卡)的路径,参数is_removable为false时得到的是内置sd卡路径,为true则为外置sd卡路径。
在API23Enviroment类中的内部类UserEnvironment中有一方法getExternalDirs与此一样,代码如下:
publicFile[]getExternalDirs(){
finalStorageVolume[]volumes=StorageManager.getVolumeList(mUserId,StorageManager.FLAG_FOR_WRITE);
finalFile[]files=newFile[volumes.length];
for(inti=0;i<volumes.length;i++){
files[i]=volumes[i].getPathFile();
}
returnfiles;
}
再看Enviroment的getExternalStorageDirectory方法实现:
publicstaticFilegetExternalStorageDirectory(){
throwIfUserRequired();
returnsCurrentUser.getExternalDirs()[0];
}
可以看出,在API23时,先是通过getExternalDirs()获取到所有存储空间的File[]数组,这个数组的第一个值:getExternalDirs()[0],即为内置sd卡所在路径。
而在API23之前的版本中,并没有类似getExternalDirs()的方法通过StorageVolume直接获得存储空间(StorageVolume),而时通过别的方式来实现的,看关键方法的源码:
publicstaticFilegetExternalStorageDirectory(){
throwIfUserRequired();
returnsCurrentUser.getExternalDirsForApp()[0];
}
这里的getExternalDirsForApp()和上面的getExternalDirs()的作用是一样的,都是得到所有存储空间的File[]数组。
publicFile[]getExternalDirsForApp(){
returnmExternalDirsForApp;
}
mExternalDirsForApp是在Enviroment类中的内部类UserEnvironment的构造方法中初始化的,Enviroment#UserEnvironment构造函数源码如下:
publicUserEnvironment(intuserId){
//Seestorageconfigdetailsathttp://source.android.com/tech/storage/
StringrawExternalStorage=System.getenv(ENV_EXTERNAL_STORAGE);
StringrawEmulatedSource=System.getenv(ENV_EMULATED_STORAGE_SOURCE);
StringrawEmulatedTarget=System.getenv(ENV_EMULATED_STORAGE_TARGET);
StringrawMediaStorage=System.getenv(ENV_MEDIA_STORAGE);
if(TextUtils.isEmpty(rawMediaStorage)){
rawMediaStorage="/data/media";
}
ArrayList<File>externalForVold=Lists.newArrayList();
ArrayList<File>externalForApp=Lists.newArrayList();
if(!TextUtils.isEmpty(rawEmulatedTarget)){
//Devicehasemulatedstorage;externalstoragepathsshouldhave
//userIdburnedintothem.
finalStringrawUserId=Integer.toString(userId);
finalFileemulatedSourceBase=newFile(rawEmulatedSource);
finalFileemulatedTargetBase=newFile(rawEmulatedTarget);
finalFilemediaBase=newFile(rawMediaStorage);
///storage/emulated/0
externalForVold.add(buildPath(emulatedSourceBase,rawUserId));
externalForApp.add(buildPath(emulatedTargetBase,rawUserId));
///data/media/0
mEmulatedDirForDirect=buildPath(mediaBase,rawUserId);
}else{
//Devicehasphysicalexternalstorage;useplainpaths.
if(TextUtils.isEmpty(rawExternalStorage)){
Log.w(TAG,"EXTERNAL_STORAGEundefined;fallingbacktodefault");
rawExternalStorage="/storage/sdcard0";
}
///storage/sdcard0
externalForVold.add(newFile(rawExternalStorage));
externalForApp.add(newFile(rawExternalStorage));
///data/media
mEmulatedDirForDirect=newFile(rawMediaStorage);
}
//Spliceinanysecondarystoragepaths,butonlyforowner
finalStringrawSecondaryStorage=System.getenv(ENV_SECONDARY_STORAGE);
if(!TextUtils.isEmpty(rawSecondaryStorage)&&userId==UserHandle.USER_OWNER){
for(StringsecondaryPath:rawSecondaryStorage.split(":")){
externalForVold.add(newFile(secondaryPath));
externalForApp.add(newFile(secondaryPath));
}
}
mExternalDirsForVold=externalForVold.toArray(newFile[externalForVold.size()]);
mExternalDirsForApp=externalForApp.toArray(newFile[externalForApp.size()]);
}
也可以根据这个方法得到一个获取所有存储空间的路径的方法getStorageDirectories():
/**
*ReturnsallavailableSD-Cardsinthesystem(includeemulated)
*<p/>
*Warning:Hack!BasedonAndroidsourcecodeofversion4.3(API18)
*Becausethereisnostandardwaytogetit.
*TODO:TestonfutureAndroidversions4.4+
*
*@returnpathstoallavailableSD-Cardsinthesystem(includeemulated)
*/
privatestaticfinalPatternDIR_SEPARATOR=Pattern.compile("/");
publicList<String>getStorageDirectories(){
//Finalsetofpaths
finalArrayList<String>rv=newArrayList<String>();
//PrimaryphysicalSD-CARD(notemulated)
finalStringrawExternalStorage=System.getenv("EXTERNAL_STORAGE");
//AllSecondarySD-CARDs(allexcludeprimary)separatedby":"
finalStringrawSecondaryStoragesStr=System.getenv("SECONDARY_STORAGE");
//PrimaryemulatedSD-CARD
finalStringrawEmulatedStorageTarget=System.getenv("EMULATED_STORAGE_TARGET");
if(TextUtils.isEmpty(rawEmulatedStorageTarget)){
//Devicehasphysicalexternalstorage;useplainpaths.
if(TextUtils.isEmpty(rawExternalStorage)){
//EXTERNAL_STORAGEundefined;fallingbacktodefault.
rv.add("/storage/sdcard0");
}else{
rv.add(rawExternalStorage);
}
}else{
//Devicehasemulatedstorage;externalstoragepathsshouldhave
//userIdburnedintothem.
finalStringrawUserId;
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.JELLY_BEAN_MR1){
rawUserId="";
}else{
finalStringpath=Environment.getExternalStorageDirectory().getAbsolutePath();
finalString[]folders=DIR_SEPARATOR.split(path);
finalStringlastFolder=folders[folders.length-1];
booleanisDigit=false;
try{
Integer.valueOf(lastFolder);
isDigit=true;
}catch(NumberFormatExceptionignored){
}
rawUserId=isDigit?lastFolder:"";
}
///storage/emulated/0[1,2,...]
if(TextUtils.isEmpty(rawUserId)){
rv.add(rawEmulatedStorageTarget);
}else{
rv.add(rawEmulatedStorageTarget+File.separator+rawUserId);
}
}
//Addallsecondarystorages
if(!TextUtils.isEmpty(rawSecondaryStoragesStr)){
//AllSecondarySD-CARDssplitedintoarray
finalString[]rawSecondaryStorages=rawSecondaryStoragesStr.split(File.pathSeparator);
Collections.addAll(rv,rawSecondaryStorages);
}
rootmode=Sp.getBoolean("rootmode",false);
if(rootmode)
rv.add("/");
Fileusb=getUsbDrive();
if(usb!=null&&!rv.contains(usb.getPath()))rv.add(usb.getPath());
returnrv;
}
publicFilegetUsbDrive(){
Fileparent;
parent=newFile("/storage");
try{
for(Filef:parent.listFiles()){
if(f.exists()&&f.getName().toLowerCase().contains("usb")&&f.canExecute()){
returnf;
}
}
}catch(Exceptione){
}
parent=newFile("/mnt/sdcard/usbStorage");
if(parent.exists()&&parent.canExecute())
return(parent);
parent=newFile("/mnt/sdcard/usb_storage");
if(parent.exists()&&parent.canExecute())
returnparent;
returnnull;
}
综上分析,通过方法一和方法二都可以正确的获取内外sd卡路径,但方法一会存在以下问题:
1、API>=23时方法一无效(暂未测试)
2、有些厂商的Rom改动太多,对相关原生API的支持存在问题,这时方法一可能会存在问题。
3、其他一些情况造成的原因(基本与2差不多,是ROM等因素造成的)
所以,在使用时建议使用方法二来获取内外置sd卡路径,在API23(Android6.0)之前使用getStorageDirectories()应该也是OK的。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!