Android端Kivy开发音乐下载App安装后启动崩溃求助
YouTube音乐下载App Android打包崩溃问题
我用Python的Kivy库开发了一款通过API从YouTube下载音乐的App,桌面运行main.py时功能完全正常,但用buildozer打包成APK安装到Android手机后,App显示图片后立即崩溃。
main.py代码
from kivy.lang import Builder from kivy.uix.recycleview import RecycleView from googleapiclient.discovery import build from pytube import YouTube from kivymd.app import MDApp from kivymd.uix.button import MDFillRoundFlatButton from kivymd.uix.textfield import MDTextField from kivymd.uix.list import OneLineListItem from kivy.uix.image import Image from kivy.uix.label import Label from kivymd.uix.boxlayout import MDBoxLayout from kivy.uix.widget import Widget from kivymd.theming import ThemeManager from kivy.uix.screenmanager import Screen, ScreenManager from kivymd.uix.dialog import MDDialog from kivy.app import App import logging import os Builder.load_string(''' <RecycleViewRow@OneLineListItem>: text: '' video_id: '' on_release: app.on_video_selected(root.video_id) <RV>: viewclass: 'RecycleViewRow' RecycleBoxLayout: default_size: None, dp(56) default_size_hint: 1, None size_hint_y: None height: self.minimum_height orientation: 'vertical' ''') class RV(RecycleView): def __init__(self, **kwargs): super(RV, self).__init__(**kwargs) self.data = [] class MusicDownloaderApp(MDApp): def build(self): logging.info('Building the app') self.theme_cls.primary_palette = 'Teal' self.theme_cls.primary_hue = '700' self.theme_cls.theme_style = 'Light' layout = MDBoxLayout(orientation='vertical', padding='32dp', spacing='20dp') # Add a BoxLayout at the top for the title/logo title_layout = MDBoxLayout(orientation='horizontal', size_hint=(1, None), height="50dp", padding='2dp', spacing='7dp' ) layout.add_widget(title_layout) # Add a label as a title title = Label(text="Music Downloader", font_size='20sp') title_layout.add_widget(title) title_layout.add_widget(Widget()) # Or add an image as a logo logo = Image(source="download.png", size_hint=(None, None), size=("80dp", "80dp")) title_layout.add_widget(logo) title_layout.add_widget(Widget()) title_layout.add_widget(Widget()) self.search_bar = MDTextField( hint_text="Enter song title or artist name", size_hint=(2, None), height="40dp" ) layout.add_widget(self.search_bar) self.search_button = MDFillRoundFlatButton( text="Search", on_release=self.perform_search ) layout.add_widget(self.search_button) self.results_list = RV() layout.add_widget(self.results_list) return layout def perform_search(self, instance): query = self.search_bar.text logging.info(f'Performing search with query {query}') if not query.strip(): return api_key = '<my-api-key' youtube = build('youtube', 'v3', developerKey=api_key) request = youtube.search().list( part="snippet", maxResults=50, q=query ) response = request.execute() results = [] for item in response['items']: if item['id']['kind'] == "youtube#video": video_title = item['snippet']['title'] video_id = item['id']['videoId'] results.append({'text': video_title, 'video_id': video_id}) self.results_list.data = results def on_video_selected(self, video_id): logging.info(f'Selected video with id {video_id}') youtube_url = f"[https://www.youtube.com/watch?v={video_id}]" youtube = YouTube(youtube_url) # Get the path to the app's internal storage directory app_path = App.get_running_app().user_data_dir # Define a subdirectory to save the downloads download_dir = os.path.join(app_path, 'downloads') # Create the download directory if it doesn't exist if not os.path.exists(download_dir): os.makedirs(download_dir) # Download the video to the download directory youtube.streams.filter(only_audio=True).first().download(download_dir) dialog = MDDialog(title='Finished Downloading', text='Enjoy your music!') dialog.open() if __name__ == "__main__": MusicDownloaderApp().run()
buildozer.spec配置
[app] # (str) Title of your application title = Music Downloader # (str) Package name package.name = musicdownloader # (str) Package domain (needed for android/ios packaging) package.domain = org.test # (str) Source code where the main.py live source.dir = . # (list) Source files to include (let empty to include all the files) source.include_exts = py,png,jpg,kv,atlas # (str) Application versioning (method 1) version = 0.1 # (list) Application requirements # comma separated e.g. requirements = sqlite3,kivy requirements = python3,kivy,kivymd,pytube,google-api-python-client # (str) Presplash of the application presplash.filename = /content/download.png # (str) Icon of the application icon.filename = /content/download.png # # OSX Specific # # # author = © Copyright Info # change the major version of python used by the app osx.python_version = 3 # Kivy version to use osx.kivy_version = 1.9.1 # # Android specific # # (bool) Indicate if the application should be fullscreen or not fullscreen = 0 # (list) Permissions # (See https://python-for-android.readthedocs.io/en/latest/buildoptions/#build-options-1 for all the supported syntaxes and properties) android.permissions = INTERNET,WRITE_EXTERNAL_STORAGE # # iOS specific # # (str) Path to a custom kivy-ios folder #ios.kivy_ios_dir = ../kivy-ios # Alternately, specify the URL and branch of a git checkout: ios.kivy_ios_url = https://github.com/kivy/kivy-ios ios.kivy_ios_branch = master # Another platform dependency: ios-deploy # Uncomment to use a custom checkout #ios.ios_deploy_dir = ../ios_deploy # Or specify URL and branch ios.ios_deploy_url = https://github.com/phonegap/ios-deploy ios.ios_deploy_branch = 1.10.0 # (bool) Whether or not to sign the code ios.codesign.allowed = false
已尝试操作
- 查看logcat日志,未找到崩溃相关具体错误
- 确认buildozer.spec中已声明
INTERNET和WRITE_EXTERNAL_STORAGE权限 - 验证requirements包含kivy、kivymd、pytube、google-api-python-client等依赖
- 确认APK安装过程无报错或警告
解决建议
1. 修复URL格式错误
on_video_selected方法里的YouTube URL被错误添加了方括号,导致pytube无法解析:
# 错误写法 youtube_url = f"[https://www.youtube.com/watch?v={video_id}]" # 修正为 youtube_url = f"https://www.youtube.com/watch?v={video_id}"
2. 主线程避免网络操作
Android禁止在主线程执行网络请求,否则会触发崩溃或ANR。需要将搜索、下载逻辑放到子线程,更新UI时切回主线程:
import threading from kivy.clock import Clock # 重构搜索方法 def perform_search(self, instance): query = self.search_bar.text if not query.strip(): return threading.Thread(target=self._search_thread, args=(query,), daemon=True).start() def _search_thread(self, query): api_key = '<my-api-key>' # 补全缺失的闭合> youtube = build('youtube', 'v3', developerKey=api_key) request = youtube.search().list(part="snippet", maxResults=50, q=query) response = request.execute() results = [] for item in response['items']: if item['id']['kind'] == "youtube#video": results.append({ 'text': item['snippet']['title'], 'video_id': item['id']['videoId'] }) # 切回主线程更新列表 Clock.schedule_once(lambda dt: setattr(self.results_list, 'data', results)) # 重构下载方法 def on_video_selected(self, video_id): threading.Thread(target=self._download_thread, args=(video_id,), daemon=True).start() def _download_thread(self, video_id): youtube_url = f"https://www.youtube.com/watch?v={video_id}" youtube = YouTube(youtube_url) download_dir = os.path.join(App.get_running_app().user_data_dir, 'downloads') os.makedirs(download_dir, exist_ok=True) youtube.streams.filter(only_audio=True).first().download(download_dir) # 切回主线程显示对话框 Clock.schedule_once(lambda dt: self._show_dialog()) def _show_dialog(self): dialog = MDDialog(title='Finished Downloading', text='Enjoy your music!') dialog.open()
3. 修正API_KEY语法错误
代码中api_key = '<my-api-key'缺失了闭合的>,会导致API调用失败,补全为api_key = '<my-api-key>'(替换为你的实际API密钥)。
4. 优化buildozer.spec配置
- 指定依赖版本:避免兼容性问题,比如:
requirements = python3,kivy>=2.1.0,kivymd>=1.1.1,pytube>=15.0.0,google-api-python-client>=2.100.0,pycryptodome - 补充权限:添加必要的存储和网络状态权限:
android.permissions = INTERNET,WRITE_EXTERNAL_STORAGE,READ_EXTERNAL_STORAGE,ACCESS_NETWORK_STATE - 修正资源路径:如果
download.png和main.py在同一目录,将启动图和图标路径改为相对路径:presplash.filename = download.png icon.filename = download.png - 启用Python日志:便于排查问题:
android.logcat_filters = *:S python:D
内容的提问来源于stack exchange,提问作者Wioletta
相关产品推荐
相关产品推荐

