1. 为什么我又把 Provider、ORMLite、GreenDao 拉出来跑了一遍
Android 本地数据库选型这件事,看起来是老生常谈,但真正落到项目里,很多人还是会卡在同一个问题上:同一张表,用 ContentProvider、ORMLite、GreenDao 分别写增删改查,到底谁快、谁省事、谁更适合长期维护。我这次干脆把三种方案放在同一个工程里,用同一套测试用例跑插入和查询,把配置骨架和验证动作都整理出来,方便你直接复现。
这篇内容适合正在做 Android 本地存储选型的人,也适合已经用了 SQLiteOpenHelper 但想对比 ORM 框架的同学。核心检索词就三个:Provider、ORMLite、GreenDao,外加性能对比和数据库操作。我会先给结论方向,再给可复制的配置片段,最后用 TaoToken 统一 Key/API 通道做一次验证请求,把整个链路串起来。
需要提前说明的是,性能数字会受测试机、数据量、字段数量、是否批量事务影响,所以我不编造具体毫秒值,而是告诉你测试方法、观察点和常见偏差来源。你按本文步骤跑一遍,得到的才是你自己设备上的真实结果。
2. TaoToken 前置:统一 Key 与 API 通道准备
在开始写三种数据库实现之前,先把 TaoToken 的配置准备好。原因很简单:后面验证请求、跑模型对话、甚至让 coding agent 帮你生成 Dao 代码,都需要一个统一的 API 通道。TaoToken 官网地址是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api ,注意 API 地址不带 UTM 参数。
你需要先拿到 API Key。进入控制台创建 Key 的路径是:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,Key 管理页面在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。创建好之后复制保存,后面 settings.json 里会用到。
如果你只是想先验证模型通道是否通,可以直接用模型对话页面:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 。如果你打算长期做 Android 编码、让 Agent 帮你写 Provider 和 Dao 代码,建议了解 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,ClaudeCodeAnthropic 相关配置参考 https://taotoken.net/claudecode-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode-anthropic&utm_campaign=rewrite 。
注意:API Key 只放在本地配置文件或环境变量里,不要硬编码进 Android 工程提交到仓库。数据库对比工程和 API 配置工程建议分开管理。
3. 可复制配置:Provider、ORMLite、GreenDao 三套骨架
3.1 ContentProvider 注册片段与表结构
ContentProvider 的写法确实比 ORM 复杂,但它的优势是系统级支持、跨进程访问稳定。先在 AndroidManifest.xml 里注册 Provider:
<provider android:name=".db.UserProvider" android:authorities="com.example.dbtest.provider" android:exported="false" />然后定义 Contract 类,把表名、列名、URI 统一管理:
public final class UserContract { public static final String AUTHORITY = "com.example.dbtest.provider"; public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/users"); public static final String TABLE = "users"; public static final String COL_ID = "_id"; public static final String COL_NAME = "name"; public static final String COL_PHONE = "phone"; }Provider 内部用 SQLiteOpenHelper 建表,onCreate 里执行:
db.execSQL("CREATE TABLE IF NOT EXISTS users (" + "_id INTEGER PRIMARY KEY AUTOINCREMENT, " + "name TEXT NOT NULL, " + "phone TEXT)");增删改查通过 UriMatcher 分发,insert 返回 Uri,query 返回 Cursor。Provider 的查询直接拿 Cursor 解析,这也是它在读取场景表现不错的原因之一。
3.2 ORMLite 配置骨架
ORMLite 用注解映射表结构,代码量比 Provider 少。先写实体类:
@DatabaseTable(tableName = "users") public class User { @DatabaseField(generatedId = true) public int id; @DatabaseField(canBeNull = false) public String name; @DatabaseField public String phone; public User() {} }再写 Helper 继承 OrmLiteSqliteOpenHelper:
public class UserDbHelper extends OrmLiteSqliteOpenHelper { private static final String DB_NAME = "ormlite.db"; private static final int DB_VERSION = 1; public UserDbHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } @Override public void onCreate(SQLiteDatabase db, ConnectionSource cs) { try { TableUtils.createTable(cs, User.class); } catch (SQLException e) { throw new RuntimeException(e); } } @Override public void onUpgrade(SQLiteDatabase db, ConnectionSource cs, int oldV, int newV) { try { TableUtils.dropTable(cs, User.class, true); onCreate(db, cs); } catch (SQLException e) { throw new RuntimeException(e); } } }Activity 可以继承 OrmLiteBaseActivity 直接拿 getHelper(),也可以手动管理 Helper 的打开关闭。插入用 userDao.create(user),查询用 userDao.queryForAll()。
3.3 GreenDao 生成器与 Dao 使用
GreenDao 不用注解,靠 Java 工程预生成代码,所以运行时快。先建一个纯 Java 工程,引入 greenDao-generator.jar 和 freemarker.jar,写生成器:
public class ExampleDaoGenerator { public static void main(String[] args) throws Exception { Schema schema = new Schema(3, "com.example.dbtest.greendao.dao"); addUser(schema); new DaoGenerator().generateAll(schema, "../dbTest/src-gen"); } private static void addUser(Schema schema) { Entity user = schema.addEntity("users"); user.addIdProperty(); user.addStringProperty("name").notNull(); user.addStringProperty("phone"); } }运行后会生成 DaoMaster、DaoSession、Users、UsersDao 四个类。Android 工程里这样初始化:
DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "greendao.db", null); DaoMaster daoMaster = new DaoMaster(helper.getWritableDatabase()); UsersDao usersDao = daoMaster.newSession().getUsersDao();之后 insert、deleteAll、queryBuilder 都通过 usersDao 调用。GreenDao 的 deleteAll 是一次性批量删除,这点比 ORMLite 逐个删除要省事很多。
3.4 TaoToken settings.json 配置示例
把 API 通道配置成统一入口,方便后面验证请求和让 Agent 辅助生成代码:
{ "api_base": "https://taotoken.net/api", "api_key": "你的_API_KEY", "model": "你的模型名", "timeout": 60 }提示:api_base 用 https://taotoken.net/api ,不要加 UTM 参数。Key 从 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 获取。
4. 验证请求与成功结果:统一测试用例怎么跑
4.1 测试用例设计
三种方案用同一张 users 表,字段一致:id 自增、name 非空、phone 可空。测试动作分两组:
第一组插入 5000 条,记录总耗时,然后查询全部并解析。第二组先清空表,再插入 1000 条,重复插入和查询。每次插入前先删除旧数据,观察删除方式对耗时的影响。
// 统一测试入口示意 long start = System.currentTimeMillis(); for (int i = 0; i < 5000; i++) { User u = new User(); u.name = "user_" + i; u.phone = "1380000" + i; insertOne(u); // 三种方案各自实现 } long insertCost = System.currentTimeMillis() - start; long qStart = System.currentTimeMillis(); List<User> list = queryAll(); long queryCost = System.currentTimeMillis() - qStart;4.2 观察到的结果方向
插入场景下,GreenDao 优势明显,因为它没有反射开销,且批量操作可以走事务。ORMLite 因为用注解加反射,单条插入偏慢,而且它没有一次性删除全部数据的便捷接口,只能逐条删,清表阶段会拖慢整体。Provider 的写入中规中矩,但读取时直接拿 Cursor 解析,反而比较快。
查询场景下,Provider 多次读取的结论比较稳定。ORMLite 查询同样受反射影响。GreenDao 的 queryBuilder 在条件查询上更灵活,但需要先生成代码,初次配置容易晕。
注意:这些是方向性结论,不是绝对排名。字段越多、数据量越大、是否开启事务,都会改变结果。建议你自己跑一遍再下判断。
4.3 用 TaoToken 验证通道
配置好 settings.json 后,发一次验证请求确认通道可用。可以用模型对话页面直接测试:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 。如果返回正常,说明 Key 和 api_base 配置正确,后面让 Agent 帮你生成 Provider 或 Dao 代码就不会卡在通道上。
5. 本篇常见错排查
5.1 Provider 报 IllegalArgumentException: Unknown URL
多数是 UriMatcher 没注册对应 code,或者 AndroidManifest 里 authorities 和 Contract 里写的不一致。检查两处字符串是否完全相同,包括包名前缀。
5.2 ORMLite 报 Unable to create table
常见原因是实体类没有无参构造函数,或者 @DatabaseField 用在了不支持的类型上。另外 TableUtils.createTable 要在 onCreate 里调用,不要放到 Activity 里重复建表。
5.3 GreenDao 生成代码失败
先确认 Java 工程和 Android 工程目录关系,generateAll 的路径是相对路径,写错就生成到别处。再确认 greenDao-generator.jar 和 freemarker.jar 都引入了。生成后记得把 src-gen 目录加入 Android 工程的 sourceSets。
5.4 清表慢得离谱
ORMLite 没有 deleteAll 接口时,逐条删除会非常慢。可以自己写 SQL 或改用 dao.executeRaw。GreenDao 直接用 deleteAll。Provider 用 delete 带 null 条件即可清空。
5.5 TaoToken 请求 401
Key 没填对,或者 api_base 写成了带 UTM 的地址。正确写法是 https://taotoken.net/api 。Key 去 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 重新复制。
6. 选型建议与后续动作
如果你追求系统级稳定和跨进程访问,Provider 是绕不开的,代价是代码量大。如果你想要注解式开发、代码量少,ORMLite 上手快,但要接受反射带来的性能损耗,以及清表这类操作要自己补。如果你追求插入和批量操作性能,GreenDao 更合适,代价是多了代码生成这一步,初次配置需要耐心。
我自己的做法是:小表、读多写少用 Provider;中等复杂度、快速迭代用 ORMLite;数据量大、写入频繁用 GreenDao。三种方案不是互斥的,同一个项目里按表选型也完全可行。
后续你可以把条件查询、条件删除、批量事务这几组测试补上,结果会更完整。需要让 Agent 帮你补测试代码或生成 Dao 骨架,可以从 Coding Plan 进入:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。接入细节看文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。