You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native Expo构建APK后无报错崩溃问题求助

Expo构建APK后模拟器/真机崩溃,但expo start运行正常的解决方案

问题描述

React Native Expo项目通过expo build:android生成APK后,在模拟器和真机安装启动均无报错直接崩溃,但使用expo start(包括开发模式和生产模式)运行时完全正常,尝试多种方案后仍未解决。

项目配置文件

Package.json

{
  "main": "index.js",
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web",
    "eject": "expo eject",
    "test": "node ./node_modules/jest/bin/jest.js --watchAll"
  },
  "jest": {
    "preset": "jest-expo"
  },
  "dependencies": {
    "@react-native-community/netinfo": "8.2.0",
    "expo": "^45.0.0",
    "expo-asset": "~8.5.0",
    "expo-font": "~10.1.0",
    "expo-location": "~14.2.2",
    "expo-network": "~4.2.0",
    "expo-splash-screen": "~0.15.1",
    "expo-status-bar": "~1.3.0",
    "expo-updates": "~0.13.3",
    "firebase": "^9.9.0",
    "map-nearest-location": "^1.1.4",
    "react": "17.0.2",
    "react-dom": "17.0.2",
    "react-native": "0.68.2",
    "react-native-beautiful-timeline": "^0.1.1",
    "react-native-gesture-handler": "~2.2.1",
    "react-native-link": "^4.1.0",
    "react-native-maps": "0.30.2",
    "react-native-safe-area-context": "4.2.4",
    "react-native-safe-area-view": "^1.1.1",
    "react-native-screens": "~3.11.1",
    "react-native-timeline-flatlist": "^0.8.0",
    "react-native-vector-icons": "^9.1.0",
    "react-native-web": "0.17.7",
    "react-navigation": "^4.4.4",
    "react-navigation-stack": "^2.10.4",
    "react-redux": "^7.2.6",
    "redux": "^4.1.2",
    "redux-thunk": "^2.4.1"
  },
  "devDependencies": {
    "@babel/core": "^7.12.9",
    "babel-preset-expo": "~9.1.0",
    "eslint": "^8.10.0",
    "jest-expo": "^45.0.0",
    "prettier": "^2.5.1"
  },
  "private": true
}

app.json

{
  "expo": {
    "name": "demoapp",
    "slug": "demoapp",
    "version": "1.0.0",
    "orientation": "portrait",
    "icon": "./assets/icon.png",
    "splash": {
      "image": "./assets/images/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#ffffff"
    },
    "updates": {
      "fallbackToCacheTimeout": 0
    },
    "assetBundlePatterns": [
      "**/*"
    ],
    "ios": {
      "supportsTablet": true
    },
    "android": {
      "package": "com.demo.demoapp",
      "versionCode": 1
    }
  }
}

App.js

import React, {useCallback, useEffect, useState} from 'react';
import {Provider} from 'react-redux';
import {Platform, StatusBar, StyleSheet, View, LogBox} from 'react-native';
import {AppLoading} from 'expo';
import AppNavigator from './navigation/AppNavigator';
import * as SplashScreen from 'expo-splash-screen';
import * as Font from 'expo-font';
import store from './modules';

export default function App() {
  const [appIsReady, setAppIsReady] = useState(false);

  useEffect(() => {
    // Ignore log notification by message:
    LogBox.ignoreLogs(['Warning: ...']);

    // Ignore all log notifications:
    LogBox.ignoreAllLogs();

    async function hideLoading() {
      return await SplashScreen.hideAsync();
    }

    async function prepare() {
      try {
        await SplashScreen.preventAutoHideAsync();
        // Pre-load fonts, make any API calls you need to do here
        await Font.loadAsync({
          'open-sans': require('./assets/fonts/OpenSans-Regular.ttf'),
          'open-sans-bold': require('./assets/fonts/OpenSans-Bold.ttf'),
        }).then(async () => {
          await new Promise((resolve) => setTimeout(resolve, 1000)).then(() => {
            setAppIsReady(true);
          });
        });
      } catch (e) {
        console.warn(e);
      } finally {
        setAppIsReady(true);
      }
    }

    const result = prepare();
    result.then(() => {
      const isHide = hideLoading();
      isHide.then(() => {
        setAppIsReady(true);
      });
    });
  });

  if (!appIsReady) {
    return null;
  }

  return (
    <Provider store={store}>
      <AppNavigator />
    </Provider>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: 20,
  },
});

排查与解决方案

1. 修复App.js异步逻辑混乱问题

当前App.js的useEffect存在重复设置appIsReady、异步执行顺序混乱、无依赖数组导致重复触发的问题,这是APK启动崩溃的核心原因之一。修改后的代码:

useEffect(() => {
  LogBox.ignoreAllLogs();
  
  async function prepareAndLaunch() {
    try {
      await SplashScreen.preventAutoHideAsync();
      // 预加载字体
      await Font.loadAsync({
        'open-sans': require('./assets/fonts/OpenSans-Regular.ttf'),
        'open-sans-bold': require('./assets/fonts/OpenSans-Bold.ttf'),
      });
      // 可选:移除1秒延迟,避免不必要的等待
      await new Promise(resolve => setTimeout(resolve, 1000));
    } catch (e) {
      console.error(e);
    } finally {
      await SplashScreen.hideAsync();
      setAppIsReady(true);
    }
  }

  prepareAndLaunch();
}, []); // 空依赖数组确保只执行一次初始化逻辑
  • 合并分散的异步操作,统一控制启动流程
  • 添加空依赖数组,避免useEffect无限循环触发
  • 在finally中统一设置启动完成状态,保证状态唯一且正确

2. 移除/替换兼容性有问题的第三方库

  • react-native-link:该库已废弃,直接替换为React Native官方的Linking API,避免潜在的编译或运行时冲突
  • react-native-beautiful-timeline、react-native-timeline-flatlist:这类小众库可能未适配RN 0.68.2和Expo 45,临时移除这两个库后重新构建APK测试,若崩溃消失则需替换为更稳定的组件

3. 补充Android权限配置

expo-location需要明确声明权限,否则APK启动时会因权限缺失崩溃,在app.json的android字段中添加权限:

"android": {
  "package": "com.demo.demoapp",
  "versionCode": 1,
  "permissions": [
    "ACCESS_COARSE_LOCATION",
    "ACCESS_FINE_LOCATION"
  ]
}

4. 清除缓存后重新构建

执行以下命令清除Expo和项目缓存,避免旧缓存导致的构建异常:

expo r -c
expo build:android -t apk --clear-cache

5. 查看崩溃日志定位根源

通过Android Studio的Logcat查看真机崩溃日志:

  1. 开启真机USB调试并连接电脑
  2. 打开Android Studio,进入Logcat面板
  3. 过滤应用包名com.demo.demoapp,查看崩溃时的错误堆栈,精准定位问题模块

内容的提问来源于stack exchange,提问作者Hohenheim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 00:05:12