广告

Kivy应用在Android 10+上实现文件读写的权限配置与存储路径管理全攻略

Android 10+ 文件读写权限机制与分区存储

核心概念与影响

在 Android 10(API 29)及以上版本,分区存储(scoped storage)成为默认的文件访问模型,应用仅能直接访问自己的应用私有目录和某些公开媒体集合。外部存储的直接读写权限受限,需要使用系统提供的存储框架或请求特定权限。

这对使用 Kivy 开发的跨平台应用意味着你不能像旧版本那样直接写入任意路径,必须通过 应用私有目录或通过 Storage Access Framework媒体库接入来实现文件操作。


<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

对Kivy应用的影响与解决思路

为了在 Android 10+ 上实现可靠的文件读写,Kivy 应用通常需要在 AndroidManifest.xml 配置中声明权限,并在运行时进行权限请求。getExternalFilesDirgetExternalStoragePublicDirectory 的使用策略也需要调整。

另外,开启 requestLegacyExternalStorage 选项仅在 Android 10 有效,且逐步被淘汰,因此长远看应优先采用 应用私有外部目录 或者通过 SAF/MediaStore 方案。

Kivy应用在Android上的存储路径与访问模式

存储路径类型概览

最常用的两类路径是 应用私有目录(内部存储)和 外部公共目录(外部存储)中的应用私有分区。前者对其他应用不可访问,后者则适合与系统媒体库共享数据。

Kivy 生态中,访问路径通常需要借助 pyjnius 获取 Android 的上下文并组合路径,例如 getExternalFilesDir 给出的路径通常位于 /Android/data/your.package.name/files


from jnius import autoclass
Context = autoclass('android.content.Context')
PythonActivity = autoclass('org.kivy.android.PythonActivity')
context = PythonActivity.mActivity.getApplicationContext()
external_files_dir = context.getExternalFilesDir(None).getAbsolutePath()

如何选择合适的路径

首要考虑的是数据的生命周期与可用性:若数据仅供应用自身使用,优先选择 应用私有外部目录,以降低权限需求并提升隐私安全。

若需要让用户跨设备共享或导出数据,应该通过 MediaStoreSAF 接口对外暴露文件,并在运行时请求所需的访问权限。

动态权限请求与用户交互流程

运行时权限模型要点

Android 6.0 起,运行时权限要求应用在需要时向用户请求,用户可在对话框中授权或拒绝。对于 READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE,在 Android 10+ 下需结合分区存储策略决定访问范围。

若用户拒绝授权,应用需要提供合适的回退路径,例如仅使用应用私有目录进行数据读写,或引导用户通过设置开启权限。对于某些场景,可能需要使用 SAF 进行手动选择文件。

在Kivy中触发权限请求的方式

可以借助 pyjnius 调用 ActivityCompat.requestPermissions 来弹出权限对话框,同时通过回调处理用户的选择结果。


from jnius import autoclass
ActivityCompat = autoclass('androidx.core.app.ActivityCompat')
ContextCompat = autoclass('androidx.core.content.ContextCompat')
PythonActivity = autoclass('org.kivy.android.PythonActivity')

READ = 'android.permission.READ_EXTERNAL_STORAGE'
WRITE = 'android.permission.WRITE_EXTERNAL_STORAGE'
activity = PythonActivity.mActivity

if ContextCompat.checkSelfPermission(activity, READ) != 0 or \
   ContextCompat.checkSelfPermission(activity, WRITE) != 0:
    ActivityCompat.requestPermissions(activity, [READ, WRITE], 101)

代码示例:在Kivy中实现文件读写的最小实现

Manifest与权限配置

在根级 AndroidManifest.xml 中,添加所需的权限声明,确保在 运行时权限请求 成功前不会阻塞应用初次启动。


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.example.kivyapp">
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest>

运行时权限请求与文件写入的实现示例

以下示例展示如何在获得权限后,将文本写入外部存储的应用私有目录,确保在 Android 10+ 环境中仍具可用性。


from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from jnius import autoclass

def write_text_to_external(text):
    PythonActivity = autoclass('org.kivy.android.PythonActivity')
    context = PythonActivity.mActivity.getApplicationContext()
    path = context.getExternalFilesDir(None).getAbsolutePath() + '/sample.txt'
    with open(path, 'w', encoding='utf-8') as f:
        f.write(text)

class TestApp(App):
    def build(self):
        pass

# 在合适的时机触发权限检查后调用 write_text_to_external("Hello Kivy Android!")

使用应用私有目录进行读写的示例

应用私有目录通常位于内部存储的专用路径,访问时无需额外权限,适合存放敏感数据或需要长期保留的数据。其典型路径形式为 getFilesDir() 的等效位置。


import os
from kivy.utils import platform
if platform == 'android':
    from android.storage import app_storage_path
    app_path = app_storage_path()
    with open(os.path.join(app_path, 'config.txt'), 'w') as f:
        f.write('config=ok')

存储路径管理策略与跨版本兼容

应用私有目录与外部公有目录的取舍

应用私有目录提供更高的隐私与安全性,且不需要额外的系统权限,但对用户可见的数据共享能力有限。

如果需要跨应用或跨设备共享,应该优先使用 外部公有目录结合 MediaStoreSAF 机制实现数据暴露与访问许可。

通过MediaStore与SAF实现跨应用访问

Android 10+ 的场景下,建议通过 MediaStoreStorage Access Framework (SAF) 实现对媒体文件的读取和写入,并通过 intents 提供给用户选择权限。

对于编写跨平台、跨版本的 Kivy 应用,开发者会把这部分逻辑模块化处理,并在不同平台标签下使用合适的 API,以实现兼容性与可维护性。

问题排错与调试要点

权限被拒绝的诊断步骤

遇到读写失败时,首先检查 AndroidManifest.xml 是否包含正确的权限声明,以及运行时是否正确触发了 权限请求 流程。

辅以设备日志(logcat)与应用私有目录的可用性检查,可以快速定位是权限问题还是路径不可用导致的故障。

在设备和模拟器上的测试要点

测试环境应覆盖 Android 10、11、12 及以上版本的行为差异,确保 scoped storageGET_EXTERNAL_FILES_DIR 的适配正确。

对外部写入场景,优先证明是否能通过 SAF 选择器完成文件创建/打开,以验证权限对比。

广告

后端开发标签