在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>
解决方案
核心问题分析
FileUriExposedException:Android 7.0+禁止直接将file://格式URI暴露给其他应用,必须通过FileProvider生成content://格式URI,这部分逻辑方向正确,但后续Intent配置有误。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
相关产品推荐
相关产品推荐

