当前位置: 首页 > news >正文

【Android】Android 悬浮窗开发 ( 动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

文章目录

  • 一、悬浮窗 动态权限请求
    • 1、动态请求权限
    • 2、悬浮窗权限说明
    • 3、检查动态权限
    • 4、申请动态权限
    • 5、权限设置完毕后返回处理
  • 二、悬浮窗 前台服务和通知
    • 1、前台服务 启动 悬浮窗 的必要性
      • ① 保持悬浮窗存活
      • ② 悬浮窗的要求
      • ③ 悬浮窗版本兼容
    • 2、其它类型服务简介
      • ① 前台服务
      • ② WorkManager 服务
      • ③ JobScheduler 服务
      • ④ AlarmManager 服务
  • 三、前台服务 创建 通知 和 悬浮窗
    • 1、启动前台服务
    • 2、前台服务通知
    • 3、创建浮动窗口
  • 四、完整代码示例
    • 1、Service 浮动窗口服务代码
    • 2、Activity 主界面代码
    • 3、AndroidManifest.xml 配置文件代码
    • 4、布局文件
    • 5、执行结果

悬浮窗实现效果 :

在这里插入图片描述





一、悬浮窗 动态权限请求




1、动态请求权限


在 Android 开发中 , 自 Android 6.0(API 级别 23)版本开始引入 " 动态权限 " ,

动态权限 指的是 在应用程序运行时向用户请求权限 , 而不是在安装时一次性请求所有权限 , 旨在提高用户隐私和安全性 ;


动态权限 请求 流程 :

  • 检查权限: 在请求权限之前,首先检查是否已经拥有该权限。
  • 请求权限: 如果没有权限,向用户请求权限。
  • 处理权限请求结果: 根据用户的响应,执行相应的操作。

2、悬浮窗权限说明


Settings.ACTION_MANAGE_OVERLAY_PERMISSION 是一个用于请求和管理 悬浮窗权限(Overlay Permission) 的系统设置页面 ;

悬浮窗权限允许应用在其他应用或系统界面上绘制悬浮窗口(如悬浮球、弹窗等);

由于悬浮窗权限涉及用户隐私和安全,Android 要求开发者显式请求该权限,并引导用户手动开启。


悬浮窗权限允许应用执行以下操作:

  • 在其他应用或系统界面上显示悬浮窗口。
  • 实现全局弹窗、悬浮按钮、画中画等功能。
  • 常用于录屏工具、悬浮球助手、消息提醒等场景。

3、检查动态权限


检查动态权限 , Android SDK 23 以上才检查动态权限 , 对应的版本是 Android 6.0(Marshmallow)‌‌, 低于该版本不需要 动态权限 , 直接使用对应功能即可 ,

通过 Build.VERSION.SDK_INT >= Build.VERSION_CODES.M 函数可以判定是否 当前版本 是否高于 Android SDK 23 Android 6.0(Marshmallow)‌‌版本 , 是否需要

通过调用 Settings.canDrawOverlays(this) 函数 , 可以检查是否浮云了 悬浮窗权限 , 如果是 Android 6.0 以上的系统 , 并且没有该 动态权限 , 则 动态请求该权限 ;

    /*** 检查悬浮窗权限的方法*/private fun checkOverlayPermission(): Boolean {// Android SDK23 对应的版本是 Android 6.0(Marshmallow)‌‌// 6.0 以上的 Android 系统需要动态申请权限if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) {/*根据当前应用是否有悬浮窗权限进行不同的操作- 如果 有 悬浮窗权限 直接返回 true 显示悬浮窗- 如果 没有悬浮窗权限, 开始请求悬浮窗权限*/if (!Settings.canDrawOverlays(this)) {// 没有悬浮窗权限, 开始请求悬浮窗权限requestOverlayPermission()return false} else {// 有 悬浮窗权限 直接返回 true 显示悬浮窗return true}} else {// 6.0 以下的 Android 系统不需要申请权限// 已经请求悬浮窗权限成功 可进行后续操作return true}}

4、申请动态权限


申请动态权限时 , 需要弹出一个对话框 , 提示用户要跳转到指定界面 , 进行某个设置 ;

在这里插入图片描述

这里需要跳转到 Settings.ACTION_MANAGE_OVERLAY_PERMISSION 权限设置界面 , 为某个应用开启 " 显示在其他应用的上层 " 权限 ;

在这里插入图片描述

在界面中 , 选中要设置的应用 , 设置该应用可以显示在其它应用的上层 ;

在这里插入图片描述

代码示例 :

    /*** 请求悬浮窗权限*/private fun requestOverlayPermission() {// 弹出 " 请允许显示在其他应用上方 " 的提示对话框AlertDialog.Builder(this) // 创建AlertDialog构建器.setTitle("需要悬浮窗权限") // 设置标题.setMessage("请允许显示在其他应用上方") // 设置消息.setPositiveButton("去设置") { _, _ -> // 设置“去设置”按钮val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, // 设置操作为管理悬浮窗权限Uri.parse("package:$packageName") // 设置URI为当前应用的包名)startActivityForResult(intent, OVERLAY_PERMISSION_REQUEST_CODE) // 启动设置界面,等待结果}.setNegativeButton("取消", null) // 设置“取消”按钮.show() // 显示对话框}

5、权限设置完毕后返回处理


设定一个请求码 , 自定义的请求码 , 用于 跳转到 申请 动态权限 页面 , 返回后判定返回结果 ;

    /*** 请求悬浮窗权限的请求码*/private val OVERLAY_PERMISSION_REQUEST_CODE = 1001

设置完 悬浮窗权限 后 , 从 Settings.ACTION_MANAGE_OVERLAY_PERMISSION 界面返回 , 会回调 onActivityResult 函数 , 返回后 再次验证 是否已经获得了 悬浮窗权限 ,

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {super.onActivityResult(requestCode, resultCode, data)if (requestCode == OVERLAY_PERMISSION_REQUEST_CODE) {// 如果权限请求成功, 会根据 请求码 命中该分支if (checkOverlayPermission()) { // 检查是否获得悬浮窗权限startFloatingService() // 启动悬浮窗服务}}}




二、悬浮窗 前台服务和通知




1、前台服务 启动 悬浮窗 的必要性


为什么必须用 前台服务 启动 悬浮窗 :

  • 系统兼容性 : Android 8.0+ 禁止后台应用直接显示悬浮窗,前台服务是唯一合法途径。
  • 资源保障 : 前台服务优先级更高,避免悬浮窗因进程被回收而消失。
  • 用户透明度 : 通知栏提示用户服务运行状态,符合隐私和设计规范。
  • 权限合规 : 减少 SYSTEM_ALERT_WINDOW 权限滥用风险,提升应用审核通过率。

如果不使用前台服务 , 会出现以下情况 :

  • 悬浮窗可能在后台被系统强制关闭。
  • 在 Android 12+ 设备上可能直接崩溃(权限拒绝)。
  • 用户可能误判应用为恶意软件(无通知提示)。

① 保持悬浮窗存活


Android 悬浮窗开发 , 需要 保证 悬浮窗 的持续存活 ,

  • 当 应用退到 后台时 , 通过 bindService 绑定的服务 就被系统回收了 , 悬浮窗就会消失 ;
  • Android 8.0 之后的系统 , 无法在后台创建 Activity 或 Window 组件 ;
  • 系统会限制后台的 CPU 和 网络资源 , 不定期杀死普通服务 ;
  • 使用 前台服务 , 可以避免上述三个问题 , 保证 悬浮窗持续存在 ;
场景问题前台服务的作用
应用退到后台普通 Service 可能被系统回收 → 悬浮窗消失前台服务优先级更高,系统更倾向于保留(即使内存不足) → 悬浮窗持续显示
Android 8.0+ 后台限制后台应用无法创建 ActivityWindow(如 TYPE_APPLICATION_OVERLAY前台服务属于“用户可见”状态 → 允许在后台显示悬浮窗
Doze 模式 / 应用待机系统限制后台应用的 CPU/网络等资源 → 普通服务可能被中断前台服务可绕过部分 Doze 限制 → 悬浮窗逻辑持续运行

② 悬浮窗的要求


在 Android 系统中 , 运行了一个 悬浮在 操作系统 中的 悬浮窗 , 这需要满足 悬浮窗相关权限 和 用户感知要求 , 要让用户知道是哪个应用启动了 悬浮窗 , 并且用户可以随时关闭该 悬浮窗 ;

使用 前台服务 可以满足上述要求 ;

要求前台服务的解决方案
权限依赖悬浮窗需要 SYSTEM_ALERT_WINDOW 权限,但 Android 10+ 要求动态申请并用户授权。前台服务通过通知栏提示用户应用正在运行,减少被系统判定为“滥用权限”的风险。
用户可感知性前台服务必须显示通知栏通知 → 用户明确知道悬浮窗关联的服务在运行(符合 Android 设计规范)。
避免后台限制从 Android 12 开始,后台应用启动前台服务需用户授权(START_FOREGROUND_SERVICES 权限),但启动后系统允许其显示悬浮窗。

③ 悬浮窗版本兼容


Android 系统中 , 不同的版本中 , 启动悬浮窗各自都有不同的限制 , 只有使用前台服务 , 可以满足所有的限制 , 因此前台服务在不同版本均有关键作用 , 所有的版本都可以使用 前台服务 启动 和 保持 悬浮窗 , 避免了不同 Android 系统版本 开发出的 悬浮窗 不兼容的问题 ;

Android 版本前台服务的关键作用
Android 8.0 (API 26)禁止后台应用创建 Window → 必须通过前台服务绑定悬浮窗逻辑。
Android 10 (API 29)禁止后台应用启动 Activity → 前台服务可绕过此限制显示悬浮窗。
Android 12 (API 31)前台服务需声明 foregroundServiceType(如 mediaPlayback)→ 明确服务用途,提升系统信任度。

2、其它类型服务简介


这里需要为 悬浮窗 设置一个绑定的服务 , 以确保悬浮窗一直保持存在 ;

服务类型使用场景特点
前台服务需要在后台持续运行且用户可感知的任务,如播放音乐、导航等。需要在通知栏显示持续的通知,告知用户服务正在运行。
WorkManager需要可靠执行的后台任务,即使应用退出或设备重启后仍需执行的任务,如上传日志、定期同步数据等。适用于需要持久性和可靠性的任务,支持链式任务、延迟执行、重试机制等特性。
JobScheduler需要在特定条件下执行的后台任务,如网络连接、设备充电等条件下执行的任务。适用于 Android 5.0(API 级别 21)及以上版本,允许在满足特定条件时调度任务。
AlarmManager需要在特定时间或周期性执行的任务,如定时提醒、定期同步等。适用于设置一次性任务、周期重复任务、定时重复任务。

① 前台服务


前台服务(Foreground Service):

  • 使用场景 : 适用于需要在后台持续运行且用户可感知的任务,如音乐播放、导航等。
  • 特点 : 必须显示一个持续的通知,确保用户知晓服务的存在。优先级高,不容易被系统杀死。
  • 优点 : 高优先级,系统不容易终止。 适用于需要用户知晓的长期运行任务 ;
  • 缺点 : 需要显示通知,可能影响用户体验。不适用于不需要用户感知的后台任务。

② WorkManager 服务


WorkManager 服务 :

  • 使用场景 : 适用于需要可靠执行的后台任务,即使应用退出或设备重启也能保证执行,如数据同步、上传日志等。
  • 特点 : 支持链式任务、延迟执行、重试机制等特性。兼容 Android 5.0(API 级别 21)及以上版本。 自动选择最佳的执行方式,适应设备状态和系统限制。
  • 优点 : 高可靠性,适用于需要持久化的任务。自动适配系统限制,确保任务执行。支持任务链式执行,方便管理复杂任务。
  • 缺点 : 相较于其他方式,可能引入额外的库和复杂性。对于简单的后台任务,可能显得过于复杂。

③ JobScheduler 服务


JobScheduler 服务 :

  • 使用场景 : 适用于需要在特定条件下执行的后台任务,如网络连接、充电状态等。
  • 特点 : 在 Android 5.0(API 级别 21)引入。允许根据设备状态和约束条件调度任务。
  • 优点 : 节省电池和资源,避免不必要的后台任务。适用于需要在特定条件下执行的任务。
  • 缺点 : 仅适用于 Android 5.0 及以上版本。功能相对有限,不如 WorkManager 灵活。

④ AlarmManager 服务


AlarmManager 服务 :

  • 使用场景 : 适用于需要在特定时间或周期性执行的任务,如定时提醒、定期同步等。
  • 特点 : 允许在指定时间或周期性触发任务。会唤醒设备执行任务,可能影响电池寿命。
  • 优点 : 适用于精确的定时任务。简单易用,适合定时提醒等场景。
  • 缺点 : 可能导致设备从低电耗模式中唤醒,影响电池寿命。在设备处于 Doze 模式或应用被限制时,可能无法按时执行任务。




三、前台服务 创建 通知 和 悬浮窗




1、启动前台服务


Android SDK 版本大于 26, Android 8.0 (Oreo) 需要 调用 startForegroundService 函数 启动 前台服务 , 前台服务 是 Android 8.0 之后才有的概念 , 之前 全都是 普通的 服务 , 只是通过 startService 和 bindService 两种启动方式 区别服务 ;

如果 Android 的 SDK 版本低于 26, Android 8.0 (Oreo) 则直接 调用 startService 函数 启动普通服务即可 ;


启动悬浮窗前台服务代码 :

    /*** 启动悬浮窗服务*/private fun startFloatingService() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // 如果 SDK 版本大于 26,  Android 8.0 (Oreo) 需要启动前台服务startForegroundService(Intent(this, FloatingWindowService::class.java)) // 启动前台服务} else {// 如果 SDK 版本低于 26,  Android 8.0 (Oreo) 则直接启动普通服务即可startService(Intent(this, FloatingWindowService::class.java)) // 启动普通服务}}

2、前台服务通知


Android SDK 版本大于 26 , 对应的系统版本是 Android 8.0 (Oreo) , 通过调用 startForegroundService 函数 启动 前台服务 , 必须在 启动服务 的 5 秒内 , 启动 前台通知 , 否则应用会崩溃退出 ;

启动通知代码如下 :

        // SDK 版本大于 26,  Android 8.0 (Oreo) , 才创建通知渠道, 并启动前台应用if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {createNotificationChannel()val notification = buildNotification()// 启动服务后, 必须在 5 秒内设置 前台服务通知信息startForeground(NOTIFICATION_ID, notification)}

首先 , 要创建 通知渠道 :

    /*** 创建通知渠道*  通知渠道是 SDK 26 Android 8.0 (Oreo) 引入的新特性*/private fun createNotificationChannel() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {// 创建通知渠道val channel = NotificationChannel(CHANNEL_ID,"悬浮窗",NotificationManager.IMPORTANCE_LOW)// 注册通知渠道getSystemService(NotificationManager::class.java).createNotificationChannel(channel)}}

然后 , 创建通知 :

    /*** 创建通知*/private fun buildNotification(): Notification {return NotificationCompat.Builder(this, CHANNEL_ID).setContentTitle("悬浮窗") // 设置通知标题.setContentText("显示前台悬浮窗服务") // 设置通知内容.setSmallIcon(R.mipmap.ic_launcher) // 设置通知小图标.setPriority(NotificationCompat.PRIORITY_LOW) // 设置通知优先级.build() // 构建并返回通知}

3、创建浮动窗口


创建浮动窗口流程 :

  • ① 设置布局类型 :
    • Android SDK 26 Android 8.0 (Oreo) 及以上的版本 , 需要设置 WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY 类型布局 ;
    • SDK 25 及以下的版本使用 WindowManager.LayoutParams.TYPE_PHONE 布局 ;
        // 获取 WindowManager 实例windowManager = getSystemService(WINDOW_SERVICE) as WindowManager// 设置布局类型val layoutFlag: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // 如果 SDK 版本大于等于 OWindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY // 设置布局类型为应用覆盖层} else {WindowManager.LayoutParams.TYPE_PHONE // 设置布局类型为电话}
  • ② 设置布局参数 :
        // 设置布局参数val params = WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, // 宽度自适应WindowManager.LayoutParams.WRAP_CONTENT, // 高度自适应layoutFlag, // 布局类型WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, // 不获取焦点PixelFormat.TRANSLUCENT // 半透明).apply {gravity = Gravity.TOP or Gravity.START // 设置重力为顶部和左侧x = 0 // 设置X坐标y = 0 // 设置Y坐标, 将浮动窗口显示在左上角}
  • ③ 加载浮动窗口布局 :
        // 加载 浮动窗口 布局val inflater = getSystemService(LAYOUT_INFLATER_SERVICE) as LayoutInflater // 获取LayoutInflater实例floatingView = inflater.inflate(R.layout.floating_window, null)      // 加载悬浮窗布局
  • ④ 设置浮动窗口事件 :
        // 设置关闭按钮的点击事件floatingView.findViewById<Button>(R.id.close_btn).setOnClickListener {stopSelf() // 停止服务}// 设置拖动事件floatingView.setOnTouchListener { view, event ->when (event.action) {MotionEvent.ACTION_DOWN -> { // 按下事件initialX = params.x // 记录初始X坐标initialY = params.y // 记录初始Y坐标initialTouchX = event.rawX // 记录初始触摸X坐标initialTouchY = event.rawY // 记录初始触摸Y坐标true}MotionEvent.ACTION_MOVE -> { // 移动事件params.x = initialX + (event.rawX - initialTouchX).toInt() // 更新X坐标params.y = initialY + (event.rawY - initialTouchY).toInt() // 更新Y坐标windowManager.updateViewLayout(floatingView, params) // 更新悬浮窗位置true}else -> false}}
  • ⑤ 添加浮动窗口 :
        // 正式添加悬浮窗到窗口windowManager.addView(floatingView, params)

完整代码如下 :

    /*** 创建悬浮窗口*/private fun createFloatingWindow() { // 创建悬浮窗的方法// 获取 WindowManager 实例windowManager = getSystemService(WINDOW_SERVICE) as WindowManager// 设置布局类型val layoutFlag: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // 如果 SDK 版本大于等于 OWindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY // 设置布局类型为应用覆盖层} else {WindowManager.LayoutParams.TYPE_PHONE // 设置布局类型为电话}// 设置布局参数val params = WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, // 宽度自适应WindowManager.LayoutParams.WRAP_CONTENT, // 高度自适应layoutFlag, // 布局类型WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, // 不获取焦点PixelFormat.TRANSLUCENT // 半透明).apply {gravity = Gravity.TOP or Gravity.START // 设置重力为顶部和左侧x = 0 // 设置X坐标y = 0 // 设置Y坐标, 将浮动窗口显示在左上角}// 加载 浮动窗口 布局val inflater = getSystemService(LAYOUT_INFLATER_SERVICE) as LayoutInflater // 获取LayoutInflater实例floatingView = inflater.inflate(R.layout.floating_window, null)      // 加载悬浮窗布局// 设置关闭按钮的点击事件floatingView.findViewById<Button>(R.id.close_btn).setOnClickListener {stopSelf() // 停止服务}// 设置拖动事件floatingView.setOnTouchListener { view, event ->when (event.action) {MotionEvent.ACTION_DOWN -> { // 按下事件initialX = params.x // 记录初始X坐标initialY = params.y // 记录初始Y坐标initialTouchX = event.rawX // 记录初始触摸X坐标initialTouchY = event.rawY // 记录初始触摸Y坐标true}MotionEvent.ACTION_MOVE -> { // 移动事件params.x = initialX + (event.rawX - initialTouchX).toInt() // 更新X坐标params.y = initialY + (event.rawY - initialTouchY).toInt() // 更新Y坐标windowManager.updateViewLayout(floatingView, params) // 更新悬浮窗位置true}else -> false}}// 正式添加悬浮窗到窗口windowManager.addView(floatingView, params)}




四、完整代码示例




1、Service 浮动窗口服务代码


浮动窗口所在 前台服务 代码 FloatingWindowService.kt :

package hsl.floatingwindowimport android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Intent
import android.graphics.PixelFormat
import android.os.Build
import android.os.IBinder
import android.view.*
import android.widget.Button
import androidx.core.app.NotificationCompatclass FloatingWindowService : Service() {/*** 窗口管理器*/private lateinit var windowManager: WindowManager/*** 悬浮窗组件*/private lateinit var floatingView: View/*声明 浮动窗口 的 初始坐标*/private var initialX = 0private var initialY = 0/*声明 浮动窗口 的 初始触摸坐标*/private var initialTouchX = 0fprivate var initialTouchY = 0f/*** 定义通知 ID*/private val NOTIFICATION_ID = 1001/*** 定义通知渠道 ID, 通知渠道需要*  调用 Service.createNotificationChannel 函数创建*/private val CHANNEL_ID = "floating_window_channel"/*** 重写 onBind 函数, 返回 null*/override fun onBind(intent: Intent?): IBinder? = nulloverride fun onCreate() {super.onCreate()// SDK 版本大于 26,  Android 8.0 (Oreo) , 才创建通知渠道, 并启动前台应用if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {createNotificationChannel()val notification = buildNotification()// 启动服务后, 必须在 5 秒内设置 前台服务通知信息startForeground(NOTIFICATION_ID, notification)}// 创建悬浮窗createFloatingWindow()}/*** 创建通知渠道*  通知渠道是 SDK 26 Android 8.0 (Oreo) 引入的新特性*/private fun createNotificationChannel() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {// 创建通知渠道val channel = NotificationChannel(CHANNEL_ID,"悬浮窗",NotificationManager.IMPORTANCE_LOW)// 注册通知渠道getSystemService(NotificationManager::class.java).createNotificationChannel(channel)}}/*** 创建通知*/private fun buildNotification(): Notification {return NotificationCompat.Builder(this, CHANNEL_ID).setContentTitle("悬浮窗") // 设置通知标题.setContentText("显示前台悬浮窗服务") // 设置通知内容.setSmallIcon(R.mipmap.ic_launcher) // 设置通知小图标.setPriority(NotificationCompat.PRIORITY_LOW) // 设置通知优先级.build() // 构建并返回通知}/*** 创建悬浮窗口*/private fun createFloatingWindow() { // 创建悬浮窗的方法// 获取 WindowManager 实例windowManager = getSystemService(WINDOW_SERVICE) as WindowManager// 设置布局类型val layoutFlag: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // 如果 SDK 版本大于等于 OWindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY // 设置布局类型为应用覆盖层} else {WindowManager.LayoutParams.TYPE_PHONE // 设置布局类型为电话}// 设置布局参数val params = WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, // 宽度自适应WindowManager.LayoutParams.WRAP_CONTENT, // 高度自适应layoutFlag, // 布局类型WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, // 不获取焦点PixelFormat.TRANSLUCENT // 半透明).apply {gravity = Gravity.TOP or Gravity.START // 设置重力为顶部和左侧x = 0 // 设置X坐标y = 0 // 设置Y坐标, 将浮动窗口显示在左上角}// 加载 浮动窗口 布局val inflater = getSystemService(LAYOUT_INFLATER_SERVICE) as LayoutInflater // 获取LayoutInflater实例floatingView = inflater.inflate(R.layout.floating_window, null)      // 加载悬浮窗布局// 设置关闭按钮的点击事件floatingView.findViewById<Button>(R.id.close_btn).setOnClickListener {stopSelf() // 停止服务}// 设置拖动事件floatingView.setOnTouchListener { view, event ->when (event.action) {MotionEvent.ACTION_DOWN -> { // 按下事件initialX = params.x // 记录初始X坐标initialY = params.y // 记录初始Y坐标initialTouchX = event.rawX // 记录初始触摸X坐标initialTouchY = event.rawY // 记录初始触摸Y坐标true}MotionEvent.ACTION_MOVE -> { // 移动事件params.x = initialX + (event.rawX - initialTouchX).toInt() // 更新X坐标params.y = initialY + (event.rawY - initialTouchY).toInt() // 更新Y坐标windowManager.updateViewLayout(floatingView, params) // 更新悬浮窗位置true}else -> false}}// 正式添加悬浮窗到窗口windowManager.addView(floatingView, params)}/*** 重写 onDestroy 方法*/override fun onDestroy() {super.onDestroy()if (::floatingView.isInitialized) { // 如果 floatingView 已初始化windowManager.removeView(floatingView) // 移除悬浮窗}}
}

2、Activity 主界面代码


下面是 Activity 主界面代码 MainActivity.kt , 主要作用就是 申请 浮动窗口所需权限 和 启动前台服务 ;

package hsl.floatingwindowimport android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivityclass MainActivity : AppCompatActivity() {/*** 请求悬浮窗权限的请求码*/private val OVERLAY_PERMISSION_REQUEST_CODE = 1001override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)// 检查是否具有悬浮窗权限if (checkOverlayPermission()) {// 启动悬浮窗服务startFloatingService()}}/*** 检查悬浮窗权限的方法*/private fun checkOverlayPermission(): Boolean {// Android SDK23 对应的版本是 Android 6.0(Marshmallow)‌‌// 6.0 以上的 Android 系统需要动态申请权限if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) {/*根据当前应用是否有悬浮窗权限进行不同的操作- 如果 有 悬浮窗权限 直接返回 true 显示悬浮窗- 如果 没有悬浮窗权限, 开始请求悬浮窗权限*/if (!Settings.canDrawOverlays(this)) {// 没有悬浮窗权限, 开始请求悬浮窗权限requestOverlayPermission()return false} else {// 有 悬浮窗权限 直接返回 true 显示悬浮窗return true}} else {// 6.0 以下的 Android 系统不需要申请权限// 已经请求悬浮窗权限成功 可进行后续操作return true}}/*** 请求悬浮窗权限*/private fun requestOverlayPermission() {// 弹出 " 请允许显示在其他应用上方 " 的提示对话框AlertDialog.Builder(this) // 创建AlertDialog构建器.setTitle("需要悬浮窗权限") // 设置标题.setMessage("请允许显示在其他应用上方") // 设置消息.setPositiveButton("去设置") { _, _ -> // 设置“去设置”按钮val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, // 设置操作为管理悬浮窗权限Uri.parse("package:$packageName") // 设置URI为当前应用的包名)startActivityForResult(intent, OVERLAY_PERMISSION_REQUEST_CODE) // 启动设置界面,等待结果}.setNegativeButton("取消", null) // 设置“取消”按钮.show() // 显示对话框}override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {super.onActivityResult(requestCode, resultCode, data)if (requestCode == OVERLAY_PERMISSION_REQUEST_CODE) {// 如果权限请求成功, 会根据 请求码 命中该分支if (checkOverlayPermission()) { // 检查是否获得悬浮窗权限startFloatingService() // 启动悬浮窗服务}}}/*** 启动悬浮窗服务*/private fun startFloatingService() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // 如果 SDK 版本大于 26,  Android 8.0 (Oreo) 需要启动前台服务startForegroundService(Intent(this, FloatingWindowService::class.java)) // 启动前台服务} else {// 如果 SDK 版本低于 26,  Android 8.0 (Oreo) 则直接启动普通服务即可startService(Intent(this, FloatingWindowService::class.java)) // 启动普通服务}}
}

3、AndroidManifest.xml 配置文件代码


在该 AndroidManifest.xml 配置文件中 , 主要需要声明 :

  • 权限声明 : 浮动窗口权限 和 前台服务权限 ;
  • Activity 组件声明
  • Service 组件声明
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="hsl.floatingwindow"><!-- 浮动窗口权限 --><uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /><!-- 前台服务权限 --><uses-permission android:name="android.permission.FOREGROUND_SERVICE" /><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.FloatingWindow"><!-- Activity 组件注册, 注意必须配置 android:exported="true" 属性, 否则报错 --><activity android:name=".MainActivity"android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><!-- Service 组件注册 --><service android:name=".FloatingWindowService" /></application></manifest>

4、布局文件


浮动窗口布局文件 floating_window.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/floating_layout"android:layout_width="200dp"android:layout_height="100dp"android:orientation="vertical"android:background="#80FFFFFF"android:padding="8dp"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="Floating Window"android:textSize="18sp"/><Buttonandroid:id="@+id/close_btn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Close"/></LinearLayout>

Activity 组件布局文件 activity_main.xml :

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Hello World!"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>

5、执行结果


执行效果 :

在这里插入图片描述

相关文章:

【Android】Android 悬浮窗开发 ( 动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

文章目录 一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后返回处理 二、悬浮窗 前台服务和通知1、前台服务 启动 悬浮窗 的必要性① 保持悬浮窗存活② 悬浮窗的要求③ 悬浮窗版本兼容 2、其它类型服务简介① 前台服务…...

【FAQ】HarmonyOS SDK 闭源开放能力 —Live View Kit (1)

1.问题描述&#xff1a; 客户端创建实况窗后&#xff0c;通过Push kit更新实况窗内容&#xff0c;这个过程是自动更新的还是客户端解析push消息数据后填充数据更新&#xff1f;客户端除了接入Push kit和创建实况窗还需要做什么工作&#xff1f; 解决方案&#xff1a; 通过Pu…...

基于Flask的第七次人口普查数据分析系统的设计与实现

【Flask】基于Flask的第七次人口普查数据分析系统的设计与实现&#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 基于Flask的人口普查可视化分析系统 二、项目界面展示 登录/注册 首页/详情 …...

java实现多图合并加字和画框等

java实现多图合并加字和画框等 在wutool中&#xff0c;封装了图片处理工具类&#xff0c;基于java自带的BufferedImage类&#xff0c;实现多图合并和加字、图片画框等。 关于wutool wutool是一个java代码片段收集库&#xff0c;针对特定场景提供轻量解决方案&#xff0c;只要…...

腾讯SQL面试题解析:如何找出连续5天涨幅超过5%的股票

腾讯SQL面试题解析:如何找出连续5天涨幅超过5%的股票 作者:某七年数据开发工程师 | 2025年02月23日 关键词:SQL窗口函数、连续问题、股票分析、腾讯面试题 一、问题背景与难点拆解 在股票量化分析场景中,"连续N天满足条件"是高频面试题类型。本题要求在单表stoc…...

Elasticsearch 自动补全搜索 - autocomplete

作者&#xff1a;来自 Elastic Amit Khandelwal 探索处理自动完成的不同方法&#xff0c;从基础到高级&#xff0c;包括输入时搜索、查询时间、完成建议器和索引时间。 在本文中&#xff0c;我们将介绍如何避免严重的性能错误、Elasticsearch 默认解决方案为何不适用以及重要的…...

大屏自适应终极方案:基于比例缩放的完美适配实践(Vue3版)

需求背景 在数据可视化大屏开发中&#xff0c;我们常面临这样的挑战&#xff1a;如何让1920*1080的设计稿在不同分辨率设备上完美呈现&#xff1f;传统的响应式布局难以应对复杂的大屏元素排布&#xff0c;本文介绍一种基于CSS3变换的终极适配方案 实现思路 本方案的核心是动…...

云计算中的API网关是什么?为什么它很重要?

在云计算架构中&#xff0c;API网关&#xff08;API Gateway&#xff09;是一个重要的组件&#xff0c;主要用于管理、保护和优化不同服务之间的接口&#xff08;API&#xff09;通信。简单来说&#xff0c;API网关就像是一个中介&#xff0c;它充当客户端和后端服务之间的“桥…...

前端ES面试题及参考答案

目录 let/const 与 var 的区别?TDZ 是什么? 箭头函数与普通函数的区别?箭头函数能否作为构造函数? 模板字符串的嵌套表达式和标签模板用法? 解构赋值的应用场景及对象 / 数组解构差异? 函数参数默认值的生效条件及暂时性死区问题? 展开运算符(...)在数组 / 对象中…...

【架构思维基础:如何科学定义问题】

架构思维基础&#xff1a;如何科学定义问题 一、问题本质认知 1.1 问题矛盾 根据毛泽东《矛盾论》&#xff0c;问题本质是系统内部要素间既对立又统一的关系。例如&#xff1a; 电商系统矛盾演变&#xff1a; 90年代&#xff1a;商品供给不足 vs 消费需求增长00年代&#x…...

【网络安全】常见的web攻击

1、SQL注入攻击 定义&#xff1a; 攻击者在HTTP请求中注入恶意的SQL代码&#xff0c;当服务器利用参数构建SQL语句的时候&#xff0c;恶意的SQL代码被一起构建,并在数据库中执行。 示例&#xff1a; 用户登录&#xff1a; 输入用户名xx&#xff0c; 密码 or 1 …...

ECOLOGY流程表单字段由单行文本改成多行文本

用户需要把单行文本改成多行文本 1、数据库端处理 select lwnr from FORMTABLE_MAIN_237 where lwnr is not null FORMTABLE_MAIN_237 有记录。DESCRIBE OA.FORMTABLE_MAIN_237 LWNR VARCHAR2(999) 由999改成4000 ALTER TABLE OA.FORMTA…...

AI发展迅速,是否还有学习前端的必要性?

今天有个小伙伴跟我讨论&#xff1a;“现在 AI 发展迅速&#xff0c;是否还有学习 JS 或者 TS 及前端知识的必要&#xff1f;” 我非常肯定地说&#xff1a; 是的&#xff0c;学习 JavaScript/TypeScript 以及前端知识仍然非常必要&#xff0c;而且在可预见的未来&#xff0c;…...

视频的分片上传

分片上传需求分析&#xff1a; 项目中很多地方需要上传视频&#xff0c;如果视频很大&#xff0c;上传到服务器需要很多时间 &#xff0c;这个时候体验就会很差。所以需要前端实现分片上传的功能。 要实现分片上传&#xff0c;需要对视频进行分割&#xff0c;分割成不同的大小…...

pyside6学习专栏(二):程序图像资源的加载方式

pyside6中的QLabel控件可以加载图像和gif动画&#xff0c;可以直接从外部文件加载&#xff0c;也可以从QRC类型的文件(实际是一脚本文件)经编绎生成对应的资源.PY模块文件(就是将qrc文本中指定的资源文件的16制内容写入.py文件)来使用&#xff0c;本文对两种方式作了一简单的示…...

QQ登录测试用例报告

QQ登录测试用例思维导图 一、安全性测试用例 1. 加密传输与存储验证 测试场景&#xff1a;输入账号密码并提交登录请求。预期结果&#xff1a;账号密码通过加密传输&#xff08;如HTTPS&#xff09;与存储&#xff08;如哈希加盐&#xff09;&#xff0c;无明文暴露。 2. 二…...

56. 合并区间 (LeetCode 热题 100)

题目来源&#xff1a; 56. 合并区间 - 力扣&#xff08;LeetCode&#xff09; 题目内容&#xff1a; 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&am…...

冒泡排序(详解)c++

冒泡排序(Bubble Sort)也是⼀种简单的排序算法。它的⼯作原理是每次检查相邻两个元素&#xff0c;如果前⾯ 的元素与后⾯的元素满⾜给定的排序条件&#xff0c;就将相邻两个元素交换。当没有相邻的元素需要交换时&#xff0c; 排序就完成了。 由于在算法的执⾏过程中&#xff0…...

CSS背景属性

<!DOCTYPE html> <html lang"zh-cn"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>CSS背景属性</title><style>.c1{/* …...

10种方法教你又小又清晰地压缩视频

视频压缩是有可能会损失画质的&#xff0c;但也可以通过一些方法尽量减少画质损失。在有效压缩视频大小的同时&#xff0c;尽量控制视频压缩画质在人眼无法察觉的范围内。下面就从10个角度向大家介绍10个不同的视频压缩方法&#xff0c;并推荐相关的视频压缩软件&#xff0c;整…...

当使用vcpkg安装的qt5时,在VS调用出现libcrypto-*-x64.dll不是有效路径时

英文解决站点 applocal.ps1 fails in Visual Studio 2019 because of wildcard path in VcpkgAppLocalDLLs Issue #28614 microsoft/vcpkg 虽然这个bug不影响生成exe文件,第一次会弹出该错误,再次运行就正常,vcpkg会把对应的libcrypto-*-x64.dll版本复制到exe路径下..但是对…...

AI大模型学习(四): LangChain(三)

Langchain构建代理 语言模型本身无法执行动作,他们只能输出文本,代理是使用大型语言模型(LLM)作为推理引擎来确定要执行的操作以及这些操作的输入应该是什么,然后这些操作的结果可以反馈到代理中,代理将决定是否需要更多的操作,或者是否可以结束 例如:我们想要查询现在北京的…...

JavaScript函数-arguments的使用

在JavaScript编程语言中&#xff0c;函数是构建复杂逻辑和实现代码复用的关键组件。虽然现代JavaScript&#xff08;尤其是ES6及之后版本&#xff09;提供了更多灵活的方式来处理函数参数&#xff08;如剩余参数、默认参数等&#xff09;&#xff0c;但arguments对象仍然是一个…...

C# 从基础神经元到实现在0~9数字识别

训练图片:mnist160 测试结果:1000次训练学习率为0.1时,准确率在60%以上 学习的图片越多&#xff0c;训练的时候越长(比如把 epochs*10 10000或更高时)效果越好 using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Windo…...

【每日八股】计算机网络篇(一):概述

OSI 的 7 层网络模型&#xff1f; OSI&#xff08;Open Systems Interconnection&#xff0c;开放互联系统&#xff09;是由国际标准化组织&#xff08;ISO&#xff09;提出的一种网络通信模型。 自上而下&#xff0c;OSI 可以被分为七层&#xff0c;分别是&#xff1a;应用层…...

Jupyter里面的manim编程学习

1.Jupyterlab的使用 因为我之前一直都是使用的vscode进行manim编程的&#xff0c;但是今天看的这个教程使用的是Jupyter&#xff0c;我也很是好奇这个manim在Jupyter这样的交互式下面会生成怎么样的效果&#xff0c;所以今天尝试了jupyter&#xff0c;并且对于两个进行比较和说…...

Web刷题之PolarDN(简单)

1.swp 考点&#xff1a;敏感文件、preg_match()函数绕过 什么是swp文件&#xff1a; vim中的swp即swap文件&#xff0c;在编辑文件时产生&#xff0c;它是隐藏文件。这个文件是一个临时交换文件&#xff0c;用来备份缓冲区中的内容。如果原文件名是data&#xff0c;那么swp文件…...

JVM 深入理解与性能优化

JVM&#xff08;Java Virtual Machine&#xff0c;Java 虚拟机&#xff09;是 Java 代码运行的核心&#xff0c;它负责 内存管理、类加载、字节码执行、垃圾回收&#xff08;GC&#xff09;和 JIT 编译。理解 JVM 有助于优化 Java 应用的性能&#xff0c;提高调试和排错能力。 …...

【人工智能】蓝耘智算平台盛大发布DeepSeek满血版:开创AI推理体验新纪元

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀ 蓝耘智算平台 蓝耘智算平台核心技术与突破元生代推理引擎快速入门&#xff1a;三步调用大模型接口&#xff0c;OpenAI SDK无缝兼容实战用例文…...

minio作为K8S后端存储

docker部署minio mkdir -p /minio/datadocker run -d \-p 9000:9000 \-p 9001:9001 \--name minio \-v /minio/data:/data \-e "MINIO_ROOT_USERjbk" \-e "MINIO_ROOT_PASSWORDjbjbjb123" \quay.io/minio/minio server /data --console-address ":90…...

关于 BK3633 上电时受串口 UART2 影响而无法启动的问题说明

1. 问题描述 BK3633 SDK 版本&#xff1a;BK3633_DesignKit_V06_2310 使用 BK3633 UART2 与指纹模块进行通讯&#xff0c;为了降低功耗&#xff0c;通过 GPIO 控制了指纹模块的供电电源。但每次给整个系统板子上电时&#xff0c;BK3633 很大概率会实际而无法正常运行程序&…...

运维Ansible面试题及参考答案

目录 简述 Ansible 的工作原理,它是如何实现对远程主机管理的? Ansible 是基于什么语言开发的?这门语言的特性对 Ansible 的功能实现有哪些帮助? 解释 Agentless 在 Ansible 中的含义,与基于 Agent 的自动化工具相比,优势体现在哪? Ansible 中的 Inventory 文件是什…...

Spring Boot 日志管理(官网文档解读)

摘要 本篇文章详细介绍了SpringBoot 日志管理相关的内容&#xff0c;文章主要参考官网文章的描述内容&#xff0c;并在其基础上进行一定的总结和拓展&#xff0c;以方便学习Spring Boot 的小伙伴能快速掌握Spring Boot 日志管理相关的内容。 日志实现方式 Sping Boot 的日志管…...

Svelte 最新中文文档教程(17)—— 生命周期钩子

前言 Svelte&#xff0c;一个语法简洁、入门容易&#xff0c;面向未来的前端框架。从 Svelte 诞生之初&#xff0c;就备受开发者的喜爱&#xff0c;根据统计&#xff0c;从 2019 年到 2024 年&#xff0c;连续 6 年一直是开发者最感兴趣的前端框架 No.1&#xff1a; Svelte 以…...

Windows 上源码安装 FastGPT

FastGPT 是一个强大的 AI RAG 平台&#xff0c;值得我们去学习了解。与常见的 Python 体系不同&#xff0c;Fast GPT 采用 Node.js/Next.js 平台&#xff08;对于广大 JS 开发者或前端开发者比较亲切友好&#xff09;&#xff0c;安装或部署比较简单。虽然一般情况下推荐简单的…...

RT-Thread+STM32L475VET6——icm20608传感器

文章目录 前言一、板载资源二、具体步骤1.打开CubeMX进行配置1.1 使用外部高速时钟&#xff0c;并修改时钟树1.2 打开I2C3&#xff0c;参数默认即可(I2C根据自己需求调整&#xff09;1.3 打开串口1.4 生成工程 2. 添加icm20608软件包3. 使能传感器&#xff0c;打开动态链接库4.…...

超高清大图渲染性能优化实战:从页面卡死到流畅加载

目录 问题背景&#xff1a;1.为什么大图会导致页面卡死&#xff1f;一、DOM树构建&#xff08;HTML Parsing&#xff09;二、 资源加载&#xff1a;下载完整图片文件&#xff08;可能高达30MB&#xff09;三、解码处理&#xff08;Decoding & Rasterization&#xff09;、四…...

基于javaweb的SpringBoot个人博客系统设计和实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…...

轻量级日志管理平台Grafana Loki

文章目录 轻量级日志管理平台Grafana Loki背景什么是Loki为什么使用 Grafana Loki&#xff1f;架构Log Storage Grafana部署使用基于 Docker Compose 安装 LokiMinIO K8s集群部署Loki采集Helm 部署方式和案例 参考 轻量级日志管理平台Grafana Loki 背景 在微服务以及云原生时…...

SOME/IP--协议英文原文讲解12(完结)

前言 SOME/IP协议越来越多的用于汽车电子行业中&#xff0c;关于协议详细完全的中文资料却没有&#xff0c;所以我将结合工作经验并对照英文原版协议做一系列的文章。基本分三大块&#xff1a; 1. SOME/IP协议讲解 2. SOME/IP-SD协议讲解 3. python/C举例调试讲解 4.3 Compa…...

Linux 命令大全完整版(12)

Linux 命令大全 5. 文件管理命令 ln(link) 功能说明&#xff1a;连接文件或目录。语  法&#xff1a;ln [-bdfinsv][-S <字尾备份字符串>][-V <备份方式>][--help][--version][源文件或目录][目标文件或目录] 或 ln [-bdfinsv][-S <字尾备份字符串>][-V…...

AI学习第二,三天-Python基础

变量、运算符与数据类型详解 注释 在 Python 中&#xff0c;注释是用于增加代码可读性、解释代码功能但不会被程序执行的部分。 单行注释&#xff1a;使用 # 符号&#xff0c;从 # 开始到本行末尾的内容均为注释。例如&#xff1a; 收起 python # 这是一个单行注释&#xff…...

Nginx代理ElasticSearch

1、将ES的账号:密码通过Base64加密 假设账号密码如下&#xff1a; 账号&#xff1a;elastic密码&#xff1a;elastichuayunworld.com echo -n elastic:elastichuayunworld.com | base64 ZWxhc3RpYzplbGFzdGljQGh1YXl1bndvcmxkLmNvbQ2、在 Nginx 配置中传递认证信息 locatio…...

开源AI网络爬虫工具Crawl4AI

引言 在信息化时代&#xff0c;网络爬虫作为从互联网中提取信息的重要工具&#xff0c;扮演着至关重要的角色。Crawl4AI作为一款开源AI网络爬虫工具&#xff0c;凭借其功能强大和易用性&#xff0c;受到了广泛关注。本文将详细探讨Crawl4AI的定义、特点、优势&#xff0c;以及…...

实现 INFINI Console 与 GitHub 的单点登录集成:一站式身份验证解决方案

本文将为您详细解析如何通过 GitHub OAuth 2.0 协议&#xff0c;为 INFINI Console 实现高效、安全的单点登录&#xff08;Single Sign-On, SSO&#xff09;集成。通过此方案&#xff0c;用户可直接使用 GitHub 账户无缝登录 INFINI Console&#xff0c;简化身份验证流程&#…...

Linux系统安装MySQL5.7(其他版本类似)避坑指南

1.远程连接 在Linux系统安装好MySQL5.7数据库&#xff0c;不要以为就大功告成了后面还有大坑等着你踩了。宏哥这里介绍一下远程连接遇到的坑以及如何处理。由于征文要求安装环境教学除外宏哥这里就不介绍在Linux系统安装mysql数据库&#xff0c;有需要的可以自己百度一下。但是…...

新数据结构(12)——代理

什么是代理 在进行操作时有时不希望用户直接接触到目标&#xff0c;这时需要使用代理让用户间接接触到目标 给目标对象提供一个代理对象&#xff0c;并且由代理对象控制着对目标对象的引用 图解&#xff1a; 代理的目的 控制访问&#xff1a;通过代理对象的方式间接的访问目…...

VLM(视觉语言模型)与DeepSeek R1(奖励机制)如何结合

VLM&#xff08;视觉语言模型&#xff09;与DeepSeek R1&#xff08;奖励机制&#xff09;如何结合 flyfish VLM的传统训练依赖于监督学习&#xff08;直接拟合问答对&#xff09;&#xff0c;而规则奖励函数通常用于强化学习&#xff08;通过试错和奖励反馈优化策略&#xf…...

问题:Flask应用中的用户会话(Session)管理失效

我来分享一个常见的PythonWeb开发问题&#xff1a; 问题&#xff1a;Flask应用中的用户会话(Session)管理失效 这是一个在Flask开发中经常遇到的问题。当用户登录后&#xff0c;有时会话会意外失效&#xff0c;导致用户需要重复登录。 解决方案&#xff1a; 1. 首先&#x…...

Qt/C++面试【速通笔记一】

Qt 信号与槽机制 什么是信号&#xff08;Signal&#xff09;和槽&#xff08;Slot&#xff09;&#xff1f; 在Qt中&#xff0c;信号&#xff08;Signal&#xff09;和槽&#xff08;Slot&#xff09;是实现对象之间通信的一种机制。信号是对象在某些事件发生时发出的通知&…...