如何实现类似WhatsApp锁屏/解锁时全屏来电弹窗(含iOS方案)
Android 端解决方案
核心问题分析
锁屏状态下系统会限制普通窗口的显示,需申请锁屏专属权限并使用符合系统规范的窗口类型/标记才能实现全屏弹窗。
具体实现步骤
申请必要权限
在AndroidManifest.xml中声明权限:<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" /> <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <uses-permission android:name="android.permission.WAKE_LOCK" />Android 6.0+需动态申请悬浮窗权限:
if (!Settings.canDrawOverlays(context)) { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + context.getPackageName())); startActivityForResult(intent, REQUEST_OVERLAY_PERMISSION); }使用Full Screen Intent(官方推荐,与WhatsApp逻辑一致)
这是锁屏下显示全屏来电弹窗的标准方案:- 配置来电全屏Activity(
AndroidManifest.xml):<activity android:name=".IncomingCallActivity" android:excludeFromRecents="true" android:launchMode="singleInstance" android:theme="@style/FullScreenCallTheme"> <intent-filter> <action android:name="com.yourpackage.ACTION_INCOMING_CALL" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> - 在Activity的
onCreate中设置锁屏显示标记:@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { setShowWhenLocked(true); setTurnScreenOn(true); } else { getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); } setContentView(R.layout.activity_incoming_call); } - 发送带全屏意图的通知:
Intent callIntent = new Intent(context, IncomingCallActivity.class); PendingIntent fullScreenPendingIntent = PendingIntent.getActivity(context, 0, callIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CALL_CHANNEL_ID) .setSmallIcon(R.drawable.ic_call) .setContentTitle("来电") .setContentText("张三") .setPriority(NotificationCompat.PRIORITY_HIGH) .setCategory(NotificationCompat.CATEGORY_CALL) .setFullScreenIntent(fullScreenPendingIntent, true); NotificationManagerCompat.from(context).notify(CALL_NOTIFICATION_ID, builder.build());
- 配置来电全屏Activity(
悬浮窗方案适配锁屏
若坚持使用悬浮窗,需额外添加锁屏相关Window标记:WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD, PixelFormat.TRANSLUCENT);同时唤醒屏幕:
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); PowerManager.WakeLock wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "Call:WakeLock"); wakeLock.acquire(5000); // 保持唤醒5秒,可按需调整
iOS 端实现思路与代码片段
核心依赖:CallKit框架
iOS系统对来电类弹窗有严格管控,CallKit是唯一能在锁屏/后台显示全屏来电界面的官方方案,WhatsApp等应用均采用此框架。
具体实现步骤
配置项目权限
- 在
Capabilities中开启Push Notifications和Background Modes下的Voice over IP权限。 - 导入CallKit与AVFoundation框架:
import CallKit import AVFoundation
- 在
实现CallKit代理逻辑
class CallManager: NSObject, CXProviderDelegate { private let provider: CXProvider override init() { let config = CXProviderConfiguration(localizedName: "我的通话") config.supportsVideo = true config.maximumCallGroups = 1 config.maximumCallsPerCallGroup = 1 config.iconTemplateImageData = UIImage(named: "call_icon")?.pngData() provider = CXProvider(configuration: config) super.init() provider.setDelegate(self, queue: nil) } // 触发来电通知 func reportIncomingCall(uuid: UUID, handle: String, hasVideo: Bool = false) { let update = CXCallUpdate() update.remoteHandle = CXHandle(type: .generic, value: handle) update.hasVideo = hasVideo provider.reportNewIncomingCall(with: uuid, update: update) { error in if let error = error { print("来电报告失败: \(error.localizedDescription)") } } } // 处理来电接听 func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) { // 跳转至自定义通话界面 action.fulfill() } // 处理来电挂断 func provider(_ provider: CXProvider, perform action: CXEndCallAction) { // 执行挂断逻辑 action.fulfill() } // 激活音频会话 func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) { do { try audioSession.setCategory(.playAndRecord, mode: .voiceChat) try audioSession.setActive(true) } catch { print("音频会话激活失败: \(error.localizedDescription)") } } // 停用音频会话 func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) { do { try audioSession.setActive(false) } catch { print("音频会话停用失败: \(error.localizedDescription)") } } }触发来电(从推送/后台事件中调用)
// 全局单例管理通话 let callManager = CallManager() // 收到来电推送时触发 func handleIncomingCall(contactName: String) { let callUUID = UUID() callManager.reportIncomingCall(uuid: callUUID, handle: contactName) // 解锁状态下可跳转至自定义全屏通话界面 }
注意事项
- 锁屏时系统会显示统一风格的来电界面,解锁后可切换至自定义全屏视图。
- 需确保功能真实用于语音/视频通话,否则苹果审核会被驳回。
内容的提问来源于stack exchange,提问作者Debug Dev
相关产品推荐
相关产品推荐

