Jetpack Compose前台服务悬浮窗异常:启动服务后应用崩溃
前台服务悬浮窗崩溃问题排查与解决
项目结构
- MyActivity.kt(主Activity):包含启动和停止服务的两个按钮
- App.kt(通知通道应用):继承Application类,用于创建通知通道
- Foreground.kt(前台服务):继承Service类
代码实现
class Foreground : Service() { private lateinit var windowManager:WindowManager private lateinit var floatingView : ComposeView @SuppressLint("ForegroundServiceType") override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { when (intent?.action){ "Start" -> startForeground(1,notification()) "Stop" -> stopSelf() } return START_NOT_STICKY } @SuppressLint("InflateParams") override fun onCreate() { super.onCreate() windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager floatingView = ComposeView(this) floatingView.setContent { myCanvas() } val params = WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT ) windowManager.addView(floatingView, params) } override fun onDestroy() { super.onDestroy() windowManager.removeView(floatingView) } private fun notification(): Notification { return NotificationCompat.Builder(this, "notification") .setSmallIcon(R.drawable.ic_launcher_foreground) .setContentTitle("Title") .setContentText("Text") .build() } override fun onBind(p0: Intent?): IBinder? = null } @Composable fun myCanvas() { Canvas( modifier = Modifier .fillMaxSize() .background(Color.Transparent) ) { drawRect( color = Color.Red, topLeft = center, size = Size(200.dp.toPx(), 200.dp.toPx()) ) } }
需求
启动服务时,显示一个覆盖在应用上方的透明悬浮窗,展示myCanvas()的内容。
问题现象
启动应用并点击“启动服务”按钮后,应用直接崩溃关闭,无悬浮窗显示,Android Studio Logcat出现报错。已在Manifest中声明服务,服务可正常显示通知;使用Android Studio HedgeHog 2023.1.1金丝雀版本,依赖为新项目默认配置。
已尝试方案
将myCanvas()移至MainActivity中调用,或放到继承AppCompatActivity/ComponentActivity的新类中,问题依旧。
问题原因及解决方案
1. 缺失悬浮窗权限
Android 6.0+ 中,悬浮窗需要SYSTEM_ALERT_WINDOW权限,该权限无法通过常规权限申请流程获取,必须引导用户手动开启:
// 在MyActivity的启动按钮点击事件中添加权限检查 if (!Settings.canDrawOverlays(this)) { val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName")) startActivityForResult(intent, 1001) } else { // 权限已获取,启动前台服务 val intent = Intent(this, Foreground::class.java).apply { action = "Start" } startForegroundService(intent) }
2. ComposeView 上下文适配问题
Service 上下文无法直接兼容 Compose 视图系统,且dp.toPx()调用需要依赖正确的密度上下文,修改myCanvas():
@Composable fun myCanvas() { val density = LocalDensity.current Canvas( modifier = Modifier .size(200.dp) .background(Color.Transparent) ) { drawRect( color = Color.Red, topLeft = center, size = Size(with(density) { 200.dp.toPx() }, with(density) { 200.dp.toPx() }) ) } }
3. 前台服务类型声明(Android 12+)
Android 12及以上版本,前台服务需指定类型,在Manifest中修改服务声明:
<service android:name=".Foreground" android:foregroundServiceType="systemExtras" />
同时移除onStartCommand上的@SuppressLint("ForegroundServiceType")注解。
4. 悬浮窗布局参数兼容
适配低版本系统的悬浮窗类型,并明确初始位置避免视图不可见:
val params = WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY } else { WindowManager.LayoutParams.TYPE_SYSTEM_ALERT }, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT ).apply { gravity = Gravity.TOP or Gravity.START x = 100 y = 100 }
内容的提问来源于stack exchange,提问作者Blitz
相关产品推荐
相关产品推荐

