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

在Beeware/Toga Android项目中播放MP3文件的问题排查

问题:基于Python/Beeware/Toga开发Android应用时打开MP3文件的异常问题

问题现象

  • 初始代码可唤起音乐应用选择器,但添加指定播放文件的逻辑后,触发android.os.FileUriExposedException错误;
  • 改用FileProvider处理文件URI后,又出现android.content.ActivityNotFoundException错误。

初始可运行代码

async def pate_tlacitko(self, widget):
    from toga.platform import current_platform
    if current_platform == "android":
        from android.content import Intent
        from android.net import Uri
        from java.io import File

        intent = Intent(Intent.ACTION_MAIN)
        intent.addCategory(Intent.CATEGORY_APP_MUSIC)

        await self.app._impl.intent_result(intent)

报错代码及错误信息

代码

async def pate_tlacitko(self, widget):
    from toga.platform import current_platform
    if current_platform == "android":
        from android.content import Intent
        from android.net import Uri
        from java.io import File

        intent = Intent(Intent.ACTION_MAIN)
        intent.addCategory(Intent.CATEGORY_APP_MUSIC)
        intent.setDataAndType(Uri.fromFile(File(str("resources/test.mp3"))), "application/mp3")
        await self.app._impl.intent_result(intent)

错误日志

W/python.stderr: android.os.FileUriExposedException: file:///resources/test.mp3 exposed beyond app through Intent.getData()


修改后代码及错误信息

代码

async def pate_tlacitko(self, widget):
    from toga.platform import current_platform
    if current_platform == "android":
        from android.content import Intent
        from androidx.core.content import FileProvider
        from android.net import Uri
        from java.io import File

        context = self.app._impl.native

        file_path = "/storage/emulated/0/Music/test.mp3"
        file = File(file_path)

        uri = FileProvider.getUriForFile(context, f"{context.getPackageName()}.fileprovider", file)
        intent = Intent(Intent.ACTION_MAIN)
        intent.addCategory(Intent.CATEGORY_APP_MUSIC)
        intent.setDataAndType(uri, "application/mp3")
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        await self.app._impl.intent_result(intent)

错误日志

W/python.stderr: android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.MAIN cat=[android.intent.category.APP_MUSIC] dat=content://cz.pd.Level.fileprovider/... typ=/ flg=0x1 }


相关配置文件

pyproject.toml 配置

android_manifest_application_extra_content = "<provider android:name='androidx.core.content.FileProvider' android:authorities='${applicationId}.provider' android:exported='false' android:grantUriPermissions='true'> <meta-data android:name='android.support.FILE_PROVIDER_PATHS' android:resource='@xml/provider_paths' /></provider>"

provider_paths.xml 配置

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="external_files" path="."/>
</paths>

解决方案

核心问题分析

  1. FileUriExposedException:Android 7.0+禁止直接将file://格式URI暴露给其他应用,必须通过FileProvider生成content://格式URI,这部分逻辑方向正确,但后续Intent配置有误。
  2. ActivityNotFoundException:ACTION_MAIN + CATEGORY_APP_MUSIC是唤起音乐应用主界面的动作,而非播放指定文件的标准动作,且MIME类型、FileProvider权限配置存在不匹配。

修正后的代码

async def pate_tlacitko(self, widget):
    from toga.platform import current_platform
    if current_platform == "android":
        from android.content import Intent
        from androidx.core.content import FileProvider
        from android.net import Uri
        from java.io import File

        context = self.app._impl.native
        file_path = "/storage/emulated/0/Music/test.mp3"
        file = File(file_path)

        # 确保authorities与pyproject.toml配置完全一致
        uri = FileProvider.getUriForFile(context, f"{context.getPackageName()}.provider", file)
        
        # 使用ACTION_VIEW作为播放指定文件的标准动作
        intent = Intent(Intent.ACTION_VIEW)
        # 改用MP3标准MIME类型audio/mpeg
        intent.setDataAndType(uri, "audio/mpeg")
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        
        # 先验证是否有应用能处理该Intent,避免崩溃
        if intent.resolveActivity(context.getPackageManager()) is not None:
            await self.app._impl.intent_result(intent)
        else:
            # 可替换为弹窗提示用户安装音频应用
            print("未找到可播放音频的应用")

额外注意事项

  • 若使用应用内置的MP3文件(如resources/test.mp3),需先将文件复制到应用可访问的内部/外部存储目录,FileProvider无法直接读取APK包内的资源文件;
  • 确保provider_paths.xml放置在Beeware项目的src/android/res/xml/目录下,否则FileProvider无法加载路径配置。

内容的提问来源于stack exchange,提问作者PD321

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 02:38:16