恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Kivy项目数据持久化:SQLite3选型与实战经验

  • 首页
  • 资讯中心
  • /
  • Kivy项目数据持久化:SQLite3选型与实战经验

相关资讯

Spring Boot 2 + Vue 3 + MyBatis-Plus + MySQL 8.0办公管理系统实战解析 2026/10/20 16:51:54
iOS逆向分析实战:拆解直播间送礼功能完整链路 2026/10/20 16:51:54
EDC系统完整源码解析:从数据模型到审计追踪的临床数据管理 2026/10/20 16:46:54

最新资讯

CentOS 7无外网环境离线安装Docker完整指南
AI私人助理本地部署实战:从零搭建到进阶配置的完整指南
金智科技SCL工具:从SCD解析到虚端子自动核对
Selenium Web自动化测试学习路径:从脚本到稳定落地
MNN llm_demo深度解析:Omni多模态模型端侧推理全链路
前端上下文模式指南:从透传地狱到Provider共享的工程实践

今日推荐

高频变压器实战经验总结:磁芯选型、绕组工艺与故障排查全解析
U-Boot移植的关键:深入理解Kconfig配置系统与依赖关系
步进电机原理与调试全解析:从步距角到丢步排查

本周热门

基于GMM的语音识别系统原理与MATLAB实现详解
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

Kivy项目数据持久化:SQLite3选型与实战经验

发布时间:2026/10/20 16:51:54
Kivy项目数据持久化:SQLite3选型与实战经验 1. 数据存储方案选型为什么 Kivy 项目最终选了 SQLite31.1 那些年踩过的存储坑做 Kivy 跨平台应用最先遇到的不是界面写不出来而是数据不知道该往哪儿放。我做过好几个练手项目最开始图省事直接用一个 Python 列表或者字典在内存里存数据程序一关数据全没了。后来改进成写 JSON 文件用json.dump和json.load读写结果数据量一大每次启动要把整个文件全部读进内存界面上做个搜索都要遍历一整遍越来越卡。更要命的是多用户同时写的时候文件会互相覆盖我亲眼见过辛苦记了一周的账目被一次异常退出清空。后来又试过用 CSV 存数据更麻烦。字段一旦有换行或者逗号解析就会出问题而且查一条记录要把文件从头到尾读一遍效率低得离谱。那段时间我最大的感受就是桌面应用随便折腾文件存储勉强凑合但 Kivy 应用是要上手机的手机的存储写入频率和稳定性要求比电脑高得多文件方案根本不靠谱。1.2 SQLite3 的优势与适用边界SQLite3 是我绕了一圈之后定下来的方案原因很简单它是 Python 标准库自带的import sqlite3就能用不需要额外装第三方依赖这对做 APK 打包来说是巨大的优势少一个纯 C 扩展就少一堆打包报错。它的核心是一个嵌入式的、单文件的关系型数据库整个数据库就是磁盘上的一个文件但你可以在它上面执行标准的 SQL 语句拥有完整的表结构、索引、事务支持。打个比方把 JSON 文件方案比作在一个巨大的纸箱里翻找东西每次都要把纸箱倒出来找那 SQLite3 就是把东西整理进带标签的档案柜每个柜子有独立的抽屉表每个抽屉里有索引目录你想找某一条记录直接按条件去取速度快一个数量级。而且它支持事务批量写入的时候要么全部成功、要么全部回滚不会再出现写了一半文件损坏的情况。对 Kivy 项目来说SQLite3 的适用场景非常明确本地单机数据持久化比如记账本、待办清单、日记、收藏夹、离线缓存。Kivy 是客户端框架大部分应用的数据都是用户自己产生的本地数据不需要真正的网络数据库服务SQLite3 恰恰是这种场景的标准答案。在我这个某账单管理 Demo项目编号 025里我只是用它记录用户每天的收入支出几十条到几千条的量级完全够用。1.3 什么情况下不建议用 SQLite3SCLite3 也不是万能药先说清楚边界免得有人盲目照搬。如果你的应用数据量达到百万级、需要多用户并发写入、需要精确的权限控制SQLite3 就不合适了它本质上还是单机文件数据库并发写同一个数据库文件时会碰到锁机制吞吐量远不如真正的数据库服务。另外如果你的数据是结构化的松耦合数据、需要跨平台同步那 SQLite3 只能做本地缓存云端同步还是要后端数据库配合。在这个项目 025 里我把判断标准写得很简单数据只在这台设备上用、不需要多人协作、单次查询目标是少量行只要满足这三个条件SQLite3 就是最优解。2. 环境准备与项目骨架搭建2.1 依赖安装与版本组合Kivy 项目跑 SQLite3 其实不需要额外安装数据库驱动因为 Python 标准库自带这一点对新手特别友好。我做这个项目时的环境是 Python 3.10、Kivy 2.2.1、Buildozer 1.4.0这几者组合在 Windows 和 Ubuntu 上跑得都很稳。装 Kivy 的常规做法是创建虚拟环境后执行pip install kivy需要装打包工具再执行pip install buildozer。有个细节值得注意Kivy 官方包在 Android 上是自带sqlite3模块的因为 Python-for-Android (p4a) 默认就把 SQLite 编译进去了。但如果你用的是某些精简版 Python 环境可能要确认一下sqlite3是否可用简单验证方式是跑一句代码import sqlite3 print(sqlite3.sqlite_version)能输出版本号就说明没问题。我在项目开头就写了这么一段自检代码省得后面 debug 半天发现是环境缺模块。2.2 项目目录结构整个项目 025 的结构我是这样安排的分工清晰数据库相关代码单独放一个模块界面代码与数据库代码互相隔离kivy025_sqlite/ ├── main.py # Kivy 入口UI 界面逻辑 ├── database.py # SQLite3 数据库管理类 ├── kv/ │ └── main.kv # Kivy 布局文件 ├── data/ │ └── app.db # 数据库文件运行时自动生成 └── buildozer.spec # APK 打包配置这么做最大的好处是如果以后想把 SQLite3 换成别的存储方案只需要改database.py界面代码完全不动。UI 层和持久化层分离这在 Kivy 小项目里尤其容易被忽略但一旦项目复杂起来你就会感谢当时的分层设计。2.3 数据库文件放置路径新手踩过最多的地方就是文件路径。Kivy 应用在电脑上跑的时候当前工作目录是项目根目录但在 Android 手机上应用的私有数据目录是在/data/data/包名/files之类的地方你直接把数据库文件放在项目根目录手机上跑起来文件写不进去或者找不到。正确的做法是使用 Kivy 提供的App.get_running_app().user_data_dir它会自动返回应用专属的数据目录适配了各个平台。我代码里是这样写的import os from kivy.app import App def get_db_path(): app App.get_running_app() data_dir app.user_data_dir if not os.path.exists(data_dir): os.makedirs(data_dir) return os.path.join(data_dir, bill.db)这段代码在桌面端会指向用户主目录下的某个应用文件夹在 Android 端会自动映射到应用私有目录一条代码解决跨平台路径问题。我在项目 025 里所有数据库连接都通过这个函数拿路径没有再手写过一次硬编码的绝对路径。3. 数据层设计与 CRUD 实现3.1 表结构设计项目 025 是一个账单流水记录应用我设计的表结构很简单但字段的取舍是认真想过的CREATE TABLE IF NOT EXISTS bills ( id INTEGER PRIMARY KEY AUTOINCREMENT, category TEXT NOT NULL, amount REAL NOT NULL, note TEXT DEFAULT , created_at TIMESTAMP DEFAULT (datetime(now, localtime)) );id是自增主键用来唯一标识每条流水category存账单分类比如吃饭、交通、购物amount存金额用 REAL 类型这个选择后面会细说note存备注允许为空created_at存创建时间默认值用 SQLite 的datetime(now, localtime)自动记录本地时间省得在代码里手动传时间戳。这里我有个教训金额字段千万不要用 FLOAT 直接累加会出精度问题0.1 加 0.2 可能变成 0.30000000000000004。为什么因为浮点数在计算机里本身就是近似存储。如果这个项目要精确计算应该把金额单位设计成“分”用 INTEGER 存整数显示的时候再除以 100。项目 025 我偷懒用了 REAL实际跑下来日常记录问题不大但如果你做的是财务类应用建议直接用整数分不要走弯路。3.2 数据库连接管理类写一个数据库管理类比在界面代码里到处散落sqlite3.connect要科学得多。我的database.py长这样import sqlite3 import threading class BillDatabase: def __init__(self, db_path): self.db_path db_path self._init_table() def _get_conn(self): conn sqlite3.connect(self.db_path) conn.row_factory sqlite3.Row return conn def _init_table(self): conn self._get_conn() try: conn.execute(CREATE TABLE IF NOT EXISTS bills (...)) conn.commit() finally: conn.close()有个关键点我要单独拎出来说_get_conn()里设置了row_factory sqlite3.Row这样查询结果可以像字典一样用列名取值比如row[amount]而不是row[0]代码可读性高非常多。还有每个方法内部都自行连接、自行关闭不共享连接对象因为 SQLite3 连接对象在多线程环境下共享是很容易出问题的后文会专门讲。3.3 查询与参数化防注入增删改查我全封装成了方法比如添加一条账单def add_bill(self, category, amount, note): conn self._get_conn() try: conn.execute( INSERT INTO bills (category, amount, note) VALUES (?, ?, ?), (category, amount, note) ) conn.commit() return conn.execute(SELECT last_insert_rowid()).fetchone()[0] finally: conn.close()注意这里用了?占位符把值作为参数传进去而不是用字符串拼接拼 SQL。这是我反复强调的安全底线任何用户输入直接拼进 SQL 语句里等于把门敞开了让人注入比如输入一句恶意 SQL你的整个表都可能被删掉。用参数化查询之后SQLite 会自动把输入转义成纯数据不再被认为是 SQL 指令。查询和删除也是类似的写法无非是SELECT ... WHERE和DELETE FROM ... WHERE。需要特别注意的是每次commit()都要写在finally之前的正确位置写完数据不 commit下次连接关闭的时候改动会丢失。我见过好几个新手在这一点上困惑很久明明 insert 了重开程序数据又没了十有八九是没 commit 或者 commit 了但连接没对上。4. Kivy 界面与数据库联动实战4.1 界面布局与数据展示界面层我用main.py搭配main.kv文件写UI 分成两个区域上方是一个输入区包含分类、金额、备注三个输入框和一个“添加”按钮下方是一个RecyclerView或带滚动效果的列表实时展示数据库里的所有流水。这个布局就是记账类 App 的典型结构代码不复杂但 UI 和数据之间的联动是关键。main.kv里的大体结构是BoxLayout: orientation: vertical BoxLayout: size_hint_y: 0.2 TextInput: id: category_input hint_text: 分类 TextInput: id: amount_input hint_text: 金额 TextInput: id: note_input hint_text: 备注 Button: text: 添加 on_release: app.add_bill() RecyclerView: id: bill_list界面是 UI 线程的活数据库是 IO 活两者不能混在一起写。我所有的 UI 控件只负责收集输入和显示结果真正的数据库操作全部在app.add_bill()方法里调用database.py的接口界面和持久化用事件方法连接起来。4.2 事件绑定与增删改查联动写一个界面方法接收TextInput的内容调用数据库接口再刷新列表数据def add_bill(self): db BillDatabase(get_db_path()) category self.root.ids.category_input.text.strip() amount_text self.root.ids.amount_input.text.strip() note self.root.ids.note_input.text.strip() if category and amount_text: try: amount float(amount_text) db.add_bill(category, amount, note) self.refresh_bill_list() except ValueError: # 金额输入了非数字 pass刷新列表时查询全部记录把结果转成 Kivy 的列表数据def refresh_bill_list(self): db BillDatabase(get_db_path()) rows db.get_all_bills() data [] for row in rows: data.append({ text: f{row[category]} - {row[amount]}元 - {row[note]}, }) self.root.ids.bill_list.adapter.data data如果用的不是RecyclerView而是ListView要注意adapter.data赋值后要重新触发视图刷新否则界面上不会变。我最开始写的时候漏了这个数据库里明明 insert 了界面死活不更新还以为是没写进去直到我 printf 调试发现数据库确实有数据才想到是视图没刷新。4.3 实时统计与删除功能的扩展只有添加没有删除的记账本是不完整的我在项目 025 里加了长按删除RecyclerView的条目被长按时弹出一个确认框用户点了确认就按记录 id 删除def delete_bill(self, bill_id): db BillDatabase(get_db_path()) db.delete_bill(bill_id) self.refresh_bill_list()这个功能看着简单但触发逻辑有个细节列表的条目事件回调里要能拿到当前条目的 id所以我在刷新列表时把id也塞进了数据项里显示的时候只展示文字事件触发时拿 id 去删记录避免用删除按钮自己的绑定数据去拼删除条件。另外我还加了总金额统计使用 SQL 聚合函数SELECT COALESCE(SUM(amount), 0) AS total FROM billsCOALESCE是防止表是空的这样查出来是 0 而不是 NULL省得界面上显示一个奇怪的空值。这个统计方法每次刷新列表时一起调用金额变化后统计数字同步变化效果很直观。5. 打包与多线程避坑指南5.1 Android 打包时 SQLite3 模块的处理Kivy 应用最终要打包成 APK 安装到手机上Buildozer 在打包时会通过 Python-for-Android 编译 Python 解释器和依赖模块SQLite3 是 p4a 内置编译的模块正常情况下不需要额外配置。但如果你的代码里用了某些不常见的 SQLite 特性比如扩展函数、FTS 全文搜索要确认 p4a 的 SQLite 编译开关是否打开默认的编译参数可能不包含这些扩展。以前我试过在 Android 端直接打开电脑端生成的数据库文件结果报错提示 database disk image is malformed后来发现是文件没有正确复制过去。正确做法是数据库文件第一次启动时在应用私有目录创建如果要预置数据打包前把数据库文件放进 Kivy 的资源目录首次启动时做一次性拷贝不要直接去打开打包在 APK 里的只读文件。5.2 多线程访问 Shared 连接与锁机制Kivy 界面跑在主线程如果你在后台线程里做数据库操作要注意 SQLite 连接不能简单共享。SQLite3 默认的连接对象check_same_threadTrue意味着这个连接只能被创建它的线程使用其他线程一调就会抛出异常。我把这个问题拆成两层解决第一层每个线程都创建自己的连接用完就关第二层如果实在要共享连接创建时传check_same_threadFalse但这时你必须自己做好锁控制conn sqlite3.connect(db_path, check_same_threadFalse)不过说实话对项目 025 这种量级的应用完全没有必要引入多线程。Kivy 的界面刷新和数据库操作都在主线程执行最坏情况就是大数据量插入时界面稍微卡顿一下本地数据量小体验并不差。给 SQLite 加线程只会引入更多踩坑点比如两个线程同时写一条记录你得处理database is locked异常得不偿失。5.3 耗时操作与 UI 响应但如果你做的是批量导入几百条数据主线程执行确实会卡住界面这时可以用 Kivy 的Clock.schedule_once分块执行或者用kivy.clock.Clock的调度机制把大的数据库操作拆成小批次每次处理几十条给界面留出刷新时间。我在一个批量导入功能里就是这么干的实测下来界面掉帧从原来的好几秒降到几乎没有。另一个常见坑是异步回调里更新 UI。用了线程或异步操作回调函数运行在后台线程直接修改 Kivy 控件属性会引发界面状态不一致必须通过Clock.schedule_once把 UI 更新动作切回主线程。原则很简单数据库操作可以放后台UI 更新永远回主线程。6. 常见问题速查与排查技巧实录6.1 database is locked 是什么情况SQLite 的锁机制是应对并发读写的database is locked这个错误最常见的原因有两个一是另一个连接持有了写锁没有释放比如你开了个事务执行到一半忘记 commit 或 rollback二是有个长查询没读完就尝试写入了。排查思路是检查所有execute后是否都关闭了连接或提交了事务我习惯写成try/finally确保任何异常下连接都能关闭。还有一种隐蔽场景在 Windows 上杀毒软件可能会短暂锁定新创建的数据库文件如果应用启动时正好在初始化表结构就会偶发报锁。遇到这种情况重试机制是有效的网上有封装好的重试装饰器遇到锁异常就等待几十毫秒再试一次。6.2 列表不刷新或者数据混乱这个问题我在 4.2 提到过是最容易被忽略的。你 insert 了数据库但列表还是旧的大概率是adapter.data更新后没有触发数据刷新。RecyclerView的 adapter 有data属性和notify_data_changed()方法数据的增删改查完成后记得正确赋值或者主动调用刷新接口。如果数据出现重复比如刷新后原来的一条记录变成了两条多半是界面初始化时序的问题on_start和__init__里都调了refresh_bill_list()或者on_enter和on_start重复执行。解决办法是统一只保留一个数据加载入口比如只在on_start里刷新其他地方都通过事件通知来触发刷新。6.3 字段类型不匹配与查询失效用TextInput拿到的全是字符串直接拿去和数据库里的 INTEGER 或 REAL 比较SQLite 的类型亲和性可能导致无法命中索引或匹配失败。比如你用WHERE id 3通常没问题因为 SQLite 会做类型转换但如果你存的是 TEXT 的日期字符串用日期函数去查询就可能匹配不上。稳妥做法是在代码层把输入转换成目标类型再传给 SQL 参数。还有一个真实的坑created_at用了datetime(now, localtime)如果你查询时用日期字符串去比较要注意格式完全一致差一个空格或者时区分隔符都会查不出来。我排查过一次“明明按日期查不到数据”的问题最后发现是本地时间和 UTC 的时区偏移没对齐浪费了两个小时。6.4 升级表结构时旧数据丢失项目做到一半你会遇到需要加字段的情况比如给账单表加一个“是否已同步”的标记。CREATE TABLE IF NOT EXISTS不会改变已有表的结构直接执行ALTER TABLE才能加字段ALTER TABLE bills ADD COLUMN sync_status INTEGER DEFAULT 0ALTER TABLE不支持的场景需要另想办法比如删掉字段或改约束条件。我的建议是写一个小的迁移管理机制在数据库类里记录当前表结构版本号每次打开数据库时检查版本按序执行迁移 SQL版本号存在一个PRAGMA user_version字段里这样以后更新表结构不会丢数据。6.5 文件损坏与备份恢复SQLite 单文件方案如果写入中断有可能损坏文件。概率不高但手机上用着用着突然没电或者应用被强制杀掉理论上都有风险。我会在每次启动时做个简单的完整性检查conn.execute(PRAGMA integrity_check)如果发现异常就把损坏文件备份成带时间戳的坏文件再新建一个干净的库。同时每周往存储里导出一份 JSON 备份算是廉价但实用的灾难恢复方案。写在最后的经验汇总这个项目 025 做完我自己最大的感受是Kivy 的 UI 部分反而比数据库简单真正容易出问题的是两者结合的工程细节比如路径、线程、刷新时机。如果你也在做类似的应用我给几个优先级最高的建议数据库操作用参数化 SQL不要拼字符串数据库路径用user_data_dir不要硬编码每个连接用try/finally保证关闭列表刷新记得触发 adapter 更新打包前先确认目标平台的sqlite3可用。把这些基础打牢SQLite3 在你的 Kivy 项目里会变成一个非常可靠的后端让你把精力放在功能本身而不是补数据漏洞上。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号