安卓锁屏/休眠时即时接收服务器消息的服务应用开发方案咨询
最佳实现方案与代码示例
首先明确:主流即时通讯APP(如Whatsapp、Telegram)的核心通知机制是系统级推送服务(FCM/APNs),而非WebSocket。以下是详细分析和实现代码:
一、方案对比:Push Notification vs WebSocket
Push Notification(系统推送)
- 优势:系统原生支持,锁屏下直接由系统触发通知,无需APP保持后台进程;厂商对系统推送优先级极高,几乎不会被后台限制影响,兼容性覆盖所有设备。
- 不足:极端情况下可能有秒级延迟,但主流服务商(FCM/APNs)的即时性已经能满足绝大多数场景需求。
WebSocket长连接
- 优势:理论上双向通信即时性更高。
- 不足:后台保活难度极大——Android的Doze模式、厂商后台查杀,iOS的后台进程限制,都会导致连接断开,无法保证锁屏下稳定运行,还会额外增加设备耗电量。
二、最佳实践
优先采用系统推送服务作为核心通知机制,仅在APP前台在线时用WebSocket做补充(比如实时聊天场景)。这种组合既能保证锁屏下的即时通知,又能兼顾前台的低延迟体验。
示例代码
一、Android端(FCM系统推送 + WebSocket前台补充)
1. FCM推送接收与锁屏通知展示
// MyFirebaseMessagingService.kt class MyFirebaseMessagingService : FirebaseMessagingService() { override fun onMessageReceived(remoteMessage: RemoteMessage) { super.onMessageReceived(remoteMessage) // 构建锁屏可见的通知 val notificationBuilder = NotificationCompat.Builder(this, "IM_CHANNEL") .setSmallIcon(R.drawable.ic_notification) .setContentTitle(remoteMessage.notification?.title ?: "新消息") .setContentText(remoteMessage.notification?.body ?: "您收到一条新消息") .setPriority(NotificationCompat.PRIORITY_HIGH) .setAutoCancel(true) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) // 强制锁屏下显示完整内容 val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // Android 8.0+ 必须创建通知渠道 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( "IM_CHANNEL", "即时消息通知", NotificationManager.IMPORTANCE_HIGH ) notificationManager.createNotificationChannel(channel) } notificationManager.notify(System.currentTimeMillis().toInt(), notificationBuilder.build()) } }
2. WebSocket前台连接示例(基于OkHttp)
// WebSocketManager.kt class WebSocketManager(private val context: Context) { private var webSocket: WebSocket? = null fun connect() { val client = OkHttpClient() val request = Request.Builder().url("wss://your-server-domain/ws").build() webSocket = client.newWebSocket(request, object : WebSocketListener() { override fun onOpen(webSocket: WebSocket, response: Response) { // 连接成功后发送身份验证 webSocket.send("auth:user_12345") } override fun onMessage(webSocket: WebSocket, text: String) { // 前台收到消息时弹出通知 showForegroundNotification(text) } override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { // 处理连接失败,可加入重连逻辑 } }) } private fun showForegroundNotification(message: String) { val notificationBuilder = NotificationCompat.Builder(context, "IM_CHANNEL") .setSmallIcon(R.drawable.ic_notification) .setContentTitle("实时消息") .setContentText(message) .setPriority(NotificationCompat.PRIORITY_HIGH) .setAutoCancel(true) val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.notify(System.currentTimeMillis().toInt()+1, notificationBuilder.build()) } fun disconnect() { webSocket?.close(1000, "主动断开连接") } }
二、iOS端(APNs系统推送 + WebSocket前台补充)
1. APNs推送接收与通知展示
// AppDelegate.swift import UIKit import UserNotifications @main class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 请求通知权限 UNUserNotificationCenter.current().delegate = self let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] UNUserNotificationCenter.current().requestAuthorization(options: authOptions) { granted, _ in if granted { DispatchQueue.main.async { application.registerForRemoteNotifications() } } } return true } // 后台/锁屏下接收推送 func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { if let aps = userInfo["aps"] as? [String: Any], let alert = aps["alert"] as? [String: String] { let title = alert["title"] ?? "新消息" let body = alert["body"] ?? "您收到一条新消息" showNotification(title: title, body: body) } completionHandler(.newData) } // 前台接收推送时显示通知 func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { completionHandler([.alert, .sound, .badge]) } private func showNotification(title: String, body: String) { let content = UNMutableNotificationContent() content.title = title content.body = body content.sound = .default content.badge = UIApplication.shared.applicationIconBadgeNumber + 1 as NSNumber let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil) UNUserNotificationCenter.current().add(request) } }
2. WebSocket前台连接示例(基于Starscream)
// WebSocketManager.swift import Starscream class WebSocketManager: WebSocketDelegate { private var webSocket: WebSocket? func connect() { guard let url = URL(string: "wss://your-server-domain/ws") else { return } var request = URLRequest(url: url) request.timeoutInterval = 5 webSocket = WebSocket(request: request) webSocket?.delegate = self webSocket?.connect() } func didReceive(event: WebSocketEvent, client: WebSocket) { switch event { case .connected(let headers): print("WebSocket连接成功: \(headers)") webSocket?.write(string: "auth:user_12345") case .text(let message): print("收到WebSocket消息: \(message)") showForegroundNotification(message: message) case .disconnected(let reason, let code): print("WebSocket断开: \(reason) 代码: \(code)") case .error(let error): print("WebSocket错误: \(error?.localizedDescription ?? "未知错误")") default: break } } private func showForegroundNotification(message: String) { let content = UNMutableNotificationContent() content.title = "实时消息" content.body = message content.sound = .default content.badge = UIApplication.shared.applicationIconBadgeNumber + 1 as NSNumber let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil) UNUserNotificationCenter.current().add(request) } func disconnect() { webSocket?.disconnect() } }
内容的提问来源于stack exchange,提问作者Dinamo
相关产品推荐
相关产品推荐

