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

Kivy安卓端全文件访问权限请求及目录创建权限被拒问题求助

安卓Kivy应用创建目录权限被拒问题解决方案

问题概述

在安卓平台的Kivy应用中尝试创建JiCsingApp目录保存历史数据时,反复触发[Errno 13] Permission denied错误,手动授予权限后程序可正常运行。以下是针对该问题的分析与修复方案,包含修改后的代码及配置。

核心问题与修复要点

  1. Android 10+存储机制限制
    Android 10(API 29)及以上版本启用分区存储,直接在外部存储根目录创建文件夹需MANAGE_EXTERNAL_STORAGE权限,且该权限无法通过代码自动获取,必须引导用户前往系统设置手动开启。

  2. 权限请求逻辑漏洞
    原代码在build和on_start中重复发起权限请求,且未处理MANAGE_EXTERNAL_STORAGE的特殊申请流程,导致权限未完全生效时就执行目录创建操作。

  3. 优先使用应用专属存储
    推荐使用应用专属存储路径(无需额外权限),既符合安卓存储规范,又能避免权限问题;若必须使用公共存储,需补充引导用户开启全存储访问权限的逻辑。

修改后的Python代码

#....
from kivy.utils import platform
from kivy.logger import Logger
from jnius import autoclass
from plyer import storagepath
import os
from kivy.storage.jsonstore import JsonStore

...

if platform == 'android':
    from android.permissions import request_permissions, Permission, check_permission
    from android.storage import app_storage_path, primary_external_storage_path, secondary_external_storage_path

...

    def build(self):
        self.title = '易經'
        self.theme_cls.theme_style = "Light"
        self.theme_cls.primary_palette = "Black"
        return Builder.load_file('oracle.kv')
    
    def request_android_permissions(self):
        permissions = [Permission.READ_EXTERNAL_STORAGE, Permission.WRITE_EXTERNAL_STORAGE]
        request_permissions(permissions, self.on_permissions_granted)

    def request_manage_storage_permission(self):
        # 引导用户前往系统设置开启全存储访问权限
        Intent = autoclass('android.content.Intent')
        Uri = autoclass('android.net.Uri')
        Settings = autoclass('android.provider.Settings')
        package_name = autoclass('android.app.ActivityThread').currentApplication().getPackageName()
        intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
        intent.setData(Uri.parse(f"package:{package_name}"))
        autoclass('org.kivy.android.PythonActivity').mActivity.startActivity(intent)

    def on_permissions_granted(self, permissions, grants):
        if all(grants):
            # 针对Android 10+检查全存储访问权限
            if platform == 'android':
                SDK_INT = autoclass('android.os.Build$VERSION').SDK_INT
                if SDK_INT >= 30:
                    Environment = autoclass('android.os.Environment')
                    if not Environment.isExternalStorageManager():
                        self.request_manage_storage_permission()
                        return
            self.store = self.get_store()
        else:
            Logger.error("基础存储权限未授予,程序将退出")
            self.exit_app()

    def create_app_directory(self):
        # 优先使用应用专属外部存储路径,无需特殊权限
        if platform == 'android':
            package_name = autoclass('android.app.ActivityThread').currentApplication().getPackageName()
            app_folder_path = os.path.join(
                primary_external_storage_path(), 
                'Android', 'data', package_name, 'files', 'JiCsingApp'
            )
        else:
            app_folder_path = 'JiCsingApp'
        
        if not os.path.exists(app_folder_path):
            os.makedirs(app_folder_path)
            print(f"目录 {app_folder_path} 创建成功")
        else:
            print(f"目录 {app_folder_path} 已存在")
        return app_folder_path
        
    def get_store(self):
        json_file_path = None
        
        if platform == 'android':
            try:
                app_directory = self.create_app_directory()
                print(f"应用目录路径: {app_directory}")
            except Exception as e:
                print(f"创建目录时出错: {e}")
                #  fallback到应用内部存储
                app_directory = app_storage_path()
            json_file_path = os.path.join(app_directory, 'prophecies.json')
        else:
            # 其他平台(Windows, MacOS, Linux)
            json_file_path = 'prophecies.json'

        Logger.info(f"使用JSON存储路径: {json_file_path}")
        return JsonStore(json_file_path)

    def on_start(self):
        # 启动时统一处理权限与存储初始化
        if platform == 'android':
            if self.check_storage_permission():
                SDK_INT = autoclass('android.os.Build$VERSION').SDK_INT
                if SDK_INT >= 30:
                    Environment = autoclass('android.os.Environment')
                    if not Environment.isExternalStorageManager():
                        self.request_manage_storage_permission()
                        return
                self.store = self.get_store()
            else:
                self.request_android_permissions()
        else:
            self.store = self.get_store()
        self.initialize_images()

    def check_storage_permission(self):
        # 检查基础读写存储权限
        return (check_permission(Permission.WRITE_EXTERNAL_STORAGE) and 
                check_permission(Permission.READ_EXTERNAL_STORAGE))

修改后的buildozer.spec配置

requirements = plyer, jnius, kivy, https://github.com/kivymd/kivymd/archive/master.zip ...

...

# (list) Permissions
android.permissions = android.permission.INTERNET, android.permission.WRITE_EXTERNAL_STORAGE, android.permission.READ_EXTERNAL_STORAGE, android.permission.MANAGE_EXTERNAL_STORAGE

内容的提问来源于stack exchange,提问作者Lőránt Nováki

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 13:22:11