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
相关产品推荐
相关产品推荐

