Android设备中Kivy-Python应用文件读写权限申请失败致启动异常
解决Kivy Android应用存储权限拒绝问题
问题描述
开发基于Kivy的Python Android应用时,尝试在外部存储创建文件夹并写入文件,首次启动虽弹出权限申请,但仍触发权限拒绝错误:
02-22 10:23:22.339 29320 29348 I python : PermissionError: [Errno 13] Permission denied: '/storage/emulated/0/folder'
用户代码:
import kivy from kivy.app import App from kivy.uix.label import Label from kivy.utils import platform from pathlib import Path class MyApp(App): def build(self): text = "1"+App.get_running_app().user_data_dir + '\n' self.label = Label(text=text) if platform == "android": from android.storage import app_storage_path,primary_external_storage_path from android.permissions import request_permissions, check_permission, Permission self.label.text += primary_external_storage_path() +'\n' folder_path = Path(primary_external_storage_path(),"folder") request_permissions([Permission.READ_EXTERNAL_STORAGE, Permission.WRITE_EXTERNAL_STORAGE]) folder_path.mkdir(parents=True, exist_ok=True) file = Path(folder_path,"file.txt") else: file = Path(Path.home(),"file.txt") with open(file,"w") as tfile: tfile.write("test test") return self.label if __name__ == '__main__': MyApp().run()
buildozer.spec配置:
# (list) Permissions # (See https://python-for-android.readthedocs.io/en/latest/buildoptions/#build-options-1 for all the supported syntaxes and properties) android.permissions = android.permission.WRITE_EXTERNAL_STORAGE, android.permission.READ_EXTERNAL_STORAGE, android.permission.INTERNET
错误原因
- 权限申请异步性:
request_permissions是异步调用,调用后不会立即获得权限,代码中直接执行文件夹创建操作时,权限尚未通过,导致拒绝。 - Scoped Storage限制:Android 10(API 29)及以上版本引入Scoped Storage机制,即使申请了读写权限,直接操作外部存储根目录的文件夹也会被系统限制。
解决方案
1. 异步处理权限申请结果
将文件操作逻辑放到权限申请成功的回调函数中,确保只有在权限通过后才执行:
import kivy from kivy.app import App from kivy.uix.label import Label from kivy.utils import platform from pathlib import Path class MyApp(App): def build(self): self.text = "1" + App.get_running_app().user_data_dir + '\n' self.label = Label(text=self.text) if platform == "android": from android.storage import primary_external_storage_path from android.permissions import request_permissions, check_permission, Permission self.label.text += primary_external_storage_path() + '\n' self.folder_path = Path(primary_external_storage_path(), "folder") # 先检查权限状态,未授权则申请 if not all([check_permission(Permission.READ_EXTERNAL_STORAGE), check_permission(Permission.WRITE_EXTERNAL_STORAGE)]): request_permissions([Permission.READ_EXTERNAL_STORAGE, Permission.WRITE_EXTERNAL_STORAGE], self.on_permission_result) else: self.perform_file_operations() else: self.file = Path(Path.home(), "file.txt") self.perform_file_operations() return self.label def on_permission_result(self, permissions, grants): # 确认所有权限都被授权 if all(grants): self.perform_file_operations() else: self.label.text += "\n权限申请被拒绝,无法执行文件操作" def perform_file_operations(self): if platform == "android": self.folder_path.mkdir(parents=True, exist_ok=True) self.file = Path(self.folder_path, "file.txt") with open(self.file, "w") as tfile: tfile.write("test test") self.label.text += "\n文件操作完成" if __name__ == '__main__': MyApp().run()
2. 适配Scoped Storage
对于Android 10+设备,优先使用以下合规路径:
- 应用专属外部存储:使用
app_storage_path(),无需额外权限,卸载应用时数据会被清理:from android.storage import app_storage_path folder_path = Path(app_storage_path(), "folder") - 公共媒体/文档目录:如果需要创建用户可见的公共文件夹,选择Documents等系统允许的目录:
from android.storage import primary_external_storage_path docs_path = Path(primary_external_storage_path(), "Documents") folder_path = docs_path / "my_app_folder"
3. 优化buildozer配置
根据目标Android版本调整权限:
- 针对API 29以下,保留原有读写权限即可;
- 针对API 30+,若确实需要访问所有外部文件,可添加
MANAGE_EXTERNAL_STORAGE权限(不推荐,仅特殊场景使用):
android.permissions = android.permission.WRITE_EXTERNAL_STORAGE, android.permission.READ_EXTERNAL_STORAGE # API 30+全存储访问权限(可选) # android.permissions = android.permission.WRITE_EXTERNAL_STORAGE, android.permission.READ_EXTERNAL_STORAGE, android.permission.MANAGE_EXTERNAL_STORAGE
4. 增加权限前置检查
在每次执行文件操作前,先检查权限状态,避免因用户后续取消权限导致报错。
内容的提问来源于stack exchange,提问作者user23447338
相关产品推荐
相关产品推荐

