在Kivy中通过Pyjnius调用MediaStore获取音频文件数据的方法
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_STORAGEto yourbuildozer.spec - For Android 13+: Use
READ_MEDIA_AUDIOinstead
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.permissionsmodule 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:ssstring for the UI. - RecycleView Setup: The KV language defines how each audio item looks, and we feed the
audio_datalist directly into the RecycleView’sdataproperty.
Troubleshooting Tips:
- If you’re on Android 13+, make sure you’re using
READ_MEDIA_AUDIOinstead ofREAD_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_URIshould 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

