记android学习之路----数据持久化之SQLite:用TaoToken统一Key跑通增删改查与事务验证

发布时间:2026/10/8 22:19:40
记android学习之路----数据持久化之SQLite:用TaoToken统一Key跑通增删改查与事务验证 1. 从一次「数据丢了」说起Android SQLite 数据持久化到底解决什么问题刚学 Android 那会儿我写了个记账小 Demo用 ArrayList 存数据界面跑得好好的。结果手机一重启所有记录全没了——那一刻才真正理解什么叫「内存数据不持久」。Android 里数据持久化主要有三条路SharedPreferences 存键值对、文件存流、SQLite 存结构化数据。前两个适合小数据一旦你要做「按条件查、按时间排序、批量更新、事务保证一致性」SQLite 就是绕不开的那一个。SQLite 是 Android 系统自带的关系型数据库轻量、单文件、零配置。一个数据库就是一个.db文件默认躺在data/data/包名/databases/目录下。它支持标准 SQL支持事务支持索引对入门阶段理解「关系型数据库怎么用代码操作」非常友好。你不需要装 MySQL、不需要起服务写几行 Java/Kotlin 就能建库建表。这篇文章聚焦一个完整可跑的落地场景用SQLiteOpenHelper建库建表封装一套 DAO把 insert / query / update / delete 四个操作逐个验证再加一个事务回滚的对照实验。同时调试期如果 Demo 里要调接口比如把本地数据同步到某个服务我会用 TaoToken 统一管理 Key 和 API 通道避免 Key 散落在代码里。适合谁看刚学完 Activity 和 ListView、想搞懂本地存储的 Android 入门同学也适合想复习 SQLite 事务细节的开发者。预期目标很明确跟着做完你能得到一个能运行、能用 Logcat 验证、能自己改表结构的本地持久化 Demo并且知道每个报错大概出在哪。2. 前置准备TaoToken 统一 Key 与 API 通道调试期接口不再乱在动手写 SQLite 之前先把「调试期接口调用」这件事理顺。很多入门 Demo 到后面会加一个「上传/同步」按钮如果直接把 Key 硬编码在Constants.java里一是容易泄露二是换环境要改代码。我的做法是用 TaoToken 做统一入口一个 Key、一个 Base URL模型对话、编码辅助、接口调试都走同一条通道。TaoToken 在这里扮演的是「统一 API 网关」的角色不是数据库本身。SQLite 负责本地持久化TaoToken 负责你在调试阶段需要调用的模型/接口能力。两者职责分开代码结构才干净。先拿到 Key。打开官网https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content注册后在控制台创建 API Key。控制台地址是https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewriteKey 管理页在https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite。创建完复制那串sk-开头的字符串只显示一次先存到本地密码管理器。Base URL 统一用https://taotoken.net/api注意这个地址不带任何查询参数是纯 API 端点。模型 ID 按你实际要用的填比如做代码补全可以用 coding 相关的模型做对话验证用通用对话模型。这三个要素——Base URL、Key、Model ID——就是后面所有配置的「三件套」缺一个都会报 401 或 model not found。如果你用 Claude Code 做辅助编码可以在它的配置里指向 TaoToken 的 Anthropic 兼容入口文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite。想先验证 Key 是否可用直接去模型对话页https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite发一句话能返回就说明通道没问题。长期做编码或 Agent 任务的话Coding Plan 页面在https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite按需选。这里要强调一点TaoToken 是合规的 API 服务入口不要把它和任何网络代理工具混为一谈。我们只是用它来统一管理调试期的接口凭证让 Key 不散落在各个文件里。3. 可复制配置SQLiteOpenHelper 建库建表 DAO 封装 TaoToken 三件套这一节是核心所有代码都可以直接复制。先建一个DbHelper继承SQLiteOpenHelper负责建库建表。public class DbHelper extends SQLiteOpenHelper { private static final String DB_NAME study.db; private static final int DB_VERSION 1; public static final String TABLE_USER t_user; public DbHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } Override public void onCreate(SQLiteDatabase db) { String sql CREATE TABLE TABLE_USER ( _id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER DEFAULT 0, score REAL DEFAULT 0.0, created_at INTEGER); db.execSQL(sql); } Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(DROP TABLE IF EXISTS TABLE_USER); onCreate(db); } }onCreate只在数据库第一次创建时调用所以建表语句放这里。onUpgrade在版本号变大时触发入门阶段直接删表重建最省事生产环境要做数据迁移。接着封装 DAO把增删改查包起来。用ContentValues装数据避免手拼 SQL 字符串。public class UserDao { private final DbHelper helper; public UserDao(Context context) { this.helper new DbHelper(context); } public long insert(String name, int age, double score) { SQLiteDatabase db helper.getWritableDatabase(); ContentValues cv new ContentValues(); cv.put(name, name); cv.put(age, age); cv.put(score, score); cv.put(created_at, System.currentTimeMillis()); long rowId db.insert(DbHelper.TABLE_USER, null, cv); db.close(); return rowId; } public ListString queryAll() { ListString result new ArrayList(); SQLiteDatabase db helper.getReadableDatabase(); Cursor cursor db.query(DbHelper.TABLE_USER, null, null, null, null, null, score DESC); while (cursor.moveToNext()) { String name cursor.getString(cursor.getColumnIndexOrThrow(name)); int age cursor.getInt(cursor.getColumnIndexOrThrow(age)); double score cursor.getDouble(cursor.getColumnIndexOrThrow(score)); result.add(name / age / score); } cursor.close(); db.close(); return result; } public int updateScore(String name, double newScore) { SQLiteDatabase db helper.getWritableDatabase(); ContentValues cv new ContentValues(); cv.put(score, newScore); int rows db.update(DbHelper.TABLE_USER, cv, name ?, new String[]{name}); db.close(); return rows; } public int deleteByName(String name) { SQLiteDatabase db helper.getWritableDatabase(); int rows db.delete(DbHelper.TABLE_USER, name ?, new String[]{name}); db.close(); return rows; } }注意query的第三个参数是 where 子句第四个是参数数组用?占位能防注入。getColumnIndexOrThrow比getColumnIndex更安全列名写错会直接抛异常而不是返回 -1。TaoToken 的三件套配置如果你用local.properties或BuildConfig管理可以这样写。以settings风格的 JSON 为例放在项目根目录的调试配置里{ taotoken: { base_url: https://taotoken.net/api, api_key: sk-你的Key, model_id: 你的模型ID } }如果是 TOML 风格比如某些 CLI 工具用对应写法[taotoken] base_url https://taotoken.net/api api_key sk-你的Key model_id 你的模型ID三件套必须齐全Base URL 决定请求打到哪Key 决定身份Model ID 决定用哪个模型。少任何一个请求都会失败。Android 里读取时建议用BuildConfig字段注入不要把 Key 提交到 Git。4. 验证请求Logcat 跑通增删改查与事务回滚代码写完跑起来验证。在 Activity 里调用 DAO用 Logcat 看结果。UserDao dao new UserDao(this); long id1 dao.insert(张三, 20, 88.5); long id2 dao.insert(李四, 22, 91.0); long id3 dao.insert(王五, 19, 76.5); Log.d(SQLITE_DEMO, insert ids: id1 , id2 , id3); ListString all dao.queryAll(); for (String s : all) { Log.d(SQLITE_DEMO, query: s); } int updated dao.updateScore(张三, 95.0); Log.d(SQLITE_DEMO, updated rows: updated); int deleted dao.deleteByName(王五); Log.d(SQLITE_DEMO, deleted rows: deleted);预期 Logcat 输出insert 返回三个自增 id1、2、3query 按 score 降序返回「李四 / 22 / 91.0」「张三 / 20 / 88.5」「王五 / 19 / 76.5」update 返回 1delete 返回 1。如果 query 顺序不对检查orderBy参数是不是写成了score DESC。事务验证是重点。SQLite 默认每条语句自动提交事务能把多条操作打包要么全成功要么全回滚。写一个对照实验SQLiteDatabase db helper.getWritableDatabase(); db.beginTransaction(); try { ContentValues cv new ContentValues(); cv.put(name, 事务A); cv.put(age, 30); cv.put(score, 60.0); db.insert(DbHelper.TABLE_USER, null, cv); cv.clear(); cv.put(name, 事务B); cv.put(age, 31); cv.put(score, 61.0); db.insert(DbHelper.TABLE_USER, null, cv); if (true) { throw new RuntimeException(模拟异常触发回滚); } db.setTransactionSuccessful(); } catch (Exception e) { Log.e(SQLITE_DEMO, 事务异常: e.getMessage()); } finally { db.endTransaction(); db.close(); }跑完再 query 一次你会发现「事务A」和「事务B」都没进去——因为异常在setTransactionSuccessful()之前抛出endTransaction()检测到没标记成功就回滚了。把throw那行注释掉再跑两条记录都会出现。这就是事务的原子性转账场景必须用。TaoToken 的验证请求可以单独做在模型对话页发一句「你好」或者用 curl 测curl https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer sk-你的Key \ -H Content-Type: application/json \ -d {model:你的模型ID,messages:[{role:user,content:ping}]}返回 200 且有 choices 字段说明 Key 和通道都正常。这一步和 SQLite 无关但调试期接口通了后面加同步功能就不会卡在鉴权上。5. 常见报错排查401、local proxy failed、reading choices、OAuth入门阶段最容易撞的几个坑我按真实报错对照说。401 UnauthorizedKey 错了、过期了、或者没带Bearer前缀。检查Authorization头是不是Bearer sk-xxx中间有空格。如果 Key 是从网页复制的注意别把首尾空格带进去。TaoToken 的 Key 在 API Keys 页面重新生成即可。local proxy failed / connection refused这类报错通常是 Base URL 写错或者本地网络环境有问题。确认 Base URL 是https://taotoken.net/api不要多加/v1之外的路径也不要带 UTM 参数到 API 请求里。如果你在 Android 模拟器里请求localhost指向的是模拟器自己要用10.0.2.2映射宿主机——但 TaoToken 是公网地址直接用域名即可。reading choices 报错 / choices 字段为空一般是请求体 JSON 格式不对或者 Model ID 写错。检查messages是不是数组model字段是不是和平台一致。返回体里如果没有choices先打印完整响应看error字段说了什么。OAuth 相关报错如果你用 Claude Code 或某些 CLI 工具接入报 OAuth 失败通常是认证方式选错了。这类工具应该用 API Key 模式而不是 OAuth 模式配置里填 Base URL Key Model ID 三件套。文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite有对应说明。SQLite 这边的常见错no such table说明onCreate没执行或表名写错卸载重装 App 能强制重建UNIQUE constraint failed说明插了重复的唯一键CursorWindowAllocationException通常是 Cursor 没关或者一次查太多数据记得cursor.close()。还有一个隐蔽的getWritableDatabase()在主线程调用可能触发StrictMode警告入门 Demo 无所谓但要知道生产环境应该放子线程。事务里如果忘了setTransactionSuccessful()数据不会提交这个坑我踩过查了半天以为 insert 失败其实是回滚了。6. 继续往下走把 Demo 变成你自己的项目到这里一个能跑通增删改查和事务回滚的 SQLite Demo 就完成了。你可以在这个基础上做几件事把queryAll改成带分页的limit查询试试rawQuery预编译防注入或者给name加个索引看查询速度变化。表结构改了就升DB_VERSION观察onUpgrade触发时机。调试期接口这块TaoToken 的 Key 统一管理能帮你省掉「这个文件改完那个文件忘了改」的麻烦。需要验证模型通道就去模型对话页https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite要管 Key 去 API Keys 页https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite接入细节查文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite。长期做编码任务的话Coding Plan 在https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite。最后留个练习给t_user表加一个email列写一个onUpgrade用ALTER TABLE而不是删表重建然后插一条数据验证旧数据还在。这个练完SQLite 的版本管理你就真懂了。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询