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

Expo Android应用开发:解决开机完成事件未触发与自动打开Expo页面的问题

Expo Android应用开发:解决开机完成事件未触发与自动打开Expo页面的问题

我明白你在开发Expo Android应用时遇到的这两个头疼问题——开机完成事件没反应,还有不知道怎么触发打开Expo的自定义页面。咱们一步步来拆解解决:

一、解决ACTION_BOOT_COMPLETED事件不触发的问题

从你贴的代码来看,基础配置已经有了,但还有几个容易忽略的点需要补全:

1. 完善BootReceiver的实现

你在AndroidManifest里注册了.BootReceiver,但没贴出它的具体代码。要确保它能正确响应开机广播并启动你的前台服务,示例代码如下:

class BootReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        if (intent?.action == Intent.ACTION_BOOT_COMPLETED) {
            val serviceIntent = Intent(context, MainService::class.java)
            // Android 8.0+必须用startForegroundService启动前台服务
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                context?.startForegroundService(serviceIntent)
            } else {
                context?.startService(serviceIntent)
            }
        }
    }
}

2. 补全前台服务的通知逻辑

你的MainService里startNotification方法没写完,Android要求前台服务必须显示通知,否则会被系统杀死。补充完整代码:

private val CHANNEL_ID = "activmotiv_foreground_channel"
private val CHANNEL_NAME = "ActivMotiv Foreground Service"

@RequiresApi(Build.VERSION_CODES.O)
private fun startNotification() {
    val channel = NotificationChannel(CHANNEL_ID, CHANNEL_NAME, IMPORTANCE_DEFAULT).apply {
        description = "ActivMotiv Notification Channel"
    }
    val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    notificationManager.createNotificationChannel(channel)

    val notification = Notification.Builder(this, CHANNEL_ID)
        .setContentTitle("ActivMotiv Running")
        .setContentText("等待解锁事件")
        .setSmallIcon(R.mipmap.ic_launcher) // 替换成你的应用图标
        .build()

    startForeground(1, notification) // 必须调用startForeground绑定通知
}

3. 关键限制:应用需先手动启动一次

Android 8.0+有个安全限制:如果应用从未被用户手动打开过,系统不会发送开机广播给它。所以测试前一定要先手动打开一次你的应用,再重启设备测试开机广播。

二、解锁后打开Expo的myframe.tsx页面

Expo的页面运行在JS层,需要从原生Kotlin代码通知JS层导航到指定页面,步骤如下:

1. 原生层发送事件到JS层

在你监听解锁事件的PresenceReceiver里,添加发送自定义事件的代码,通知JS层打开MyFrame页面:

import com.facebook.react.bridge.ReactContext
import com.facebook.react.modules.core.DeviceEventManagerModule
import fr.devsoleo.activmotiv.MainApplication

class PresenceReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        if (intent?.action == Intent.ACTION_USER_PRESENT) {
            // 获取当前的ReactContext
            val reactContext = (context?.applicationContext as MainApplication).reactNativeHost.reactInstanceManager.currentReactContext
            reactContext?.let {
                // 发送名为"openMyFrame"的事件到JS层
                it.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
                    .emit("openMyFrame", null)
            }
        }
    }
}

2. JS层监听事件并导航到MyFrame

假设你用React Navigation做页面导航,在App.tsx(或根组件)里监听原生事件,收到事件后跳转到myframe.tsx:

import { useEffect } from 'react';
import { NativeEventEmitter, NativeModules } from 'react-native';
import { NavigationContainer, useNavigation } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import MyFrame from './myframe';
import Home from './Home'; // 你的主页面组件

const Stack = createStackNavigator();

function AppContent() {
  const navigation = useNavigation();

  useEffect(() => {
    const eventEmitter = new NativeEventEmitter(NativeModules.DeviceEventManagerModule);
    // 监听原生发送的"openMyFrame"事件
    const subscription = eventEmitter.addListener('openMyFrame', () => {
      navigation.navigate('MyFrame');
    });

    // 组件卸载时移除监听
    return () => subscription.remove();
  }, [navigation]);

  return (
    <Stack.Navigator initialRouteName="Home">
      <Stack.Screen name="Home" component={Home} />
      <Stack.Screen name="MyFrame" component={MyFrame} options={{ headerShown: false }} />
    </Stack.Navigator>
  );
}

export default function App() {
  return (
    <NavigationContainer>
      <AppContent />
    </NavigationContainer>
  );
}

3. 实现MyFrame页面的点击关闭功能

在myframe.tsx里添加点击事件,点击后关闭页面(返回上一页):

import { View, TouchableOpacity, Text, StyleSheet } from 'react-native';
import { useNavigation } from '@react-navigation/native';

export default function MyFrame() {
  const navigation = useNavigation();

  return (
    <TouchableOpacity 
      style={styles.container}
      onPress={() => navigation.goBack()}
    >
      <Text style={styles.text}>点击关闭页面</Text>
    </TouchableOpacity>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'rgba(0, 0, 0, 0.6)', // 半透明背景
    justifyContent: 'center',
    alignItems: 'center',
  },
  text: {
    color: '#fff',
    fontSize: 22,
    fontWeight: '500',
  },
});

额外注意事项

  • 悬浮窗权限:如果希望MyFrame页面能在其他应用上层显示,需要确保SYSTEM_ALERT_WINDOW权限已开启。这个是特殊权限,需要引导用户到系统设置里手动开启,可以用react-native-permissions库来检测和请求权限。
  • 系统杀后台:部分国产ROM对后台服务限制严格,可能需要把应用加入“白名单”或“后台保护”列表,避免被系统杀死。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 12:49:30