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

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

错误原因

  1. 权限申请异步性:request_permissions是异步调用,调用后不会立即获得权限,代码中直接执行文件夹创建操作时,权限尚未通过,导致拒绝。
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 15:07:34