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的。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!