React Native安卓应用后台可解锁设备 进程被杀时无法解锁如何解决
问题根因
你的猜测是正确的,应用被系统杀死后,收到推送触发Headless JS任务时,进程仅启动了RN的后台执行上下文,此时还没有启动任何Activity实例,DeviceLock.unlock()中获取的activity为空,后续所有对窗口、Activity的操作都无法生效。
改造方案
1. 调整唤醒逻辑执行顺序
先监听AppState切换到前台的事件,等Activity完全启动后再执行解锁操作,修改nativePhoneUI.js的wakeApp方法:
import { AppState } from 'react-native' export function wakeApp(name) { calleeName = name; if (deviceWasLocked) { // 注册状态监听,App切到前台后再执行解锁 const appStateListener = AppState.addEventListener('change', (nextAppState) => { if (nextAppState === 'active') { NativeModules.DeviceLock.unlock(); deviceWasLocked = false; appStateListener.remove(); } }) } RNCallKeep.backToForeground(); }
2. 改造原生DeviceLock模块
增加空指针防护和Activity生命周期监听,应用被杀状态下调用解锁时先标记待执行任务,等Activity启动后自动执行解锁逻辑,修改DeviceLock.java代码如下:
package com.fleeting; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.LifecycleEventListener; import android.app.Activity; import android.os.Build; import android.view.WindowManager; import android.os.PowerManager; import android.content.Context; public class DeviceLock extends ReactContextBaseJavaModule { @Override public String getName() { return "DeviceLock"; } private ReactContext mReactContext; private PowerManager.WakeLock sCpuWakeLock; private boolean pendingUnlock = false; public DeviceLock(ReactApplicationContext reactContext) { super(reactContext); mReactContext = reactContext; // 注册Activity生命周期监听 mReactContext.addLifecycleEventListener(new LifecycleEventListener() { @Override public void onHostResume() { if (pendingUnlock) { executeUnlock(); pendingUnlock = false; } } @Override public void onHostPause() {} @Override public void onHostDestroy() {} }); } @ReactMethod public void unlock() { Activity currentActivity = mReactContext.getCurrentActivity(); if (currentActivity == null) { // Activity未启动,标记待执行解锁 pendingUnlock = true; return; } executeUnlock(); } private void executeUnlock() { Activity activity = mReactContext.getCurrentActivity(); if (activity == null) return; PowerManager pm = (PowerManager) mReactContext.getSystemService(Context.POWER_SERVICE); int flags = PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE; if (sCpuWakeLock != null && sCpuWakeLock.isHeld()) { sCpuWakeLock.release(); } // 增加10分钟超时,避免一直持有锁导致耗电 sCpuWakeLock = pm.newWakeLock(flags, activity.getClass().getName() + ":unlock"); sCpuWakeLock.acquire(10*60*1000L); activity.runOnUiThread(() -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { activity.setShowWhenLocked(true); activity.setTurnScreenOn(true); } activity.getWindow().addFlags( WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); }); } @ReactMethod public void lock() { if (sCpuWakeLock != null && sCpuWakeLock.isHeld()) { sCpuWakeLock.release(); } Activity activity = mReactContext.getCurrentActivity(); if (activity == null) return; activity.runOnUiThread(() -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { activity.setShowWhenLocked(false); activity.setTurnScreenOn(false); } activity.getWindow().clearFlags( WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); }); } }
3. 补充安卓权限配置
在AndroidManifest.xml中添加所需权限:
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <uses-permission android:name="android.permission.SHOW_WHEN_LOCKED" /> <uses-permission android:name="android.permission.TURN_SCREEN_ON" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
同时需要引导用户在系统设置中给应用开启「后台弹出界面」「显示在其他应用上层」的特殊权限,不同厂商的权限命名可能存在差异。
4. 调整后台消息回调判断逻辑
修改backgroundNotificationHandler.js的判断条件,覆盖锁屏场景:
// 兼容被杀状态下appState为空的情况 if (appState === BACKGROUND || appState === null) { registerPhoneAccount(); displayIncomingCallFromKilled(calleeName, phoneNumber, nativeCallUIUUID); setNativeCallUIForegroundService(); registerNativePhoneUIEvents(); addNativeCallUIEventListeners(nativeCallUIUUID, true, locked); wakeApp(calleeName); }
内容的提问来源于stack exchange,提问作者Mr. Robot
相关产品推荐
相关产品推荐

