You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android API19+:如何通过Tree URI获取USB存储卷信息?

嘿,针对你要识别通过Intent.ACTION_OPEN_DOCUMENT_TREE选中的USB存储设备信息的需求,我整理了适配API19及以上的可行方案,分不同Android版本来处理,毕竟不同版本的系统API差异不小:

核心思路

通过Intent.ACTION_OPEN_DOCUMENT_TREE拿到的Tree URI,本质上对应系统的Documents Provider。要获取设备的描述、容量等信息,核心是把这个Tree URI关联到具体的存储卷,再通过系统API提取对应的属性——不同API版本的实现方式有明显区别,得分开处理。

适配方案

API 24+(Android 7.0及以上)

从Android 7.0开始,系统引入了StorageVolume类,能直接获取存储设备的大部分信息,这部分实现相对规整:

  1. 从Tree URI中提取文档ID,再拆分出存储卷ID(通常是文档ID的前半部分,格式类似primary:path或xxxx-xxxx:path);
  2. 通过StorageManager获取所有存储卷列表,遍历匹配对应的存储卷;
  3. 从匹配到的StorageVolume中直接读取描述、容量等信息。

代码示例:

// 假设你已经通过onActivityResult拿到了选中的treeUri
String treeDocId = DocumentsContract.getTreeDocumentId(treeUri);
String volumeId = treeDocId.split(":")[0];

StorageManager storageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
List<StorageVolume> storageVolumes = storageManager.getStorageVolumes();

for (StorageVolume volume : storageVolumes) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        // Android 11+直接用getVolumeId匹配
        if (volume.getVolumeId().equals(volumeId)) {
            String deviceDesc = volume.getDescription(this);
            long totalStorage = volume.getTotalBytes();
            long freeStorage = volume.getFreeBytes();
            // 在这里处理获取到的设备信息
            break;
        }
    } else {
        // Android 7.0-10用getUuid匹配(外部存储通常有UUID)
        UUID uuid = volume.getUuid();
        if (uuid != null && uuid.toString().equals(volumeId)) {
            String deviceDesc = volume.getDescription(this);
            // 容量需要通过StatFs获取,因为这几个版本的StorageVolume没有直接的容量方法
            String storagePath = volume.getDirectory().getAbsolutePath();
            StatFs statFs = new StatFs(storagePath);
            long totalStorage = (long) statFs.getBlockCountLong() * statFs.getBlockSizeLong();
            long freeStorage = (long) statFs.getAvailableBlocksLong() * statFs.getBlockSizeLong();
            // 处理信息
            break;
        }
    }
}

API 19-23(Android 4.4-6.0)

这部分系统没有提供StorageVolume类,只能通过解析URI、查询文件系统或USB设备信息来实现:

  1. 先判断URI对应的是否是外部存储的Documents Provider(通常Authority为com.android.externalstorage.documents);
  2. 拆分文档ID得到存储卷名称(比如USB1234或sdcard1),对应/storage/目录下的文件夹;
  3. 通过StatFs获取该目录的容量信息;
  4. 设备描述可以结合UsbManager获取连接的USB设备信息,或者直接用存储卷名称作为描述(因为不同厂商的关联逻辑差异大,这是最稳妥的方式)。

代码示例:

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
    String authority = treeUri.getAuthority();
    if ("com.android.externalstorage.documents".equals(authority)) {
        String docId = DocumentsContract.getTreeDocumentId(treeUri);
        String[] parts = docId.split(":");
        String volumeName = parts[0];
        
        // 构建存储路径
        String storagePath = "/storage/" + volumeName;
        File storageDir = new File(storagePath);
        if (storageDir.exists()) {
            // 获取容量信息
            StatFs statFs = new StatFs(storagePath);
            long totalStorage;
            long freeStorage;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                totalStorage = (long) statFs.getBlockCountLong() * statFs.getBlockSizeLong();
                freeStorage = (long) statFs.getAvailableBlocksLong() * statFs.getBlockSizeLong();
            } else {
                // API19用int类型的方法,注意可能溢出,建议强制转long
                totalStorage = (long) statFs.getBlockCount() * statFs.getBlockSize();
                freeStorage = (long) statFs.getAvailableBlocks() * statFs.getBlockSize();
            }
            
            // 获取设备描述
            UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
            HashMap<String, UsbDevice> usbDevices = usbManager.getDeviceList();
            String deviceDesc = "USB存储设备(" + volumeName + ")";
            // 如果需要更精准的设备名称,可以遍历USB设备列表尝试关联,不过不同厂商实现差异大,按需处理
            for (UsbDevice device : usbDevices.values()) {
                // 这里可以根据设备的vendorId、productId等信息匹配,逻辑需自行调整
                // deviceDesc = device.getDeviceName();
            }
            // 处理获取到的信息
        }
    }
}
关键注意事项
  • 权限问题:API19-28需要申请READ_EXTERNAL_STORAGE权限,API23+还需要动态申请;API29+不需要该权限,但建议在Manifest中声明以兼容低版本;
  • 厂商适配:不同品牌的Documents Provider可能有特殊的文档ID格式,测试时要覆盖主流厂商的设备;
  • 容量计算溢出:API19的StatFs方法返回int类型,计算时要强制转为long避免溢出;
  • USB设备关联:API19-23中,存储卷名称和USB设备的关联逻辑没有统一标准,若不需要精准名称,用存储卷名称作为描述是最稳妥的方案。

内容的提问来源于stack exchange,提问作者Nuno V.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:00:24