戰(zhàn)全攻略:用Python打造跨平臺移動(dòng)應(yīng)用與APK打包)
做跨平臺移動(dòng)應(yīng)用開發(fā)這些年我試過的框架不算少。Flutter、React Native、uni-app都走過一遍流程但最后真正讓我愿意沉下心來做完整項(xiàng)目的反而是Kivy——一個(gè)用Python寫UI、一套代碼能跑Android和iOS的框架。很多人一聽Python寫App第一反應(yīng)是性能不行、打包太大、界面丑說實(shí)話這些吐槽早期都成立但Kivy從1.9到2.x這幾個(gè)版本的迭代已經(jīng)把很多坑填平了。今天這篇就當(dāng)是掏心窩子的經(jīng)驗(yàn)貼把我從入門到做出一個(gè)能上架的原生APK過程中踩過的坑、總結(jié)出的套路一次性講清楚。這篇文章適合誰如果你已經(jīng)會一點(diǎn)Python基礎(chǔ)語法又想快速把想法變成手機(jī)上的App或者你跟我一樣受夠了為一個(gè)簡單的工具類應(yīng)用去單獨(dú)學(xué)Java/Kotlin/Swift那Kivy這條路值得你花半小時(shí)看完。我不僅會講Hello World還會告訴你布局怎么設(shè)計(jì)才不卡、打包時(shí)哪些坑是必踩的、以及為什么說Kivy的Canvas比一堆現(xiàn)成控件更值錢。1. 為什么選Kivy跨平臺方案的一次務(wù)實(shí)取舍1.1 Kivy能做什么解決什么問題Kivy是一個(gè)開源的Python GUI框架核心賣點(diǎn)就是一套代碼多端運(yùn)行。你寫好的邏輯和界面可以直接跑在Windows、macOS、Linux桌面也能打包成Android的APK和iOS的IPA。對我這種后端出身、日常拿Python寫腳本的人來說這幾乎是唯一能讓我不用換語言就能交付移動(dòng)端項(xiàng)目的方案。它的底層基于OpenGL ES 2.0做渲染界面不是用原生控件堆出來的而是自己畫出來的。這意味著同一套UI在Android和iOS上長得一模一樣不會出現(xiàn)那種“Android上好好的iOS上按鈕位置歪了”的適配噩夢。你也可以把它理解成“Python界的Flutter”——自帶渲染引擎不依賴系統(tǒng)控件所以跨端一致性特別好。它能解決的問題很明確快速驗(yàn)證MVP一個(gè)小工具、內(nèi)部管理應(yīng)用、給客戶演示的原型用Kivy一兩天就能端到端跑起來比原生快太多。Python生態(tài)復(fù)用你已有的Python業(yè)務(wù)邏輯、算法、爬蟲代碼直接嵌進(jìn)App里不用像別的框架那樣得用JNI或者重寫一遍。桌面向移動(dòng)端平滑過渡先在PC上調(diào)試UI和邏輯最后再打包到手機(jī)開發(fā)效率翻倍。1.2 跟其他跨平臺框架比Kivy的取舍在哪里很多人喜歡拿Kivy跟Flutter、React Native做對比。我的看法是它們根本不是一個(gè)賽道上的產(chǎn)品。Flutter用的是Dart語言性能確實(shí)好UI也精致但你要學(xué)一門新語言而且Flutter的包體積動(dòng)輒20MB起步。React Native和uni-app本質(zhì)是橋接原生控件渲染性能依賴系統(tǒng)組件調(diào)試時(shí)經(jīng)常要處理原生層和JS層的通信問題。Kivy則是走了一條完全不同的路所有控件都是自己繪制的語言是Python布局用聲明式的kv文件一套代碼直接從桌面端跑到移動(dòng)端開發(fā)鏈路非常統(tǒng)一。代價(jià)也很明顯App包體積偏大一個(gè)空殼APK基本在15MB左右加了依賴很容易上30MB。啟動(dòng)速度不如原生和Flutter因?yàn)镻ython解釋器要先初始化但合理優(yōu)化后完全在可接受范圍內(nèi)。復(fù)雜動(dòng)畫和大量列表場景性能需要專門調(diào)優(yōu)不能像寫原生那樣隨手懟。所以我的結(jié)論是如果你要做的App是業(yè)務(wù)邏輯復(fù)雜、UI偏表單和列表、需要快速上線驗(yàn)證的Kivy非常適合。如果你要做的重度游戲或者對啟動(dòng)速度有極致要求的產(chǎn)品那還是老老實(shí)實(shí)Flutter或者走原生。工具沒有絕對的好壞關(guān)鍵看你拿它做什么。2. 環(huán)境準(zhǔn)備與第一個(gè)Kivy應(yīng)用2.1 安裝配置與項(xiàng)目骨架搭建先強(qiáng)調(diào)一點(diǎn)Kivy的安裝強(qiáng)烈建議用虛擬環(huán)境別直接懟進(jìn)系統(tǒng)Python里。因?yàn)镵ivy的依賴涉及到Cython的編譯版本沖突會搞得人想砸電腦。# 創(chuàng)建虛擬環(huán)境Windows/macOS/Linux通用 python -m venv kivy_env # 激活環(huán)境 # Windows: # kivy_env\Scripts\activate # macOS/Linux: # source kivy_env/bin/activate # 安裝Kivy pip install kivy # 如果需要打包Android APK需要額外安裝buildozer pip install buildozer一個(gè)標(biāo)準(zhǔn)的Kivy項(xiàng)目骨架我建議按下面這個(gè)結(jié)構(gòu)組織my_app/ ├── main.py # 程序入口App類定義 ├── myapp.kv # 界面布局文件和main.py同名不同后綴 ├── buildozer.spec # 打包配置文件由buildozer init生成 ├── assets/ │ ├── fonts/ # 自定義字體 │ └── images/ # 圖標(biāo)和圖片資源 └── libs/ # 自定義Python模塊main.py的最小可運(yùn)行版本長這樣from kivy.app import App from kivy.uix.label import Label class MyApp(App): def build(self): return Label(textHello, Kivy!) if __name__ __main__: MyApp().run()跑起來之后你會看到一個(gè)窗口里面居中顯示了這行文字。Kivy的Window默認(rèn)是800x600按F11可以全屏。到這里你的第一個(gè)Kivy應(yīng)用就算活了。2.2 理解kv語言界面和邏輯分離的哲學(xué)Kivy最好用的一個(gè)設(shè)計(jì)就是kv語言。它相當(dāng)于一個(gè)專門描述界面的DSL讓界面布局和Python邏輯代碼徹底分離有點(diǎn)像HTML之于JavaScript。寫多了你會發(fā)現(xiàn)這種分離對后期維護(hù)的幫助非常大——改界面結(jié)構(gòu)的時(shí)候你根本不需要?jiǎng)覲ython代碼只要改kv文件保存后界面自動(dòng)熱更新??匆粋€(gè)簡單的kv示例# myapp.kv MyRoot: BoxLayout: orientation: vertical Label: text: 歡迎使用Kivy font_size: 24sp Button: text: 點(diǎn)擊我 on_press: root.on_button_click()你的main.py對應(yīng)改成from kivy.app import App from kivy.uix.boxlayout import BoxLayout class MyRoot(BoxLayout): def on_button_click(self): print(按鈕被點(diǎn)擊了) class MyApp(App): def build(self): return MyRoot() if __name__ __main__: MyApp().run()注意看幾個(gè)關(guān)鍵點(diǎn)kv文件中的MyRoot表示該規(guī)則作用于這個(gè)類。BoxLayout是Kivy最常用的布局容器還有AnchorLayout、GridLayout、StackLayout等按場景選。on_press: root.on_button_click()里的root指的是當(dāng)前規(guī)則對應(yīng)的根組件。這套寫法的核心思想就是“視圖歸視圖邏輯歸邏輯”。哪怕你后面把界面復(fù)雜度翻十倍main.py照樣清爽這也是Kivy項(xiàng)目能做到大型化的基礎(chǔ)。3. 核心細(xì)節(jié)解析布局、事件與常用控件3.1 布局系統(tǒng)自適應(yīng)不同屏幕尺寸的底層邏輯Kivy的布局系統(tǒng)我認(rèn)為是它最值得下功夫研究的部分。移動(dòng)端碎片化嚴(yán)重屏幕尺寸從4寸到10寸都有如果布局寫死像素值換臺設(shè)備直接翻車。Kivy的布局核心思路是相對位置相對尺寸。比如BoxLayout會根據(jù)子組件的size_hint屬性按比例分配空間而不是固定像素值??催@個(gè)例子BoxLayout: orientation: horizontal Button: text: 左 size_hint_x: 0.3 Button: text: 右 size_hint_x: 0.7兩個(gè)按鈕的水平寬度會按3:7分配不管屏幕是320px還是1080px。這個(gè)設(shè)計(jì)讓應(yīng)用在不同的設(shè)備上天然具備一定的自適應(yīng)能力而不需要像原生Android那樣寫N套dimens資源。不過光有size_hint還不夠?qū)τ谖淖?、圖標(biāo)這類元素建議統(tǒng)一用dp密度無關(guān)像素和sp縮放像素單位來指定尺寸而不是用像素px。dp保證同一物理尺寸在不同DPI的屏幕上看起來一致sp則在dp基礎(chǔ)上額外跟隨用戶系統(tǒng)的字體縮放設(shè)置。3.2 事件綁定從按鈕點(diǎn)擊到自定義事件Kivy的事件體系非常靈活它內(nèi)置了一套事件分派機(jī)制。每個(gè)組件默認(rèn)支持on_touch_down、on_touch_move、on_touch_up三個(gè)觸摸事件同時(shí)通過bind()方法可以綁定任意自定義事件。實(shí)際開發(fā)中最常用的幾種綁定方式# 方式一在kv文件中直接綁定屬性事件 Button: text: 提交 on_press: root.submit() # 方式二在Python中使用bind方法 submit_btn Button(text提交) submit_btn.bind(on_pressroot.submit) # 方式三繼承Button重寫on_release class CustomButton(Button): def on_release(self): super().on_release() # 自定義邏輯這里有個(gè)經(jīng)驗(yàn)之談能用on_release就不要用on_press。因?yàn)橐苿?dòng)端觸摸有滑動(dòng)取消的場景——用戶手指按上去又滑走通常意味著他改變了主意這時(shí)候不應(yīng)該觸發(fā)點(diǎn)擊邏輯。on_release只有在手指在Button區(qū)域內(nèi)抬起時(shí)才觸發(fā)更符合移動(dòng)端的交互預(yù)期。還有個(gè)大坑是組件尺寸為0時(shí)點(diǎn)擊不到。很多新手用BoxLayout包了一個(gè)只有size_hint沒有實(shí)際內(nèi)容的容器發(fā)現(xiàn)怎么點(diǎn)都沒反應(yīng)檢查一下是否給容器設(shè)置了合適的大小或者加了background_color/canvas內(nèi)容撐起尺寸。3.3 常用控件與自定義樣式擺脫“Kivy丑”的魔咒網(wǎng)上說“Kivy丑”的人大多是直接用默認(rèn)樣式寫完就下定論。實(shí)際Kivy能做的界面遠(yuǎn)遠(yuǎn)被低估了。它支持用canvas指令繪制任意形狀、任意顏色支持完全自定義的控件主題。舉個(gè)例子默認(rèn)的Button是一個(gè)灰色矩形我們想做一個(gè)圓角漸變背景的按鈕可以這樣CustomButtonButton: background_color: (0, 0, 0, 0) # 去掉默認(rèn)背景 canvas.before: Color: rgba: 0.2, 0.6, 0.9, 1 RoundedRectangle: pos: self.pos size: self.size radius: [20, 20, 20, 20]canvas.before、canvas、canvas.after分別對應(yīng)在繪制組件之前、自身內(nèi)容、之后繪制。用這套機(jī)制你可以給任何控件畫陰影、畫邊框、畫背景圖甚至是畫一個(gè)動(dòng)態(tài)的進(jìn)度條。想要界面不丑熟用canvas是關(guān)鍵這比研究幾十個(gè)自帶控件的屬性都管用。4. 實(shí)操過程從零打包一個(gè)Android APK4.1 完整打包流程buildozer從配置到出包講完UI和邏輯接下來是Kivy開發(fā)中最容易勸退新手的環(huán)節(jié)——打包APK。桌面端運(yùn)行得好好的代碼一鍵打包卻可能報(bào)幾十個(gè)錯(cuò)。這里我給你一份排過雷的完整流程。首先初始化buildozer配置# 在項(xiàng)目根目錄執(zhí)行 buildozer init這會生成一個(gè)buildozer.spec文件里面有很多配置項(xiàng)重點(diǎn)要改這幾處# 包名必須是倒置域名格式 package.name myapp package.domain org.example # 源碼目錄默認(rèn)是.我建議改成app避免把build工具目錄也打包進(jìn)去 source.dir . # 入口文件 source.main main.py # Android權(quán)限按需添加 android.permissions INTERNET, VIBRATE # Python版本打包工具對版本敏感必須匹配 android.api 33 android.minapi 21 android.ndk_api 24 python3.version 3.11 # 圖標(biāo)和啟動(dòng)畫面 icon.filename %(source.dir)s/assets/images/icon.png presplash.filename %(source.dir)s/assets/images/splash.png配置文件準(zhǔn)備好之后執(zhí)行打包命令buildozer -v android debug第一次打包會下載Android SDK、NDK、Python-for-Android等一堆工具耗時(shí)比較久大概需要三十分鐘到一個(gè)小時(shí)取決于網(wǎng)絡(luò)。之后再進(jìn)行增量打包通常幾分鐘就能完成。生成的APK在bin/目錄下。4.2 打包必踩的坑與解決方案打包過程中有幾乎每個(gè)新手都會遇到的問題我摔過很多次把典型問題和對應(yīng)的解決辦法整理成了一張速查表常見報(bào)錯(cuò)/問題根本原因解決方案unable to find platformSDK平臺版本沒裝全運(yùn)行buildozer android clean后重新打包或者手動(dòng)修改spec里的android.api為本地已安裝的版本Cython not found打包環(huán)境沒有安裝Cython在虛擬環(huán)境執(zhí)行pip install cython打包后App閃退通常是第三方庫不兼容查看logcat日志adb logcatAPK體積過大默認(rèn)打包了全ABI架構(gòu)在spec中設(shè)置android.archs arm64-v8a體積能縮小近60%中文顯示亂碼/方塊默認(rèn)字體不支持中文下載中文字體如思源黑體放到assets/fonts/在main.py中全局設(shè)置字體關(guān)于中文字體貼上這個(gè)代碼片段很多人都會用到from kivy.core.text import LabelBase LabelBase.register(nameChineseFont, fn_regularassets/fonts/SourceHanSansCN-Regular.otf) # 然后在kv文件中全局約束字體族 # Widget: # font_name: ChineseFont4.3 性能優(yōu)化讓Kivy應(yīng)用跑得更順說到Kivy的性能有幾個(gè)我自己實(shí)測下來效果顯著的優(yōu)化點(diǎn)分享給可能踩坑的你。第一列表數(shù)據(jù)多時(shí)不要用ScrollView直接懟組件。Kivy有一個(gè)RecycleView它是專門為大數(shù)據(jù)量列表優(yōu)化的復(fù)用機(jī)制類似Android的RecyclerView。實(shí)測在1000條數(shù)據(jù)量下ScrollView會明顯掉幀RecycleView還能流暢滑動(dòng)??匆粋€(gè)最小改法from kivy.uix.recycleview import RecycleView class MyListView(RecycleView): def __init__(self, **kwargs): super().__init__(**kwargs) self.data [{text: f第{i}項(xiàng)} for i in range(1000)]配套的kv文件MyListView: viewclass: Label RecycleBoxLayout: default_size: None, dp(56) default_size_hint: 1, None size_hint_y: None height: self.minimum_height orientation: vertical第二圖片資源必須壓縮。Kivy加載大圖會直接吃滿內(nèi)存一個(gè)1920x1080的PNG可能占據(jù)幾十MB內(nèi)存。實(shí)際開發(fā)中我統(tǒng)一用stretch模式配合壓縮后的圖片資源或者用AsyncImage異步加載網(wǎng)絡(luò)圖片避免UI線程卡頓。第三減少不必要的屬性綁定。bind()綁定越多事件通知開銷越大。尤其不要在on_touch_move里去更新復(fù)雜布局屬性高頻觸發(fā)會帶來嚴(yán)重的性能損耗。碰到實(shí)時(shí)更新場景優(yōu)先考慮用Clock.schedule_once做節(jié)流from kivy.clock import Clock def on_touch_move(self, touch): # 壓縮更新頻率每隔0.05秒最多更新一次 if not hasattr(self, _last_update): self._last_update 0 now Clock.get_time() if now - self._last_update 0.05: self._last_update now self.update_ui(touch.pos)5. 常見問題與排查技巧實(shí)錄5.1 桌面端開發(fā)時(shí)的調(diào)試技巧Kivy桌面端調(diào)試有一個(gè)福音支持自動(dòng)熱重載在Window事件循環(huán)中重載kv文件。你在kv文件里改完樣式按下CtrlR可以快速重載不用整個(gè)程序重啟大大提升調(diào)樣式時(shí)候的效率。另外F1按鍵能在桌面端打開Kivy自帶的Inspector鼠標(biāo)點(diǎn)哪個(gè)控件就能直接看到它的類、屬性、層級關(guān)系。這個(gè)東西調(diào)試布局問題非常好用幾乎等價(jià)于瀏覽器里的DevTools。我在做復(fù)雜布局的時(shí)候基本離不開它。調(diào)試還有個(gè)經(jīng)常被忽略的點(diǎn)config文件里的日志級別。在main.py里可以這樣打開DEBUG日志from kivy.config import Config Config.set(kivy, log_level, debug) Config.set(kivy, log_enable, 1)開啟之后控制臺會輸出每個(gè)控件的觸摸事件、布局計(jì)算、繪制耗時(shí)排查起問題來直觀很多。5.2 真機(jī)調(diào)試時(shí)如何正確獲取日志移動(dòng)端開發(fā)日志是你排查問題的主要依據(jù)。Android上可以用adb logcat查看Kivy的Python異常輸出# 連接手機(jī)開啟USB調(diào)試后執(zhí)行 adb logcat -s python如果App在啟動(dòng)階段就崩潰來不及看日志可以用buildozer的logcat命令直接查看buildozer android logcat真機(jī)調(diào)試最常見的問題是權(quán)限和路徑。Kivy在Android上的App.user_data_dir返回的是應(yīng)用的私有目錄不等于桌面端的當(dāng)前路徑。如果你在桌面端用了相對路徑加載文件到了Android上大概率找不到文件。建議統(tǒng)一用Kivy提供的App.get_running_app().user_data_dir來拼接資源路徑import os from kivy.app import App def get_data_file(filename): data_dir App.get_running_app().user_data_dir return os.path.join(data_dir, filename)5.3 那些文檔不會明說的坑說實(shí)話Kivy的文檔整體不錯(cuò)但有些坑是社區(qū)踩過無數(shù)遍、官方卻只字未提的。我分享三個(gè)印象最深、也最影響開發(fā)的坑。第一個(gè)kv文件中的中文注釋在某些解碼下會報(bào)錯(cuò)。Kivy的kv解析器默認(rèn)使用UTF-8但個(gè)別Windows環(huán)境下的編輯器會把文件保存成GBK導(dǎo)致解析報(bào)錯(cuò)。解決辦法是把kv文件統(tǒng)一保存為UTF-8 without BOM格式。我習(xí)慣用VS Code的“編碼”功能統(tǒng)一處理。第二個(gè)在Android上切換鍵盤時(shí)布局會被頂上去。這個(gè)跟Kivy默認(rèn)的窗口軟輸入模式有關(guān)。在buildozer.spec里可以設(shè)置android.window_soft_input_mode adjustPan或者adjustResize能緩解鍵盤遮擋輸入框的問題。不同手機(jī)廠商還可能有差異適配的時(shí)候要多測幾臺。第三個(gè)Kivy的Clock在App進(jìn)入后臺后行為會變化。如果你用Clock.schedule_interval做定時(shí)器當(dāng)App退到后臺定時(shí)器可能被暫?;蜓舆t回到前臺后又突然補(bǔ)觸發(fā)。這會讓心跳、倒計(jì)時(shí)、輪詢邏輯出問題。穩(wěn)妥的做法是監(jiān)聽on_pause和on_resume用系統(tǒng)時(shí)間差值來做補(bǔ)償。class MyApp(App): def on_pause(self): self._pause_time time.time() return True # 必須返回True才能支持暫停 def on_resume(self): if self._pause_time: diff time.time() - self._pause_time # 把diff加到你的計(jì)時(shí)器累計(jì)值里6. 基于Kivy的進(jìn)階實(shí)戰(zhàn)跨平臺音樂管理系統(tǒng)的設(shè)計(jì)思路Kivy的應(yīng)用場景遠(yuǎn)比很多人想象的寬泛。我來拆解一個(gè)我曾經(jīng)做過的跨平臺音樂管理系統(tǒng)講清楚這個(gè)項(xiàng)目的技術(shù)選型和架構(gòu)組成給想用Kivy做實(shí)操項(xiàng)目的你做一個(gè)完整的參考。這個(gè)系統(tǒng)的核心功能是音樂的掃描、分類、播放與歌單管理需要同時(shí)跑在Windows收銀臺終端和Android移動(dòng)設(shè)備上。在技術(shù)選型時(shí)考慮過Electron Vue但被桌面端體積和內(nèi)存占用勸退考慮過Flutter但生態(tài)里音樂元數(shù)據(jù)解析和ID3庫的成熟度跟Python的mutagen完全沒法比。最終選了Kivy正是因?yàn)樗囊粢曨l支持可以通過pygame、kivy.core.audio等模塊快速集成而Python在元數(shù)據(jù)解析、文件管理、網(wǎng)絡(luò)請求方面有天然優(yōu)勢。來看核心代碼的組織方式。音樂掃描模塊借助Python的os.walk和mutagen庫實(shí)現(xiàn)了對指定目錄的遞歸遍歷import os from mutagen.mp3 import MP3 from mutagen.flac import FLAC SUPPORTED_EXT {.mp3, .flac, .wav, .ogg, .m4a} def scan_music(folder): songs [] for root, dirs, files in os.walk(folder): for file in files: ext os.path.splitext(file)[1].lower() if ext not in SUPPORTED_EXT: continue full_path os.path.join(root, file) info extract_metadata(full_path, ext) songs.append(info) return songs def extract_metadata(path, ext): title, artist, album , , try: if ext .mp3: audio MP3(path) title audio.get(TIT2, ) artist audio.get(TPE1, ) album audio.get(TALB, ) elif ext .flac: audio FLAC(path) title audio.get(title, ) artist audio.get(artist, ) album audio.get(album, ) except Exception as e: pass return {path: path, title: title, artist: artist, album: album}播放模塊我在Kivy自帶的SoundLoader和pygame.mixer之間綜合選型最終項(xiàng)目傾向于SoundLoader因?yàn)樗募虞d接口面向Kivy Widget體系音量控制和事件回調(diào)更順手from kivy.core.audio import SoundLoader class PlayerManager: def __init__(self): self.sound None self.current_index 0 def play(self, path): if self.sound: self.sound.stop() self.sound SoundLoader.load(path) if self.sound: self.sound.play()播放列表的UI則采用RecycleView展示歌單雙擊切換歌曲并自動(dòng)更新封面。打包的時(shí)候在buildozer.spec里加了mutagen和pygame到requirements最終APK運(yùn)行后能同時(shí)完成掃描2000首本地歌曲、按歌手分類、播放列表持久化這三項(xiàng)核心需求。這個(gè)案例說明白了一件事Kivy真正擅長的領(lǐng)域是帶業(yè)務(wù)邏輯的工具型應(yīng)用尤其是需要跟Python生態(tài)深度整合的場景。它不需要你成為前端高手也不需要你懂原生控件只要你會寫Python就能做出一款能在自己手機(jī)上跑起來的App這種體驗(yàn)極具成就感。7. 擴(kuò)展思考Kivy未來的應(yīng)用場景與社區(qū)生態(tài)我對Kivy的判斷是它不會成為Flutter那樣的主流框架但它會在一個(gè)特定生態(tài)位里長期存活凡是Python能解決問題的場景Kivy就是最順手的端側(cè)方案。比如AI模型推理App、教育類小工具、企業(yè)內(nèi)部管理系統(tǒng)、IoT的控制面板——這些場景重邏輯、輕交互動(dòng)畫對包體積不敏感卻是Kivy的理想適用區(qū)。社區(qū)生態(tài)方面Kivy的擴(kuò)展庫雖然沒有前端生態(tài)那么熱鬧但plyer訪問相機(jī)、GPS、傳感器、buildozer打包工具、pyjnius調(diào)用Java API等配套工具鏈相當(dāng)穩(wěn)定。特別是plyer讓Kivy應(yīng)用能方便地調(diào)用手機(jī)硬件能力比如震動(dòng)、通知、獲取剪貼板這些是移動(dòng)應(yīng)用的高頻需求。最后再分享一個(gè)我踩過幾次坑之后養(yǎng)成的小習(xí)慣每次新建Kivy項(xiàng)目我會先去buildozer.spec里把a(bǔ)ndroid.archs設(shè)置成arm64-v8a然后專門建一個(gè)assets/fonts目錄放中文字體再在代碼開頭注冊全局字體。這些動(dòng)作花不了兩分鐘但能避開后面至少一小時(shí)的坑。希望這篇長文能幫你真正跨過Kivy的門檻做出你想要的第一個(gè)應(yīng)用。