Android 体系读取外置存储设备及其插拔监听

打印 上一主题 下一主题

主题 564|帖子 564|积分 1692

Android 设备存储一般分成内置存储(自身ROM)和外置存储,外置存储设备大抵就两种,即 SD 卡和 U 盘,本篇将先容如何获取外置存储设备的路径、读取文件列表和监听其插拔状态。
一、文件读写根本知识

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


  • 直接文件路径 File API
  • 媒体文件 MediaStore API
  • 存储访问框架 (SAF)
Android 界说了以下与存储相干的权限


  • READ_EXTERNAL_STORAGE
  • WRITE_EXTERNAL_STORAGE
  • MANAGE_EXTERNAL_STORAGE:MANAGE_EXTERNAL_STORAGE
Android 13 后增加的细分媒体权限
  1. <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
  2. <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
  3. <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
复制代码
一般持有MANAGE_EXTERNAL_STORAGE权限的即可访问绝大多数文件路径,例外情况Android/data、Android/obb访问私有应用文件目录,只有使用SAF方式访问,需要用户授权目录。
别的OTG存储,无法通过MediaStore API方式查询其媒体文件,但是可以查询内置存储和SD卡存储。
二、存储卷读取

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


  • storageManager.getVolumes() 返回一个 List<VolumeInfo> 对象,其中包含有关每个存储卷的信息,比方卷名、路径、范例等。
  • StorageManager.getVolumeList() 返回一个 StorageVolume[] 数组,其中包含每个存储卷的引用。
2. 可访问性:


  • storageManager.getVolumes() 可以访问全部范例的存储卷,包括内部存储、外部存储和 USB 存储。
  • StorageManager.getVolumeList() 只能访问可公开访问的存储卷,比方外部存储。(测试USB 存储无法读取)
3. 权限:


  • storageManager.getVolumes() 需要 READ_EXTERNAL_STORAGE 权限才气访问外部存储和 USB 存储。
  • StorageManager.getVolumeList() 不需要任何权限。
4. 版本:


  • storageManager.getVolumes() 是 Android 5.0 (API 21) 中引入的新方法。
  • StorageManager.getVolumeList() 是旧方法,在 Android 4.0 (API 14) 中引入。
表格总结了 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() 方法。
  • 如果您只需要访问可公开访问的存储卷,大概不想使用任何权限,请使用 StorageManager.getVolumeList() 方法。
三、 VolumeInfo和StorageVolume属性及其寄义

VolumeInfoStorageVolume 是 Android 体系中用于表示存储卷的两个类。它们之间存在一些相似之处,但也有一些关键区别。
VolumeInfo 类包含有关存储卷的更多信息,比方卷的状态和 UUID,而 StorageVolume 类只包含根本信息,比方卷名、路径、范例等。
以下是 VolumeInfoStorageVolume 类的属性及其寄义:
VolumeInfo 类属性:


  • _id: 卷 ID
  • type: 卷范例,比方内部存储、外部存储、USB 存储等
  • state: 卷状态,比方挂载、卸载、不可访问等
  • mountPoint: 卷挂载路径
  • fsUuid: 卷文件体系 UUID
  • partGuid: 卷分区 GUID
  • flags: 卷标记,比方只读、可移动等
  • ownerUid: 卷全部者用户 ID
  • primaryDir: 卷主目录
  • volumeId: 卷 ID
StorageVolume 类属性:


  • volumeId: 卷 ID
  • fsType: 卷文件体系范例
  • state: 卷状态,比方挂载、卸载、不可访问等
  • path: 卷挂载路径
  • isPrimary: 是否为主要存储卷
  • isEmulated: 是否为模拟存储卷
  • maxFileSize: 卷上最大文件大小
以下是一些属性的寄义:


  • type:
    指示存储卷的范例。可能的范例包括:

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

  • state:
    指示存储卷的状态。可能的 state 包括:

    • MEDIA_MOUNTED: 存储卷已挂载
    • MEDIA_UNMOUNTED: 存储卷已卸载
    • MEDIA_BAD_REMOVAL: 存储卷被不精确移除
    • MEDIA_NOFS: 存储卷没有文件体系
    • MEDIA_CHECKING: 存储卷正在查抄

  • mountPoint: 指示存储卷的挂载路径。
  • fsUuid: 指示存储卷的文件体系 UUID。
  • partGuid: 指示存储卷的分区 GUID。
  • flags:
    指示存储卷的标记。可能的标记包括:

    • FLAG_READ_ONLY: 存储卷只读
    • FLAG_REMOVABLE: 存储卷可移动

  • ownerUid: 指示存储卷的全部者用户 ID。
  • primaryDir: 指示存储卷的主目录。
  • volumeId: 指示存储卷的 ID。
  • fsType: 指示存储卷的文件体系范例。
  • isPrimary: 指示存储卷是否为主要存储卷。
  • isEmulated: 指示存储卷是否为模拟存储卷。
  • maxFileSize: 指示存储卷上最大文件大小。
四、输出存储卷代码

输出打印存储卷代码段:
  1.     public void getStorageVolume(Context context) {
  2.         StorageManager mStorageManagerr = ((StorageManager) context.getSystemService(Context.STORAGE_SERVICE));
  3.         // 使用 storageManager.getVolumes() 获取所有存储卷
  4.         List<VolumeInfo> volumeInfos = mStorageManager.getVolumes();
  5.         for (VolumeInfo volumeInfo : volumeInfos) {
  6.             android.util.Log.e("maxx", "getVolumes volumeInfo:" + volumeInfo.toString());
  7.         }
  8.         // 使用 StorageManager.getVolumeList() 获取所有可公开访问的存储卷
  9.         StorageVolume[] storageVolumes = mStorageManager.getVolumeList();
  10.         ArrayList<StorageVolume> mountVolumeList = new ArrayList<>();
  11.         for (StorageVolume storageVolume : storageVolumes) {
  12.             android.util.Log.e("maxx", "getVolumeList storageVolume:" + storageVolume.dump());
  13.         }
  14.     }
复制代码
输出打印存储卷信息:
  1. M144CA9  03-12 17:50:36.871 22035 22035 E maxx    : getVolumes volumeInfo:VolumeInfo{public:8,49}:
  2. M144CA9  03-12 17:50:36.871 22035 22035 E maxx    :     type=PUBLIC diskId=disk:8,48 partGuid= mountFlags=0 mountUserId=0
  3. M144CA9  03-12 17:50:36.871 22035 22035 E maxx    :     state=MOUNTED
  4. M144CA9  03-12 17:50:36.871 22035 22035 E maxx    :     fsType=vfat fsUuid=BEA6-BBCE fsLabel=
  5. M144CA9  03-12 17:50:36.871 22035 22035 E maxx    :     path=/mnt/media_rw/BEA6-BBCE internalPath=/mnt/media_rw/BEA6-BBCE
  6. M144CA9  03-12 17:50:36.871 22035 22035 E maxx    :     linkName=usbdisk
  7. M144CAA  03-12 17:50:36.871 22035 22035 E maxx    : getVolumes volumeInfo:VolumeInfo{private}:
  8. M144CAA  03-12 17:50:36.871 22035 22035 E maxx    :     type=PRIVATE diskId=null partGuid=null mountFlags=0 mountUserId=-10000
  9. M144CAA  03-12 17:50:36.871 22035 22035 E maxx    :     state=MOUNTED
  10. M144CAA  03-12 17:50:36.871 22035 22035 E maxx    :     fsType=null fsUuid=null fsLabel=null
  11. M144CAA  03-12 17:50:36.871 22035 22035 E maxx    :     path=/data internalPath=null linkName=unknown
  12. M144CAB  03-12 17:50:36.871 22035 22035 E maxx    : getVolumes volumeInfo:VolumeInfo{public:179,1}:
  13. M144CAB  03-12 17:50:36.871 22035 22035 E maxx    :     type=PUBLIC diskId=disk:179,0 partGuid= mountFlags=VISIBLE mountUserId=0
  14. M144CAB  03-12 17:50:36.871 22035 22035 E maxx    :     state=MOUNTED
  15. M144CAB  03-12 17:50:36.871 22035 22035 E maxx    :     fsType=vfat fsUuid=4819-161B fsLabel=
  16. M144CAB  03-12 17:50:36.871 22035 22035 E maxx    :     path=/storage/4819-161B internalPath=/mnt/media_rw/4819-161B
  17. M144CAB  03-12 17:50:36.871 22035 22035 E maxx    :     linkName=sdcard0
  18. M144CAC  03-12 17:50:36.871 22035 22035 E maxx    : getVolumes volumeInfo:=VolumeInfo{emulated;0}:
  19. M144CAC  03-12 17:50:36.871 22035 22035 E maxx    :     type=EMULATED diskId=null partGuid= mountFlags=PRIMARY|VISIBLE
  20. M144CAC  03-12 17:50:36.871 22035 22035 E maxx    :     mountUserId=0 state=MOUNTED
  21. M144CAC  03-12 17:50:36.871 22035 22035 E maxx    :     fsType=null fsUuid=null fsLabel=null
  22. M144CAC  03-12 17:50:36.871 22035 22035 E maxx    :     path=/storage/emulated internalPath=/data/media linkName=
  23. M144CAD  03-12 17:50:36.871 22035 22035 E maxx    : getVolumeList storageVolume:StorageVolume:
  24. M144CAD  03-12 17:50:36.871 22035 22035 E maxx    :     mId=emulated;0 mPath=/storage/emulated/0 mInternalPath=/storage/emulated/0
  25. M144CAD  03-12 17:50:36.871 22035 22035 E maxx    :     mDescription=内部共享存储空间 mPrimary=true mRemovable=false mEmulated=true
  26. M144CAD  03-12 17:50:36.871 22035 22035 E maxx    :     mAllowMassStorage=false mMaxFileSize=0 mOwner=UserHandle{0} mFsUuid=null
  27. M144CAD  03-12 17:50:36.871 22035 22035 E maxx    :     mState=mounted
  28. M144CCE  03-12 17:50:36.931 22035 22035 E maxx    : getVolumeList storageVolume=StorageVolume:
  29. M144CCE  03-12 17:50:36.931 22035 22035 E maxx    :     mId=public:179,1 mPath=/storage/4819-161B
  30. M144CCE  03-12 17:50:36.931 22035 22035 E maxx    :     mInternalPath=/mnt/media_rw/4819-161B mDescription=SanDisk SD 卡
  31. M144CCE  03-12 17:50:36.931 22035 22035 E maxx    :     mPrimary=false mRemovable=true mEmulated=false mAllowMassStorage=false
  32. M144CCE  03-12 17:50:36.931 22035 22035 E maxx    :     mMaxFileSize=4294967295 mOwner=UserHandle{0} mFsUuid=4819-161B
  33. M144CCE  03-12 17:50:36.931 22035 22035 E maxx    :     mState=mounted
复制代码
如何判断是SD卡照旧OTG照旧内部存储,根据输出的Log可以分析,内部存储的 type=EMULATED 而且 diskId=null,而SD卡和OTG范例都是 type=PUBLIC,而且 diskId 不为空。
所以通过volumeInfo的话,一般volumeInfo.getType() == VolumeInfo.TYPE_PRIVATE大概volumeInfo.getType() == VolumeInfo.TYPE_EMULATED就是内部存储,我碰到有些设备这种判断照旧不正确。volumeInfovol.getDisk() != null && vol.getDisk().isUsb()就是OTG,volumeInfo.getDisk() != null && sv.getDisk().isSd()就是SD卡。
大部分情况下,如果要获取全部存储卷,我们都是使用mStorageManager.getVolumes(),但是通过打印的Log,可以发现storageVolume会多一个属性mDescription用于形貌设备的内容,一般会包含设备的品牌名,如何从VolumeInfo中获取形貌设备属性呢?
  1. String mDescription = mStorageManager.getBestVolumeDescription(volumeInfo);
复制代码
我们还可以通过VolumeInfo获取存储设备的根目录,通过直接文件路径遍历文件目录
  1. public static String ROOT_PATH = "/storage/emulated/0";
  2. String path = (volume.getType() == VolumeInfo.TYPE_PRIVATE && VolumeInfo.ID_PRIVATE_INTERNAL.equals(volume.getId())
  3.                 || volume.getType() == VolumeInfo.TYPE_EMULATED)
  4.                 ? ROOT_PATH
  5.                 : volume.getPath().toString();
复制代码
五、监听插拔状态

5.1 监听SD卡插拔

  1. // 监听广播
  2. private final BroadcastReceiver mSDReceiver = new BroadcastReceiver() {
  3.     @Override
  4.     public void onReceive(Context context, Intent intent) {
  5.         String action = intent.getAction();
  6.         switch (action) {
  7.             case Intent.ACTION_MEDIA_CHECKING://SD卡正在检查
  8.             case Intent.ACTION_MEDIA_MOUNTED://SD卡挂载成功
  9.                 android.util.Log.e("maxx","mSDReceiver mounted");
  10.                 break;
  11.             case Intent.ACTION_MEDIA_EJECT://SD卡拔出
  12.             case Intent.ACTION_MEDIA_UNMOUNTED://SD卡卸载成功
  13.                 android.util.Log.e("maxx","mSDReceiver eject");
  14.                 break;
  15.             default:
  16.                 break;
  17.         }
  18.     }
  19. };
  20. // 注册监听
  21. IntentFilter intentFilter = new IntentFilter();
  22. intentFilter.addAction(Intent.ACTION_MEDIA_CHECKING);
  23. intentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
  24. intentFilter.addAction(Intent.ACTION_MEDIA_EJECT);
  25. intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
  26. registerReceiver(mSDReceiver, intentFilter);
  27. // 取消监听
  28. unregisterReceiver(mScannerReceiver);
复制代码
5.2 监听OTG插拔

网上很多说使用UsbManager.ACTION_USB_DEVICE_ATTACHED和UsbManager.ACTION_USB_DEVICE_DETACHED这二个广播可以监听到USB设备插拔,但是我使用OTG的设备插拔无法担当到此广播。
后面使用VolumeInfo.ACTION_VOLUME_STATE_CHANGED 才可以监听到,而且可以查察设备有没有准备好,有没有mounted大概unmounted。
VolumeInfo :
  1. public static final String ACTION_VOLUME_STATE_CHANGED =
  2.         "android.os.storage.action.VOLUME_STATE_CHANGED";
  3. public static final String EXTRA_VOLUME_ID =
  4.         "android.os.storage.extra.VOLUME_ID";
  5. public static final String EXTRA_VOLUME_STATE =
  6.         "android.os.storage.extra.VOLUME_STATE";
复制代码
  1. // 监听广播
  2. public final BroadcastReceiver mDiskReceiver = new BroadcastReceiver() {
  3.     @Override
  4.     public void onReceive(Context context, Intent intent) {
  5.         if(intent.getAction().equals(VolumeInfo.ACTION_VOLUME_STATE_CHANGED) ){
  6.             int id = intent.getIntExtra(VolumeInfo.EXTRA_VOLUME_ID, 0);
  7.             int state =intent.getIntExtra(VolumeInfo.EXTRA_VOLUME_STATE, VolumeInfo.STATE_UNMOUNTABLE);
  8.             android.util.Log.e("maxx","mDiskReceiver id:"+id+" state="+state);
  9.         }
  10.     }
  11. };
  12. //注册监听
  13. registerReceiver(mDiskReceiver, new IntentFilter(VolumeInfo.ACTION_VOLUME_STATE_CHANGED));
  14. //取消监听
  15. unregisterReceiver(mDiskReceiver);
复制代码
相干参考

[1] USB 存储前言
[2] 使用 MediaStore 查询 USB OTG 中的文件失败

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

去皮卡多

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表