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

Expo安卓应用关闭后点击通知启动崩溃问题求助

React Native Expo安卓应用冷启动点击通知崩溃问题
  • 生产构建版本中,应用关闭/未运行时点击通知会崩溃:启动后显示闪屏,几秒后崩溃
  • 应用已打开时点击通知运行正常
  • Bugsnag未上报任何错误,尝试用Logcat查看但无法理解错误含义及修复方法

背景:导航器结构调整

原本有三个导航器:

  1. InitNavigator - 仅包含闪屏页
  2. AuthNavigator - 认证页面
  3. AppNavigator - 用户登录后可见页面

最初实现中,AppNavigator仅在用户登录(通过AsyncStorage的JWT判断)时条件挂载,应用启动需先从AsyncStorage取信息。出现问题后修改为启动时挂载所有导航器,代码如下:

<AppStack.Navigator
      screenOptions={{ headerShown: false }}
      // initialRouteName={routes.APP_NAVIGATOR.SINGLE_POST}
    >
      {/* Initial/Splash Screen */}
      <AppStack.Group>
        <AppStack.Screen name="splashScreen" component={SplashScreen} />
      </AppStack.Group>
      {/* Authentication Screen */}
      <AppStack.Group>
        <AppStack.Screen
          name={routes.AUTH_STACK_NAVIGATOR.SIGNUP_NAVIGATOR}
          component={UserProvider}
        />
      </AppStack.Group>
      <AppStack.Group>
        {/* <AppStack.Screen name="demo" component={ViewCommunityProfile} /> */}
        <AppStack.Screen
          name={routes.APP_NAVIGATOR.COMMUNITY_NAVIGATOR}
          component={SideDrawer}
        />

        <AppStack.Screen
          name={routes.APP_NAVIGATOR.SET_LOCATION}
          component={Location}
        />

        <AppStack.Screen
          name="EditCommunityProfile"
          component={EditCommunityProfile}
        />
        <AppStack.Screen
          name={routes.APP_NAVIGATOR.CREATE_POST_NAVIGATOR}
          component={CreatePostNavigator}
        />
        <AppStack.Screen
          name={routes.APP_NAVIGATOR.PROFILE_NAVIGATOR}
          component={ProfileNavigator}
        />
        <AppStack.Screen
          name={routes.APP_NAVIGATOR.CHAT_NAVIGATOR}
          component={ChatNavigator}
        />
        <AppStack.Screen
          name={routes.APP_NAVIGATOR.SINGLE_POST}
          component={SinglePost}
        />
      </AppStack.Group>
    </AppStack.Navigator>

通知处理代码

通知在包裹整个应用的Context中处理:

responseListener.current =
      Notifications.addNotificationResponseReceivedListener((response) => {
        const notificatonContent = response.notification.request.content;
        if (notificatonContent.data?.url) {
          openUsingDefaultUrl(notificatonContent.data.url);
        } else {
          openUsingDefaultUrl("/notification");
        }
      });

后台通知任务代码

import * as TaskManager from "expo-task-manager";

const BACKGROUND_NOTIFICATION_TASK = "BACKGROUND-NOTIFICATION-TASK";

TaskManager.defineTask(
  BACKGROUND_NOTIFICATION_TASK,
  ({ data, error, executionInfo }) => {
    if (error) {
      Bugsnag.notify(error);
    }
    if (data) {
      if (data?.url) {
        openUsingDefaultUrl(data.url);
      } else {
        openUsingDefaultUrl("/notification");
      }
    }
  }
);

修复方案

1. 禁止在后台任务中执行路由跳转

TaskManager的后台任务运行在非UI线程,无法直接调用导航跳转方法,这是冷启动崩溃的核心原因之一:

  • 修改后台任务逻辑,仅存储通知的跳转URL到AsyncStorage:
    TaskManager.defineTask(
      BACKGROUND_NOTIFICATION_TASK,
      ({ data, error }) => {
        if (error) {
          Bugsnag.notify(error);
          return;
        }
        if (data?.url) {
          AsyncStorage.setItem('pendingDeepLink', data.url);
        } else {
          AsyncStorage.setItem('pendingDeepLink', "/notification");
        }
      }
    );
    
  • 在SplashScreen的初始化逻辑中,读取pendingDeepLink并执行跳转,完成后删除该存储项

2. 等待应用初始化完成后再处理通知跳转

冷启动时应用未完成初始化,直接跳转路由会导致导航器状态异常:

  • 在根Context或SplashScreen中设置appReady状态标记,在AsyncStorage读取、导航器初始化完成后设为true
  • 修改通知响应监听逻辑,等待appReady为true后再执行跳转:
    responseListener.current = Notifications.addNotificationResponseReceivedListener(async (response) => {
      // 等待应用就绪
      while (!appReady) {
        await new Promise(resolve => setTimeout(resolve, 100));
      }
      const notificatonContent = response.notification.request.content;
      const targetUrl = notificatonContent.data?.url || "/notification";
      openUsingDefaultUrl(targetUrl);
    });
    

3. 优化导航器初始化逻辑

恢复导航器的条件渲染,避免未登录状态下加载依赖用户信息的组件:

  • 在SplashScreen中完成JWT验证,直接跳转到对应导航器的初始页面:
    // SplashScreen组件内
    useEffect(() => {
      const initApp = async () => {
        const token = await AsyncStorage.getItem('jwt');
        await AsyncStorage.removeItem('pendingDeepLink'); // 清理旧的跳转记录
        const pendingUrl = await AsyncStorage.getItem('pendingDeepLink');
        
        if (token) {
          if (pendingUrl) {
            navigation.navigate(pendingUrl);
          } else {
            navigation.navigate(routes.APP_NAVIGATOR.COMMUNITY_NAVIGATOR);
          }
        } else {
          navigation.navigate(routes.AUTH_STACK_NAVIGATOR.SIGNUP_NAVIGATOR);
        }
      };
      initApp();
    }, [navigation]);
    
  • 根导航器仅保留必要的初始路由,其他导航器作为子页面按需加载

4. 提取Logcat关键错误信息

连接安卓设备后运行以下命令获取错误日志:

adb logcat *:E | grep -E "AndroidRuntime|ReactNative|Exception"

重点查看NullPointerException、组件初始化失败等信息,定位崩溃的具体组件或逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 15:18:15