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

在Kivy中通过Pyjnius调用MediaStore获取音频文件数据的方法

Fetching Android Audio Files with Pyjnius & Populating Kivy RecycleView

Hey there! I’ve been exactly where you are—trying to hook up Kivy with Android’s MediaStore to build a media player, and figuring out Pyjnius bindings felt like navigating a maze at first. Let’s break this down step by step with a complete, working example that’ll get your audio files into a RecycleView in no time.

First: Set Up Permissions

Android won’t let your app access media files without explicit permission. Here’s what you need:

  • For Android 12 and below: Add READ_EXTERNAL_STORAGE to your buildozer.spec
  • For Android 13+: Use READ_MEDIA_AUDIO instead

In your buildozer.spec:

android.permissions = READ_MEDIA_AUDIO, INTERNET  # INTERNET is optional, but sometimes needed for build tools

Step 1: Pyjnius Code to Fetch Audio Data

We’ll use Pyjnius to call Android’s MediaStore API directly. This code will query all audio files and pull key metadata (title, artist, path, duration, etc.).

from kivy.app import App
from kivy.uix.recycleview import RecycleView
from kivy.lang import Builder
from jnius import autoclass, cast
from android.permissions import request_permissions, Permission

# Autoclass the Android classes we need
Context = autoclass('android.content.Context')
ContentResolver = autoclass('android.content.ContentResolver')
MediaStore = autoclass('android.provider.MediaStore')
Cursor = autoclass('android.database.Cursor')

class AudioRecycleView(RecycleView):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        # Request permissions first
        request_permissions([Permission.READ_MEDIA_AUDIO, Permission.READ_EXTERNAL_STORAGE], self.on_permissions_granted)

    def on_permissions_granted(self, permissions, grant_results):
        # Only proceed if permissions are granted
        if all(grant_results):
            self.fetch_audio_files()

    def fetch_audio_files(self):
        # Get the ContentResolver from the current app context
        app_context = cast(Context, App.get_running_app().android_context)
        content_resolver = app_context.getContentResolver()

        # Define the columns we want to retrieve from MediaStore
        projection = [
            MediaStore.Audio.Media.TITLE,
            MediaStore.Audio.Media.ARTIST,
            MediaStore.Audio.Media.DATA,  # File path
            MediaStore.Audio.Media.ALBUM,
            MediaStore.Audio.Media.DURATION
        ]

        # Query the MediaStore for audio files
        cursor = content_resolver.query(
            MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
            projection,
            None,
            None,
            MediaStore.Audio.Media.TITLE + " ASC"  # Sort by title ascending
        )

        audio_data = []
        if cursor is not None and cursor.moveToFirst():
            # Get index positions for each column
            title_idx = cursor.getColumnIndex(MediaStore.Audio.Media.TITLE)
            artist_idx = cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST)
            path_idx = cursor.getColumnIndex(MediaStore.Audio.Media.DATA)
            album_idx = cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM)
            duration_idx = cursor.getColumnIndex(MediaStore.Audio.Media.DURATION)

            # Iterate through all rows in the cursor
            while not cursor.isAfterLast():
                # Convert duration from milliseconds to mm:ss format
                duration_ms = cursor.getLong(duration_idx)
                minutes = int(duration_ms / 60000)
                seconds = int((duration_ms % 60000) / 1000)
                duration_str = f"{minutes:02d}:{seconds:02d}"

                audio_data.append({
                    'title': cursor.getString(title_idx) or 'Unknown Title',
                    'artist': cursor.getString(artist_idx) or 'Unknown Artist',
                    'album': cursor.getString(album_idx) or 'Unknown Album',
                    'duration': duration_str,
                    'path': cursor.getString(path_idx)
                })
                cursor.moveToNext()

            # Close the cursor to avoid memory leaks
            cursor.close()

        # Assign the data to RecycleView's data property
        self.data = audio_data

# Load the KV layout
Builder.load_string('''
<AudioRecycleView>:
    viewclass: 'AudioItem'
    RecycleBoxLayout:
        default_size: None, dp(60)
        default_size_hint: 1, None
        size_hint_y: None
        height: self.minimum_height
        orientation: 'vertical'

<AudioItem@BoxLayout>:
    items: {}
    padding: dp(10)
    spacing: dp(10)
    Label:
        text: root.items['title']
        size_hint_x: 0.4
    Label:
        text: root.items['artist']
        size_hint_x: 0.3
    Label:
        text: root.items['duration']
        size_hint_x: 0.2
''')

class MediaPlayerApp(App):
    def build(self):
        return AudioRecycleView()

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

Key Notes to Understand:

  • Permissions Handling: We use Kivy’s android.permissions module to request access before fetching files—critical for Android’s security model.
  • Cursor Navigation: Android returns query results as a Cursor; we have to iterate through it, get column indices, and extract data row by row.
  • Data Formatting: We convert the raw duration (in milliseconds) to a user-friendly mm:ss string for the UI.
  • RecycleView Setup: The KV language defines how each audio item looks, and we feed the audio_data list directly into the RecycleView’s data property.

Troubleshooting Tips:

  • If you’re on Android 13+, make sure you’re using READ_MEDIA_AUDIO instead of READ_EXTERNAL_STORAGE.
  • If no files show up, double-check that permissions are granted (you can add a fallback for denied permissions in on_permissions_granted).
  • Some devices might have media files in different locations, but EXTERNAL_CONTENT_URI should cover all standard audio files.

This example should get you up and running—you can extend it to add album art, play functionality, or sorting options as needed!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 09:11:51