ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SQLite在安卓中的基本详解和简单使用:来自阿里巴巴佛系Android程序员的指南(TaoToken 统一 Key 通道版)

SQLite在安卓中的基本详解和简单使用:来自阿里巴巴佛系Android程序员的指南(TaoToken 统一 Key 通道版) 1. 安卓本地持久化为什么绕不开 SQLite从建库到智能查询扩展的完整链路SQLite 是 Android 系统内置的轻量级关系型数据库它不需要单独安装服务端进程整个数据库就是一个文件适合做本地数据持久化。你能用它存用户配置、离线缓存、聊天记录、订单草稿甚至做一个小型的本地检索库。适合谁适合所有做 Android 应用、需要把数据留在手机本地的开发者尤其是刚接触SQLiteOpenHelper的同学。我见过太多项目一上来就上 Room结果连onCreate和onUpgrade的调用时机都说不清楚升级时表结构没改用户一更新 App 就闪退。所以这篇我按“建库建表 → 封装 Helper → 增删改查 → 事务 → 用统一 Key 通道做智能查询扩展 → 排错”的顺序写每一步都给可复制的代码和验证动作。核心检索词先摆出来SQLite 在安卓中的基本详解和简单使用重点在SQLiteOpenHelper封装、getWritableDatabase()与getReadableDatabase()的区别、Cursor遍历、事务处理以及如何把模型调用 endpoint 改到 TaoToken 的统一 Key/API 通道完成示例里的智能查询扩展。整篇的代码你都能直接贴进 Android Studio 跑。先说一个最容易踩的坑数据库版本只能升不能降。你把version从 2 改回 1系统会认为要降级直接抛SQLiteDowngradeFailedException闪退。所以版本号管理要当成正式的发版流程来对待。另外getWritableDatabase()以读写方式打开磁盘满了会直接抛错getReadableDatabase()先尝试读写失败后降级为只读打开。日常写操作我用getWritableDatabase()纯展示列表用getReadableDatabase()这样在极端存储情况下至少还能读。下面进入正题先看原问题场景再一步步把代码落地。2. 原问题与场景SQLiteOpenHelper 封装与增删改查到底怎么写才不闪退场景很具体一个记账或通讯录类 App本地要存person表字段有自增主键、姓名、年龄。第一次安装时建表后续版本要加一列phone。同时希望输入“查一下年龄大于 20 的人”这种自然语言时能调用模型把意图转成 SQL 条件再落到本地查询。先看SQLiteOpenHelper的标准封装。onCreate只在数据库第一次生成时调用onUpgrade在版本号变大时调用。注意super(context, name, null, version)第三个参数是CursorFactory传null用系统默认工厂。public class DatabaseHelper extends SQLiteOpenHelper { private static final String DB_NAME count.db; private static final int DB_VERSION 2; public DatabaseHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE TABLE IF NOT EXISTS person ( personid INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(20), age INTEGER DEFAULT 0)); } Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion 2) { db.execSQL(ALTER TABLE person ADD COLUMN phone VARCHAR(12)); } } }建表语句里AUTOINCREMENT保证主键自增DEFAULT 0给年龄兜底。升级逻辑用oldVersion 2判断比无脑ALTER更安全因为用户可能从版本 1 直接跳到版本 3。增删改查有两套写法execSQL走原生 SQLinsert/delete/update/query走封装 API。插入用ContentValues更省心不用手动转义。SQLiteDatabase db helper.getWritableDatabase(); ContentValues values new ContentValues(); values.put(name, 小明); values.put(age, 20); long rowid db.insert(person, null, values); db.close();查询用Cursor遍历moveToNext()返回 false 表示到底了。列索引从 0 开始顺序和你columns数组一致。Cursor cursor db.rawQuery( SELECT * FROM person WHERE name LIKE ? AND age ?, new String[]{%小明%, 20}); while (cursor.moveToNext()) { int id cursor.getInt(0); String name cursor.getString(1); int age cursor.getInt(2); Log.d(SQLiteDemo, id id , name name , age age); } cursor.close(); db.close();事务处理是重点。批量插入 1000 条不用事务会慢到怀疑人生而且中途失败会留下脏数据。用beginTransaction()和setTransactionSuccessful()包起来。db.beginTransaction(); try { for (int i 0; i 1000; i) { ContentValues v new ContentValues(); v.put(name, user_ i); v.put(age, i % 60); db.insert(person, null, v); } db.setTransactionSuccessful(); } finally { db.endTransaction(); }setTransactionSuccessful()不调用endTransaction()时会回滚。这个细节很多人漏掉导致数据没写进去还以为代码没问题。到这里本地部分就完整了。接下来把模型调用 endpoint 改到 TaoToken用统一 Key 通道做智能查询扩展。3. TaoToken 前置统一 Key 通道与可复制配置片段TaoToken 提供统一的 API 通道你只需要一个 Key 和统一的 Base URL就能调用多种模型。官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 注意 API 地址不带 UTM 参数。在 Android 项目里我建议把模型调用封装成一个IntentParser输入自然语言输出 SQL 条件片段。配置用settings.gradle或local.properties管理别硬编码 Key。先看可复制的配置片段。如果你用 Claude Code 或 Cline 这类工具做辅助开发配置文件路径和原文保持一致。以~/.claude/settings.json为例{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: 你的TaoTokenKey, ANTHROPIC_MODEL: claude-sonnet-4-20250514 } }如果你用 Codex 的auth.json路径通常是~/.codex/auth.json{ base_url: https://taotoken.net/api, api_key: 你的TaoTokenKey, model: gpt-4o }三件套必须写全Base URL、Key、Model ID。少一个都会报 401 或模型不存在。Cline MCP 的配置也是同样逻辑在 MCP server 配置里填baseUrl、apiKey、model。在 Android 代码里我用 OkHttp 发请求。先加依赖implementation com.squareup.okhttp3:okhttp:4.12.0然后写一个解析方法把“年龄大于 20 且名字包含小明”转成age 20 AND name LIKE %小明%public String toSqlCondition(String naturalLanguage) throws IOException { OkHttpClient client new OkHttpClient(); String json {\model\:\claude-sonnet-4-20250514\,\messages\:[{\role\:\user\, \content\:\把下面的查询意图转成SQLite的WHERE条件只输出条件不要解释 naturalLanguage \}]}; Request request new Request.Builder() .url(https://taotoken.net/api/v1/messages) .addHeader(Authorization, Bearer BuildConfig.TAOTOKEN_KEY) .addHeader(Content-Type, application/json) .post(RequestBody.create(json, MediaType.parse(application/json))) .build(); try (Response response client.newCall(request).execute()) { return response.body().string(); } }Key 放在BuildConfig里通过local.properties注入别提交到 Git。这一步做完前置就齐了。4. 可复制配置与验证请求请求返回与数据库写入双重校验配置写好了必须验证。分两步先验证模型请求返回正常再验证返回的条件能正确落到 SQLite 查询。第一步用 curl 快速验证通道是否通curl -X POST https://taotoken.net/api/v1/messages \ -H Authorization: Bearer 你的TaoTokenKey \ -H Content-Type: application/json \ -d {model:claude-sonnet-4-20250514,messages:[{role:user,content:把年龄大于20转成SQLite WHERE条件只输出条件}]}正常返回里会有content字段里面是age 20。如果返回 401说明 Key 不对如果返回model not found说明 Model ID 写错了。第二步把返回的条件拼进查询做双重校验。先看模型返回String condition toSqlCondition(年龄大于20且名字包含小明); Log.d(SQLiteDemo, 模型返回条件: condition);假设返回age 20 AND name LIKE %小明%拼进查询String sql SELECT * FROM person WHERE condition; Cursor cursor db.rawQuery(sql, null); int count 0; while (cursor.moveToNext()) { count; Log.d(SQLiteDemo, 命中: cursor.getString(1) , age cursor.getInt(2)); } cursor.close(); Log.d(SQLiteDemo, 共命中 count 条);双重校验的意思是Logcat 里既能看到模型返回的条件字符串又能看到数据库实际命中的行数和字段值。如果模型返回了条件但数据库 0 条命中要么是数据没插进去要么是条件字段名对不上。写入校验也一样。插入一条name小明, age25然后用模型条件查应该命中 1 条。Logcat 过滤SQLiteDemo标签能看到完整链路。ContentValues v new ContentValues(); v.put(name, 小明); v.put(age, 25); db.insert(person, null, v); Cursor c db.rawQuery(SELECT COUNT(*) FROM person WHERE condition, null); if (c.moveToFirst()) { Log.d(SQLiteDemo, 校验命中数: c.getInt(0)); } c.close();实测下来这套双重校验能挡住大部分“模型返回对了但数据库没写进去”的隐蔽问题。验证通过后再看常见报错。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth 对照排错部分我按真实报错来。第一个401 Unauthorized。原因通常是 Key 没带、Key 过期、或者Authorization头格式不对。正确格式是Bearer 你的Key中间一个空格。检查BuildConfig.TAOTOKEN_KEY是否为空。第二个local proxy failed。这个报错一般出现在你本地配了代理但代理没起来或者 Base URL 写成了http://localhost。检查你的ANTHROPIC_BASE_URL或base_url是否写成https://taotoken.net/api别加多余路径。第三个reading choices相关报错通常是响应体解析失败。比如你按 OpenAI 格式解析但返回的是 Anthropic 格式字段名对不上。先打印原始response.body().string()看结构再解析。第四个OAuth 相关报错。如果你用 Claude Code 登录态可能提示 OAuth token 失效。这时候改用 API Key 方式在settings.json里配ANTHROPIC_AUTH_TOKEN别混用登录态和 Key。还有一个高频问题SQLiteDowngradeFailedException。这就是版本号降级导致的把DB_VERSION改回比手机里已存版本小的值就会触发。解决办法是只升不降或者卸载重装。CursorIndexOutOfBoundsException也常见列索引越界。检查getInt(2)的 2 是否超出columns长度。用cursor.getColumnIndex(age)更稳。SQLiteDatabaseLockedException出现在多线程同时写。用单例 Helper或者把写操作放同一个线程配合事务。no such table说明onCreate没执行或表名拼错。检查getWritableDatabase()是否真的被调用过Helper 是否用了同一个数据库名。排完这些基本就能稳定跑了。最后把入口整理一下。6. 语义一致 CTA按场景分流到 API Keys、模型对话与 Coding Plan不同需求走不同入口别只记首页。如果你在排障或接入阶段需要看 Key 怎么建、文档怎么读走 API Keys 和接入文档https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 和 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。如果你想先验证模型返回格式不想写代码直接用模型对话页面试https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。如果你是长期做 Android 编码、要接 Agent 做自动化走 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。控制台入口在 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite Claude Code 相关配置参考 https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。最后给一个实用技巧把DatabaseHelper做成单例避免多次new导致多连接锁库模型返回的 SQL 条件一定要做白名单校验只允许AND、OR、比较运算符和LIKE别直接把模型输出拼进 SQL防止注入。这两点做完本地持久化和智能查询扩展就能稳稳落地。
返回列表