设置userInterfaceStyle为dark时Expo SDK51安卓应用启动崩溃
问题:Expo SDK 51设置强制深色模式后安卓启动崩溃
基于Expo SDK 51开发的应用,为强制使用深色配色方案,在app.config.js中设置了userInterfaceStyle: "dark",iOS端可正常运行并生效,但安卓开发构建版本启动时崩溃,报错Must be called from main thread。Expo Doctor检测无任何问题,相关配置及依赖如下:
应用配置代码(app.config.js)
import { dirname, join } from "node:path"; import type { ConfigContext, ExpoConfig } from "@expo/config"; const INTER_FONTS = [ "100Thin", "200ExtraLight", "300Light", "400Regular", "500Medium", "600SemiBold", "700Bold", "800ExtraBold", "900Black", ].map((name) => join( dirname(require.resolve("@expo-google-fonts/inter")), `Inter_${name}.ttf`, ), ); const DIN_NEXT_LT_PRO_FONTS = ["Medium", "Bold", "Heavy"].map((name) => require.resolve(`./assets/fonts/din-next-lt-pro/DINNextLTPro-${name}.ttf`), ); export default ({ config }: ConfigContext): ExpoConfig => ({ ...config, owner: "<REDACTED>", name: "<REDACTED>", slug: "<REDACTED>", version: "1.0.0", orientation: "portrait", icon: "./assets/images/icon.png", scheme: "<REDACTED>", userInterfaceStyle: "dark", splash: { image: "./assets/images/splash.png", resizeMode: "contain", backgroundColor: "#03161C", }, ios: { supportsTablet: false, bundleIdentifier: "com.<REDACTED>.app", }, android: { adaptiveIcon: { foregroundImage: "./assets/images/adaptive-icon/foreground.png", backgroundImage: "./assets/images/adaptive-icon/background.png", backgroundColor: "#04151C", }, package: "com.<REDACTED>.app", }, web: { bundler: "metro", output: "static", favicon: "./assets/images/favicon.png", }, plugins: [ "expo-router", [ "expo-font", { fonts: [...INTER_FONTS, ...DIN_NEXT_LT_PRO_FONTS], }, ], ], experiments: { typedRoutes: true, }, extra: { eas: { projectId: "<REDACTED>", }, }, });
依赖配置(package.json)
{ "name": "mobile-client", "main": "expo-router/entry", "version": "1.0.0", "scripts": { "start": "expo start", "android": "expo run:android", "ios": "expo run:ios", "web": "expo start --web", "eas-build-post-install": "yarn workspaces foreach --parallel --interlaced --recursive --from mobile-client --exclude mobile-client run build" }, "installConfig": { "hoistingLimits": "workspaces" }, "dependencies": { "@expo-google-fonts/inter": "^0.2.3", "@expo/html-elements": "^0.10.1", "@expo/vector-icons": "^14.0.2", "@gluestack-ui/accordion": "^1.0.7", "@gluestack-ui/actionsheet": "^0.2.45", "@gluestack-ui/alert": "^0.1.15", "@gluestack-ui/alert-dialog": "^0.1.31", "@gluestack-ui/avatar": "^0.1.17", "@gluestack-ui/button": "^1.0.7", "@gluestack-ui/checkbox": "^0.1.32", "@gluestack-ui/divider": "^0.1.9", "@gluestack-ui/fab": "^0.1.21", "@gluestack-ui/form-control": "^0.1.18", "@gluestack-ui/icon": "^0.1.22", "@gluestack-ui/image": "^0.1.10", "@gluestack-ui/input": "^0.1.31", "@gluestack-ui/link": "^0.1.22", "@gluestack-ui/menu": "^0.2.36", "@gluestack-ui/modal": "^0.1.34", "@gluestack-ui/nativewind-utils": "patch:@gluestack-ui/nativewind-utils@npm%3A1.0.23#~/.yarn/patches/@gluestack-ui-nativewind-utils-npm-1.0.23-7232dc7123.patch", "@gluestack-ui/overlay": "^0.1.15", "@gluestack-ui/popover": "^0.1.41", "@gluestack-ui/pressable": "^0.1.16", "@gluestack-ui/progress": "^0.1.17", "@gluestack-ui/radio": "^0.1.33", "@gluestack-ui/select": "^0.1.29", "@gluestack-ui/slider": "^0.1.25", "@gluestack-ui/spinner": "^0.1.14", "@gluestack-ui/switch": "^0.1.22", "@gluestack-ui/textarea": "^0.1.23", "@gluestack-ui/toast": "^1.0.7", "@gluestack-ui/tooltip": "^0.1.34", "@legendapp/motion": "^2.4.0", "@react-native-masked-view/masked-view": "0.3.1", "@react-navigation/bottom-tabs": "^6", "@react-navigation/native": "^6", "expo": "~51.0.39", "expo-blur": "~13.0.2", "expo-constants": "~16.0.2", "expo-dev-client": "~4.0.29", "expo-font": "~12.0.10", "expo-haptics": "~13.0.1", "expo-linear-gradient": "~13.0.2", "expo-linking": "~6.3.1", "expo-router": "~3.5.24", "expo-splash-screen": "~0.27.7", "expo-status-bar": "~1.12.1", "expo-symbols": "~0.1.5", "expo-system-ui": "~3.0.7", "expo-web-browser": "~13.0.3", "lucide-react-native": "^0.455.0", "metro-config": "^0.80.0", "nativewind": "4.0.36", "react": "18.2.0", "react-dom": "18.2.0", "react-native": "0.74.5", "react-native-gesture-handler": "~2.16.1", "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "4.10.5", "react-native-screens": "3.31.1", "react-native-svg": "15.2.0", "react-native-svg-transformer": "^1.5.0", "react-native-web": "~0.19.13", "shared": "workspace:^", "tailwind-merge": "^2.5.4", "tailwindcss": "^3.4.14", "ts-node": "^10.9.2" }, "devDependencies": { "@babel/core": "^7.25.2", "@types/jest": "^29.5.12", "@types/node": "^22.9.0", "@types/react": "~18.2.79", "@types/react-test-renderer": "^18.2.0", "jest": "^29.2.1", "jest-expo": "~51.0.4", "react-test-renderer": "18.2.0", "typescript": "~5.3.3" } }
解决方案
该问题是Expo SDK 51在安卓端设置全局userInterfaceStyle时的线程调用bug,可通过以下方式解决:
方案1:移除全局配置,用代码动态设置深色模式
删除app.config.js中的userInterfaceStyle: "dark",在应用入口(如_layout.tsx)添加以下代码:
import { useEffect } from 'react'; import { Appearance } from 'react-native'; import { StatusBar } from 'expo-status-bar'; export default function RootLayout() { // 强制设置深色模式 useEffect(() => { Appearance.setColorScheme('dark'); }, []); return ( <> {/* 原有布局内容 */} <StatusBar style="light" /> </> ); }
方案2:升级/降级Expo SDK版本
若坚持使用全局配置,可升级到Expo SDK 52或降级到SDK 50,该bug在这些版本中已修复。
方案3:修改安卓原生代码(仅必要时使用)
手动修改android/app/src/main/java/com/[你的包名]/MainActivity.kt,确保UI模式设置在主线程执行:
import android.os.Bundle import androidx.appcompat.app.AppCompatDelegate import expo.modules.ReactActivity class MainActivity : ReactActivity() { override fun onCreate(savedInstanceState: Bundle?) { runOnUiThread { AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) } super.onCreate(savedInstanceState) } // 保留原有其他代码 }
验证
修改后重新执行expo run:android,安卓应用即可正常启动并保持深色模式。
内容的提问来源于stack exchange,提问作者Fez Vrasta
相关产品推荐
相关产品推荐

