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

使用KivyMD调用Android文件管理器无法选中文件求助

用Android原生文件管理器替代MDFileManager时无法获取选中文件信息的问题

我希望用Android原生文件管理器替代MDFileManager,目前能成功打开文件管理器,但选中文件后无法正常获取选中的文件信息。已在buildozer.spec中配置相关权限,调试时在Android Studio出现错误,以下是我的KivyMD代码:

from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivy.app import App

KV = '''
BoxLayout:
    orientation: 'vertical'

    Button:
        text: "Open File Manager"
        on_release: app.open_file_manager()

    Label:
        id: file_label
        text: "Selected File: None"
        halign: 'center'

    ScrollView:
        Label:
            id: file_content_label
            text: ""
'''

class FileManagerApp(App):
    file_label = ObjectProperty()
    file_content_label = ObjectProperty()

    def build(self):
        return Builder.load_string(KV)

    def open_file_manager(self):
        from jnius import autoclass
        PythonActivity = autoclass('org.kivy.android.PythonActivity')
        Intent = autoclass('android.content.Intent')
        activity = PythonActivity.mActivity

        intent = Intent()
        intent.setAction(Intent.ACTION_OPEN_DOCUMENT)
        intent.setType("*/*")

        activity.startActivityForResult(intent, 0)

    def on_activity_result(self, requestCode, resultCode, data):
        if resultCode == -1:  # Activity.RESULT_OK
            uri = data.getData()
            selected_file_name = self.get_file_name_from_uri(uri)
            self.root.ids.file_label.text = f"Selected File: {selected_file_name}"

            try:
                with open(uri.getPath(), 'rb') as file:
                    raw_data = file.read()
                    file_content = raw_data.decode('utf-8')
                    self.file_content_label.text = file_content
            except Exception as e:
                self.root.ids.file_content_label.text = f"Error reading file: {e}"

    def get_file_name_from_uri(self, uri):
        resolver = autoclass('android.content.ContentResolver')
        resolver_instance = resolver(activity.getContentResolver())
        cursor = resolver_instance.query(uri, None, None, None, None)
        if cursor.moveToFirst():
            column_index = cursor.getColumnIndex('_display_name')
            return cursor.getString(column_index)

if __name__ == '__main__':
    FileManagerApp().run()

问题分析与修复方案

核心问题点

  • Kivy的App类不会自动触发on_activity_result,必须通过JNI手动注册回调
  • ACTION_OPEN_DOCUMENT返回的是内容URI,不能直接用uri.getPath()打开文件,必须通过ContentResolver读取
  • get_file_name_from_uri中未正确获取ContentResolver实例,且未关闭cursor导致内存泄漏
  • 缺少文件持久化权限请求,重启APP后可能无法访问已选中的文件

修正后的代码

from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from jnius import autoclass, cast
from android import mActivity

KV = '''
BoxLayout:
    orientation: 'vertical'

    Button:
        text: "Open File Manager"
        on_release: app.open_file_manager()

    Label:
        id: file_label
        text: "Selected File: None"
        halign: 'center'

    ScrollView:
        Label:
            id: file_content_label
            text: ""
            text_size: self.width, None
            size_hint_y: None
            height: self.texture_size[1]
'''

class FileManagerApp(App):
    def build(self):
        self.register_activity_result_callback()
        return Builder.load_string(KV)

    def register_activity_result_callback(self):
        # 注册Activity结果回调
        PythonActivity = autoclass('org.kivy.android.PythonActivity')
        activity = PythonActivity.mActivity
        ActivityResultCallback = autoclass('android.app.Activity$OnActivityResultCallback')
        
        class MyCallback(ActivityResultCallback):
            def onActivityResult(self, requestCode, resultCode, data):
                self.parent.on_activity_result(requestCode, resultCode, data)
        
        self.callback = MyCallback()
        self.callback.parent = self
        activity.registerActivityResultCallback(self.callback)

    def open_file_manager(self):
        Intent = autoclass('android.content.Intent')
        intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
        intent.setType("*/*")
        # 添加权限标记,确保能持久访问文件
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
        mActivity.startActivityForResult(intent, 0)

    def on_activity_result(self, requestCode, resultCode, data):
        Activity = autoclass('android.app.Activity')
        if resultCode == Activity.RESULT_OK and data is not None:
            uri = data.getData()
            content_resolver = mActivity.getContentResolver()
            
            # 申请持久化权限
            take_flags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION)
            content_resolver.takePersistableUriPermission(uri, take_flags)
            
            selected_file_name = self.get_file_name_from_uri(uri)
            self.root.ids.file_label.text = f"Selected File: {selected_file_name}"

            try:
                # 通过ContentResolver读取文件内容
                input_stream = content_resolver.openInputStream(uri)
                input_stream = cast('java.io.InputStream', input_stream)
                raw_data = input_stream.read()
                input_stream.close()
                file_content = raw_data.decode('utf-8')
                self.root.ids.file_content_label.text = file_content
            except Exception as e:
                self.root.ids.file_content_label.text = f"Error reading file: {str(e)}"

    def get_file_name_from_uri(self, uri):
        content_resolver = mActivity.getContentResolver()
        cursor = content_resolver.query(uri, None, None, None, None)
        file_name = "Unknown"
        try:
            if cursor.moveToFirst():
                column_index = cursor.getColumnIndex('_display_name')
                if column_index != -1:
                    file_name = cursor.getString(column_index)
        finally:
            cursor.close()  # 必须关闭cursor,避免内存泄漏
        return file_name

if __name__ == '__main__':
    FileManagerApp().run()

额外配置要求

在buildozer.spec中确保添加以下权限和配置:

android.permissions = READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE, MANAGE_EXTERNAL_STORAGE
android.api = 33
android.ndk = 25b

内容的提问来源于stack exchange,提问作者Harshit

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 19:33:14