外部存储和 USB 存储

发布时间:2025-06-24 18:32:56  作者:北方职教升学中心  阅读量:739


  • StorageManager.getVolumeList()不需要任何权限。

    二、路径、

  • 2. 可访问性:

    • storageManager.getVolumes()可以访问所有类型的存储卷,包括内部存储、(测试USB 存储无法读取

    3. 权限:

    • storageManager.getVolumes()需要 READ_EXTERNAL_STORAGE权限才能访问外部存储和 USB 存储。可能的 state 包括:

      • MEDIA_MOUNTED: 存储卷已挂载
      • MEDIA_UNMOUNTED: 存储卷已卸载
      • MEDIA_BAD_REMOVAL: 存储卷被不正确移除
      • MEDIA_NOFS: 存储卷没有文件系统
      • MEDIA_CHECKING: 存储卷正在检查
    • mountPoint:指示存储卷的挂载路径。外部存储、

    • flags:

      指示存储卷的标志。USB 存储等

    • state:卷状态,例如挂载、

    • fsUuid:指示存储卷的文件系统 UUID。volumeInfovol.getDisk() != null && vol.getDisk().isUsb()就是OTG,volumeInfo.getDisk() != null && sv.getDisk().isSd()就是SD卡。

    • 如果您只需要访问可公开访问的存储卷,或者不想使用任何权限,请使用 StorageManager.getVolumeList()方法。

    • maxFileSize:指示存储卷上最大文件大小。

    四、类型等。

    后面使用VolumeInfo.ACTION_VOLUME_STATE_CHANGED才可以监听到,而且可以查看设备有没有准备好,有没有mounted或者unmounted。读取文件列表和监听其插拔状态。可能的类型包括:

    • TYPE_INTERNAL: 内部存储
    • TYPE_EXTERNAL: 外部存储
    • TYPE_PUBLIC: 公共存储
    • TYPE_USB: USB 存储
    • TYPE_SDCARD: SD 卡
  • state:

    指示存储卷的状态。可移动等

  • ownerUid:卷所有者用户 ID
  • primaryDir:卷主目录
  • volumeId:卷 ID
  • StorageVolume类属性:

    • volumeId:卷 ID
    • fsType:卷文件系统类型
    • state:卷状态,例如挂载、

      以下是 VolumeInfoStorageVolume类的属性及其含义:

      VolumeInfo类属性:

      • _id:卷 ID
      • type:卷类型,例如内部存储、可能的标志包括:

        • FLAG_READ_ONLY: 存储卷只读
        • FLAG_REMOVABLE: 存储卷可移动
      • ownerUid:指示存储卷的所有者用户 ID。

      表格总结了 storageManager.getVolumes()StorageManager.getVolumeList()之间的区别:

      特性storageManager.getVolumes()StorageManager.getVolumeList()
      返回值List<VolumeInfo>StorageVolume[]
      可访问性所有类型可公开访问
      权限READ_EXTERNAL_STORAGE
      版本Android 5.0 (API 21)Android 4.0 (API 14)
      • 如果您需要访问所有类型的存储卷,并可以使用 READ_EXTERNAL_STORAGE权限,请使用 storageManager.getVolumes()方法。它们之间存在一些相似之处,但也有一些关键区别。Android/obb访问私有应用文件目录,只有使用SAF方式访问,需要用户授权目录。

      4. 版本:

      • storageManager.getVolumes()是 Android 5.0 (API 21) 中引入的新方法。不可访问等
      • mountPoint:卷挂载路径
      • fsUuid:卷文件系统 UUID
      • partGuid:卷分区 GUID
      • flags:卷标志,例如只读、

      • fsType:指示存储卷的文件系统类型。

        另外OTG存储,无法通过MediaStore API方式查询其媒体文件,但是可以查询内置存储和SD卡存储。

        所以通过volumeInfo的话,一般volumeInfo.getType() == VolumeInfo.TYPE_PRIVATE或者volumeInfo.getType() == VolumeInfo.TYPE_EMULATED就是内部存储,我遇到有些设备这种判断还是不准确。
        VolumeInfo

        publicstaticfinalStringACTION_VOLUME_STATE_CHANGED="android.os.storage.action.VOLUME_STATE_CHANGED";publicstaticfinalStringEXTRA_VOLUME_ID="android.os.storage.extra.VOLUME_ID";publicstaticfinalStringEXTRA_VOLUME_STATE="android.os.storage.extra.VOLUME_STATE";
        // 监听广播publicfinalBroadcastReceivermDiskReceiver =newBroadcastReceiver(){@OverridepublicvoidonReceive(Contextcontext,Intentintent){if(intent.getAction().equals(VolumeInfo.ACTION_VOLUME_STATE_CHANGED)){intid =intent.getIntExtra(VolumeInfo.EXTRA_VOLUME_ID,0);intstate =intent.getIntExtra(VolumeInfo.EXTRA_VOLUME_STATE,VolumeInfo.STATE_UNMOUNTABLE);android.util.Log.e("maxx","mDiskReceiver id:"+id+" state="+state);}}};//注册监听registerReceiver(mDiskReceiver,newIntentFilter(VolumeInfo.ACTION_VOLUME_STATE_CHANGED));//取消监听unregisterReceiver(mDiskReceiver);

        相关参考

        [1] USB 存储媒介

        [2] 使用 MediaStore 查询 USB OTG 中的文件失败

  • StorageManager.getVolumeList()返回一个 StorageVolume[]数组,其中包含每个存储卷的引用。

  • isEmulated:指示存储卷是否为模拟存储卷。文件读写基本知识

    Android 中文件读写的方式一般有如下三种

    • 直接文件路径 File API
    • 媒体文件 MediaStore API
    • 存储访问框架 (SAF)

    Android 定义了以下与存储相关的权限

    • READ_EXTERNAL_STORAGE
    • WRITE_EXTERNAL_STORAGE
    • MANAGE_EXTERNAL_STORAGE:MANAGE_EXTERNAL_STORAGE

    Android 13 后增加的细分媒体权限

    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /><uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /><uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

    一般持有MANAGE_EXTERNAL_STORAGE权限的即可访问绝大多数文件路径,例外情况Android/data

  • volumeId:指示存储卷的 ID。

    VolumeInfo类包含有关存储卷的更多信息,例如卷的状态和 UUID,而 StorageVolume类只包含基本信息,例如卷名、路径、 VolumeInfo和StorageVolume属性及其含义

    VolumeInfoStorageVolume是 Android 系统中用于表示存储卷的两个类。不可访问等

  • path:卷挂载路径
  • isPrimary:是否为主要存储卷
  • isEmulated:是否为模拟存储卷
  • maxFileSize:卷上最大文件大小
  • 以下是一些属性的含义:

    • type:

      指示存储卷的类型。

      一、存储卷读取

      storageManager.getVolumes()StorageManager.getVolumeList()都是用于获取设备上所有存储卷的方法,但它们之间存在一些关键区别:

      1. 返回值:

      • storageManager.getVolumes()返回一个 List<VolumeInfo>对象,其中包含有关每个存储卷的信息,例如卷名、卸载、

      • isPrimary:指示存储卷是否为主要存储卷。外部存储和 USB 存储。

        大部分情况下,如果要获取所有存储卷,我们都是使用mStorageManager.getVolumes(),但是通过打印的Log,可以发现storageVolume会多一个属性mDescription用于描述设备的内容,一般会包含设备的品牌名,如何从VolumeInfo中获取描述设备属性呢?

        StringmDescription =mStorageManager.getBestVolumeDescription(volumeInfo);

        我们还可以通过VolumeInfo获取存储设备的根目录,通过直接文件路径遍历文件目录

        publicstaticStringROOT_PATH="/storage/emulated/0";Stringpath =(volume.getType()==VolumeInfo.TYPE_PRIVATE&&VolumeInfo.ID_PRIVATE_INTERNAL.equals(volume.getId())||volume.getType()==VolumeInfo.TYPE_EMULATED)?ROOT_PATH:volume.getPath().toString();

        五、

      • StorageManager.getVolumeList()是旧方法,在 Android 4.0 (API 14) 中引入。

      • primaryDir:指示存储卷的主目录。

      • StorageManager.getVolumeList()只能访问可公开访问的存储卷,例如外部存储。

      • partGuid:指示存储卷的分区 GUID。监听插拔状态

        5.1 监听SD卡插拔

        // 监听广播privatefinalBroadcastReceivermSDReceiver =newBroadcastReceiver(){@OverridepublicvoidonReceive(Contextcontext,Intentintent){Stringaction =intent.getAction();switch(action){caseIntent.ACTION_MEDIA_CHECKING://SD卡正在检查            caseIntent.ACTION_MEDIA_MOUNTED://SD卡挂载成功                android.util.Log.e("maxx","mSDReceiver mounted");break;caseIntent.ACTION_MEDIA_EJECT://SD卡拔出            caseIntent.ACTION_MEDIA_UNMOUNTED://SD卡卸载成功                android.util.Log.e("maxx","mSDReceiver eject");break;default:break;}}};// 注册监听IntentFilterintentFilter =newIntentFilter();intentFilter.addAction(Intent.ACTION_MEDIA_CHECKING);intentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);intentFilter.addAction(Intent.ACTION_MEDIA_EJECT);intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);registerReceiver(mSDReceiver,intentFilter);// 取消监听unregisterReceiver(mScannerReceiver);

        5.2 监听OTG插拔

        网上很多说使用UsbManager.ACTION_USB_DEVICE_ATTACHEDUsbManager.ACTION_USB_DEVICE_DETACHED这二个广播可以监听到USB设备插拔,但是我使用OTG的设备插拔无法接受到此广播。类型等。输出存储卷代码

        输出打印存储卷代码段:

        publicvoidgetStorageVolume(Contextcontext){StorageManagermStorageManagerr =((StorageManager)context.getSystemService(Context.STORAGE_SERVICE));// 使用 storageManager.getVolumes() 获取所有存储卷List<VolumeInfo>volumeInfos =mStorageManager.getVolumes();for(VolumeInfovolumeInfo :volumeInfos){android.util.Log.e("maxx","getVolumes volumeInfo:"+volumeInfo.toString());}// 使用 StorageManager.getVolumeList() 获取所有可公开访问的存储卷StorageVolume[]storageVolumes =mStorageManager.getVolumeList();ArrayList<StorageVolume>mountVolumeList =newArrayList<>();for(StorageVolumestorageVolume :storageVolumes){android.util.Log.e("maxx","getVolumeList storageVolume:"+storageVolume.dump());}}

        输出打印存储卷信息:

        M144CA9  03-12 17:50:36.871 2203522035E maxx    :getVolumes volumeInfo:VolumeInfo{public:8,49}:M144CA9  03-12 17:50:36.871 2203522035E maxx    :type=PUBLIC diskId=disk:8,48 partGuid=mountFlags=0mountUserId=0M144CA9  03-12 17:50:36.871 2203522035E maxx    :state=MOUNTED M144CA9  03-12 17:50:36.871 2203522035E maxx    :fsType=vfat fsUuid=BEA6-BBCE fsLabel=M144CA9  03-12 17:50:36.871 2203522035E maxx    :path=/mnt/media_rw/BEA6-BBCE internalPath=/mnt/media_rw/BEA6-BBCE M144CA9  03-12 17:50:36.871 2203522035E maxx    :linkName=usbdisk M144CAA  03-12 17:50:36.871 2203522035E maxx    :getVolumes volumeInfo:VolumeInfo{private}:M144CAA  03-12 17:50:36.871 2203522035E maxx    :type=PRIVATE diskId=null partGuid=null mountFlags=0mountUserId=-10000 M144CAA  03-12 17:50:36.871 2203522035E maxx    :state=MOUNTED M144CAA  03-12 17:50:36.871 2203522035E maxx    :fsType=null fsUuid=null fsLabel=null M144CAA  03-12 17:50:36.871 2203522035E maxx    :path=/data internalPath=null linkName=unknown M144CAB  03-12 17:50:36.871 2203522035E maxx    :getVolumes volumeInfo:VolumeInfo{public:179,1}:M144CAB  03-12 17:50:36.871 2203522035E maxx    :type=PUBLIC diskId=disk:179,0 partGuid=mountFlags=VISIBLE mountUserId=0M144CAB  03-12 17:50:36.871 2203522035E maxx    :state=MOUNTED M144CAB  03-12 17:50:36.871 2203522035E maxx    :fsType=vfat fsUuid=4819-161B fsLabel=M144CAB  03-12 17:50:36.871 2203522035E maxx    :path=/storage/4819-161B internalPath=/mnt/media_rw/4819-161B M144CAB  03-12 17:50:36.871 2203522035E maxx    :linkName=sdcard0 M144CAC  03-12 17:50:36.871 2203522035E maxx    :getVolumes volumeInfo:=VolumeInfo{emulated;0}:M144CAC  03-12 17:50:36.871 2203522035E maxx    :type=EMULATED diskId=null partGuid=mountFlags=PRIMARY|VISIBLE M144CAC  03-12 17:50:36.871 2203522035E maxx    :mountUserId=0state=MOUNTED M144CAC  03-12 17:50:36.871 2203522035E maxx    :fsType=null fsUuid=null fsLabel=null M144CAC  03-12 17:50:36.871 2203522035E maxx    :path=/storage/emulated internalPath=/data/media linkName=M144CAD  03-12 17:50:36.871 2203522035E maxx    :getVolumeList storageVolume:StorageVolume:M144CAD  03-12 17:50:36.871 2203522035E maxx    :mId=emulated;0mPath=/storage/emulated/0 mInternalPath=/storage/emulated/0 M144CAD  03-12 17:50:36.871 2203522035E maxx    :mDescription=内部共享存储空间 mPrimary=true mRemovable=false mEmulated=true M144CAD  03-12 17:50:36.871 2203522035E maxx    :mAllowMassStorage=false mMaxFileSize=0mOwner=UserHandle{0}mFsUuid=null M144CAD  03-12 17:50:36.871 2203522035E maxx    :mState=mounted M144CCE  03-12 17:50:36.931 2203522035E maxx    :getVolumeList storageVolume=StorageVolume:M144CCE  03-12 17:50:36.931 2203522035E maxx    :mId=public:179,1 mPath=/storage/4819-161B M144CCE  03-12 17:50:36.931 2203522035E maxx    :mInternalPath=/mnt/media_rw/4819-161B mDescription=SanDisk SD 卡 M144CCE  03-12 17:50:36.931 2203522035E maxx    :mPrimary=false mRemovable=true mEmulated=false mAllowMassStorage=false M144CCE  03-12 17:50:36.931 2203522035E maxx    :mMaxFileSize=4294967295mOwner=UserHandle{0}mFsUuid=4819-161B M144CCE  03-12 17:50:36.931 2203522035E maxx    :mState=mounted 

        如何判断是SD卡还是OTG还是内部存储,根据输出的Log可以分析,内部存储的 type=EMULATED 并且 diskId=null,而SD卡和OTG类型都是 type=PUBLIC,并且 diskId 不为空。

        Android 设备存储一般分成内置存储(自身ROM)和外置存储,外置存储设备大致就两种,即 SD 卡和 U 盘,本篇将介绍如何获取外置存储设备的路径、卸载、

      三、