Android端Flutter Method Channel从URI获取音频元数据失败求助
问题分析与解决方案
你遇到的setDataSource failed: status = 0xFFFFFFEA错误,本质是Content URI的处理方式错误:你拿到的content://com.mi.android.globalFileexplorer.myprovider/external_files/song.mp3是Android的内容URI,不是本地文件系统的绝对路径,直接调用intent.getData().getPath()得到的/external_files/song.mp3没有实际文件意义,导致MediaMetadataRetriever无法找到音频文件,触发IO错误。
修正步骤与代码示例
1. 修正变量名错误
你原代码里的变量名存在拼写错误,先修正:
// 错误写法:private MediaMetadataRetriever song details; private MediaMetadataRetriever songDetails; // 移除多余的MetadataDetails[]定义(如果不需要的话) // private MetadataDetails[] songDetails;
2. 使用支持Content URI的setDataSource重载方法
MediaMetadataRetriever提供了直接接收Context和Uri的重载方法,它会通过ContentResolver正确访问内容URI指向的文件:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); songDetails = new MediaMetadataRetriever(); Intent intent = getIntent(); new MethodChannel(AudioServicePlugin.getFlutterEngine(this).getDartExecutor().getBinaryMessenger(), CHANNEL) .setMethodCallHandler((call, result) -> { if (call.method.equals("song")) { if (intent != null && intent.getData() != null) { try { // 直接传入Context和Content URI songDetails.setDataSource(this, intent.getData()); // 注意:不能直接返回MediaMetadataRetriever给Flutter,需提取需要的元数据 HashMap<String, String> metadata = new HashMap<>(); metadata.put("title", songDetails.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE)); metadata.put("artist", songDetails.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST)); metadata.put("duration", songDetails.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)); result.success(metadata); } catch (IOException e) { e.printStackTrace(); result.error("SET_DATA_SOURCE_FAILED", "无法加载音频元数据", e.getMessage()); } } else { result.success(null); } } }); }
3. 备选方案:通过ParcelFileDescriptor访问文件
如果需要使用文件描述符,需通过ContentResolver获取ParcelFileDescriptor,而不是直接用FileInputStream:
if (intent != null && intent.getData() != null) { try (ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(intent.getData(), "r")) { if (pfd != null) { songDetails.setDataSource(pfd.getFileDescriptor()); // 提取元数据并返回 HashMap<String, String> metadata = new HashMap<>(); metadata.put("title", songDetails.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE)); result.success(metadata); } else { result.error("FILE_DESCRIPTOR_NULL", "无法获取文件描述符", null); } } catch (IOException e) { e.printStackTrace(); result.error("IO_ERROR", "访问音频文件失败", e.getMessage()); } }
关键注意点
- 不要直接将
MediaMetadataRetriever对象返回给Flutter:原生对象无法被Flutter序列化,必须提取具体的元数据(如标题、艺术家、时长等)封装成Map或JSON格式返回。 - 权限问题:通过Intent接收的Content URI,系统已临时授予访问权限,无需额外申请存储权限;如果是主动查询的URI,需确保应用有对应权限。
内容的提问来源于stack exchange,提问作者Ruchit Soni
相关产品推荐
相关产品推荐

