2025年8月

Expo 提供的“后台”解决方案 Expo 提供了一些 API 来在 App 处于后台时执行有限的任务,而不是真正的“常驻进程”。

  1. 后台任务 - expo-task-manager + expo-background-fetch 这是 Expo 中最接近“后台进程”的官方解决方案。它允许你在后台定期执行一小段代码(例如每15分钟一次)。

典型用例:

定期获取最新数据(如天气、消息)

定期上报位置信息

处理待推送的通知

工作原理:

你定义一个“任务”(Task),即一个异步函数。

系统(iOS/Android)会在它认为合适的时间(考虑用户行为、电量等)唤醒你的 App 并执行这个任务。

任务执行时间非常有限(在 iOS 上通常只有 30 秒左右)。

简单示例:

javascript // 1. 导入 import * as BackgroundFetch from 'expo-background-fetch'; import * as TaskManager from 'expo-task-manager';

// 2. 定义任务 const BACKGROUND_FETCH_TASK = 'my-background-task';

TaskManager.defineTask(BACKGROUND_FETCH_TASK, async () => { const now = Date.now(); console.log(在后台运行!时间戳: ${now}); // 这里执行你的后台逻辑,例如调用 API、更新本地存储等 // 必须返回一个结果! return BackgroundFetch.BackgroundFetchResult.NewData; });

// 3. 注册任务 async function registerBackgroundFetchAsync() { return BackgroundFetch.registerTaskAsync(BACKGROUND_FETCH_TASK, { minimumInterval: 15 * 60, // 15分钟(单位:秒)。实际间隔由系统决定。 stopOnTerminate: false, // 安卓专用:App 关闭后是否继续 startOnBoot: true, // 安卓专用:手机重启后是否启动 }); }

// 在您的 App 组件中调用 registerBackgroundFetchAsync() 2. 后台位置跟踪 - expo-location 如果你需要持续在后台获取用户位置(例如导航、健身应用),可以使用 expo-location。

工作原理:

它使用专门优化的后台位置更新 API。

即使 App 在后台,系统也会持续发送位置更新。

这非常耗电,需要向用户明确请求权限并说明用途。

简单示例:

javascript import * as Location from 'expo-location';

// 请求权限并开始后台定位 await Location.requestBackgroundPermissionsAsync(); await Location.startLocationUpdatesAsync(LOCATION_TASK_NAME, { accuracy: Location.Accuracy.Balanced, // 其他配置选项... }); 3. 处理接收到的通知 - expo-notifications 当 App 在后台时收到推送通知,你可以配置一个“任务”来处理它(即使 App 完全关闭,在 Android 上也可以)。

工作原理:

当通知到达且 App 不在前台时,你可以设置一个后台处理函数。

这个函数可以执行一些操作,例如更新本地数据库、静默同步数据等。

简单示例:

javascript import * as Notifications from 'expo-notifications';

// 设置后台通知处理函数 Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldShowAlert: true, shouldPlaySound: false, shouldSetBadge: false, }), });

// 监听收到的通知(即使App在后台或关闭) Notifications.addNotificationReceivedListener(notification => { console.log('收到通知:', notification); // 可以在这里执行后台逻辑 }); 开发版(Development Build)和裸应用(Bare Workflow)的进阶选项 如果你使用 expo prebuild 生成了一个裸应用(Bare Workflow)或使用开发版(Development Build),你就可以脱离 Expo Go 的限制,使用社区原生模块来实现更强大的后台功能。

常用社区库:

react-native-background-actions: 可以在后台执行 JavaScript 代码(甚至在通知栏显示一个持续运行的通知来保活)。

react-native-background-timer: 在后台实现定时器功能。

Headless JS(仅Android): React Native 官方提供的在后台运行 JS 代码的方案。

重要警告:

在 iOS 上,滥用后台进程会导致你的 App 审核被拒。你必须遵循 Apple 的 Background Execution Guidelines。通常只允许音频播放、位置更新、VoIP、后台获取等特定场景。

过度活跃的后台进程会严重消耗用户电量,导致用户体验极差。

总结与建议 你的需求 推荐方案 定期同步少量数据(< 30秒) expo-task-manager + expo-background-fetch 持续获取地理位置 expo-location 的后台位置API 收到推送后执行简单操作 expo-notifications 的后台处理函数 需要长时间运行复杂逻辑(如音乐播放、录音) 使用 开发版(Development Build) 或 裸应用(Bare Workflow),并集成像 react-native-background-actions 这样的社区库。务必注意平台政策。 对于大多数 Expo 开发者来说,expo-background-fetch 是处理全局后台任务的首选和官方支持的方式。它平衡了功能与平台合规性。如果你的需求超出了它的能力范围,那么就需要考虑转向开发版或裸应用,并准备好应对更复杂的原生配置和平台审核挑战。


  1. 安装依赖 bash npx expo install expo-sqlite npm install drizzle-orm npm install -D @types/better-sqlite3 drizzle-kit
  2. 定义数据库模式 (schema.ts) typescript // database/schema.ts import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';

// 定义一张表 export const todos = sqliteTable('todos', { id: integer('id').primaryKey({ autoIncrement: true }), text: text('text').notNull(), isCompleted: integer('is_completed', { mode: 'boolean' }).notNull().default(false), createdAt: integer('created_at', { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()), });

// 导出模式类型 export type InsertTodo = typeof todos.$inferInsert; export type SelectTodo = typeof todos.$inferSelect; 3. 创建数据库连接和客户端 typescript // database/index.ts import { openDatabaseSync } from 'expo-sqlite'; import { drizzle } from 'drizzle-orm/expo-sqlite';

// 打开 Expo 的 SQLite 数据库 const expoDb = openDatabaseSync('myApp.db'); // 创建 Drizzle 客户端 export const db = drizzle(expoDb); 4. 使用 Drizzle Kit 进行迁移和检查(可选但推荐) 在 package.json 中添加脚本:

json { "scripts": { "generate": "drizzle-kit generate:sqlite --schema=./database/schema.ts --out=./database/migrations", "studio": "drizzle-kit studio --schema=./database/schema.ts" } } npm run generate:根据你的 schema.ts 生成 SQL 迁移文件。

npm run studio:启动 Drizzle Studio,一个强大的 GUI 来查看和编辑你的数据库。

  1. 在应用中进行数据库操作 typescript import { db } from './database'; import { todos, type InsertTodo } from './database/schema';

// 查询 const allTodos = await db.select().from(todos).all();

// 插入 const newTodo: InsertTodo = { text: 'Learn Drizzle ORM' }; const insertedTodo = await db.insert(todos).values(newTodo).returning().get();

// 更新 await db.update(todos) .set({ isCompleted: true }) .where(eq(todos.id, 1)) .run();

// 使用复杂查询 import { desc, eq, and, or } from 'drizzle-orm'; const importantTodos = await db.select() .from(todos) .where( or( eq(todos.isCompleted, false), eq(todos.text, '%urgent%') ) ) .orderBy(desc(todos.createdAt)) .all(); 总结 追求最佳类型安全、性能和现代开发体验:毫不犹豫地选择 Drizzle ORM。

构建具有复杂离线同步功能的大型应用:深入评估 WatermelonDB。

避免使用 TypeORM:它在 React Native 环境中的体验不佳。

除非万不得已,不要直接写裸 SQL:这会让代码变得难以维护。

对于绝大多数 Expo 开发者来说,expo-sqlite + Drizzle ORM 是当前技术栈下的黄金标准,能为你提供高效、可靠且愉快的开发体验。