Android 后台启动Activity适配
在Android 9及以下版本,后台启动Activity相对自由,但是如果在Activity上下文之外启动Activity会有限制。
Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag
所以此时需要给intent添加flag:FLAG_ACTIVITY_NEW_TASK。
在Android版本10及以后版本, 引入了后台执行限制,限制了应用在后台执行操作的能力。非核心任务的后台启动 Activity 可能会受到限制。详情可参见官方文档:从后台启动 Activity 的限制。
根据文档可知,大致有两种方案可实现从后台启动Activity。
方案一:设置全屏Notification
设置Notification时通过setFullScreenIntent添加一个全屏Intent对象,可以在Android 10上从后台启动一个Activity界面,需要在Manifest.xml清单文件中加上:
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/>
示例代码如下:
private fun getChannelNotificationQ(context: Context, title: String?, content: String?): Notification {val fullScreenPendingIntent = PendingIntent.getActivity(context,0,DemoActivity.genIntent(context),PendingIntent.FLAG_UPDATE_CURRENT)val notificationBuilder = NotificationCompat.Builder(context, ID).setSmallIcon(R.drawable.ic_launcher_foreground).setContentTitle(title).setContentText(content).setPriority(NotificationCompat.PRIORITY_MAX).setCategory(Notification.CATEGORY_CALL).setOngoing(true).setFullScreenIntent(fullScreenPendingIntent, true)return notificationBuilder.build()}
方案二:获取SYSTEM_ALERT_WINDOW权限
如果用户已向应用授予SYSTEM_ALERT_WINDOW权限,则可以在后台启动Activity。在 Android 10 Go 版本中,应用已经无法直接获得SYSTEM_ALERT_WINDOW权限。不过Android引入了一种称为"Display over other apps"(在其他应用上层显示)的新权限体系。这种新的权限体系允许应用请求"TYPE_APPLICATION_OVERLAY"类型的窗口权限。申请步骤如下:
在Manifest.xml清单文件中加上:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
代码中发起请求权限申请:
if (!Settings.canDrawOverlays(this)) {Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);intent.setData(Uri.parse("package:" + getPackageName()));startActivityForResult(intent, 0);
}
定制化ROM新权限
有些机型增加了一项权限——后台弹出界面,比如在华为、 小米等设备上便新增了这项权限,且默认是关闭的,除非加入了它们的白名单。而且如果权限是关闭的,那么前面所说的两种方案将无效。所以在这些机型上,必须获取后台弹出界面权限,才能够从后台启动Activity。
判断是否获取弹出界面权限:
object PopBackgroundPermissionUtil {private const val TAG = "PopPermissionUtil"private const val HW_OP_CODE_POPUP_BACKGROUND_WINDOW = 100000private const val XM_OP_CODE_POPUP_BACKGROUND_WINDOW = 10021/*** 是否有后台弹出页面权限*/fun hasPopupBackgroundPermission(): Boolean {if (isHuawei()) {return checkHwPermission()}if (isXiaoMi()) {return checkXmPermission()}if (isVivo()) {checkVivoPermission()}if (isOppo() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {return Settings.canDrawOverlays(sContext)}return true}fun isHuawei(): Boolean {return checkManufacturer("huawei")}fun isXiaoMi(): Boolean {return checkManufacturer("xiaomi")}fun isOppo(): Boolean {return checkManufacturer("oppo")}fun isVivo(): Boolean {return checkManufacturer("vivo")}private fun checkManufacturer(manufacturer: String): Boolean {return manufacturer.equals(Build.MANUFACTURER, true)}private fun checkHwPermission(): Boolean {val context = sContexttry {val c = Class.forName("com.huawei.android.app.AppOpsManagerEx")val m = c.getDeclaredMethod("checkHwOpNoThrow",AppOpsManager::class.java,Int::class.javaPrimitiveType,Int::class.javaPrimitiveType,String::class.java)val result = m.invoke(c.newInstance(),*arrayOf(context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager,HW_OP_CODE_POPUP_BACKGROUND_WINDOW,Binder.getCallingUid(),context.packageName)) as IntLog.d(TAG,"PopBackgroundPermissionUtil checkHwPermission result:" + (AppOpsManager.MODE_ALLOWED == result))return AppOpsManager.MODE_ALLOWED == result} catch (e: Exception) {//ignore}return false}private fun checkXmPermission(): Boolean {val context = sContextval ops = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManagertry {val method = ops.javaClass.getMethod("checkOpNoThrow", *arrayOf<Class<*>?>(Int::class.javaPrimitiveType, Int::class.javaPrimitiveType, String::class.java))val result = method.invoke(ops,XM_OP_CODE_POPUP_BACKGROUND_WINDOW,Process.myUid(),context.packageName) as IntLog.d(TAG,"PopBackgroundPermissionUtil checkXmPermission result:" + (AppOpsManager.MODE_ALLOWED == result))return result == AppOpsManager.MODE_ALLOWED} catch (e: Exception) {//ignore}return false}private fun checkVivoPermission(): Boolean {val context = sContextval uri =Uri.parse("content://com.vivo.permissionmanager.provider.permission/start_bg_activity")val selection = "pkgname = ?"val selectionArgs = arrayOf(context.packageName)var result = 1val contentResolver = context.contentResolvertry {contentResolver.query(uri, null, selection, selectionArgs, null).use { cursor ->if (cursor!!.moveToFirst()) {result = cursor.getInt(cursor.getColumnIndex("currentstate"))}}} catch (exception: Exception) {//ignore}Log.d(TAG,"PopBackgroundPermissionUtil checkVivoPermission result:" + (AppOpsManager.MODE_ALLOWED == result))return result == AppOpsManager.MODE_ALLOWED}}
跳转弹出界面权限界面:
class SystemAlertWindow(private val mSource: Activity) {fun start(requestCode: Int) {var intent: Intent?intent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {if (MARK.contains("meizu")) {meiZuApi(mSource)} else {MdefaultApi(mSource)}} else {if (MARK.contains("huawei")) {huaweiApi(mSource)} else if (MARK.contains("xiaomi")) {xiaomiApi(mSource)} else if (MARK.contains("oppo")) {oppoApi(mSource)} else if (MARK.contains("vivo")) {vivoApi(mSource)} else if (MARK.contains("meizu")) {meizuApi(mSource)} else {LdefaultApi(mSource)}}try {mSource.startActivityForResult(intent, requestCode)} catch (e: Exception) {intent = appDetailsApi(mSource)mSource.startActivityForResult(intent, requestCode)}}private fun huaweiApi(context: Context): Intent? {val intent = Intent()intent.setClassName("com.huawei.systemmanager","com.huawei.permissionmanager.ui.MainActivity")if (hasActivity(context, intent)) {return intent}intent.setClassName("com.huawei.systemmanager","com.huawei.systemmanager.addviewmonitor.AddViewMonitorActivity")if (hasActivity(context, intent)) {return intent}intent.setClassName("com.huawei.systemmanager","com.huawei.notificationmanager.ui.NotificationManagmentActivity")return if (hasActivity(context, intent)) {intent} else MdefaultApi(context)}private fun xiaomiApi(context: Context): Intent? {val intent = Intent("miui.intent.action.APP_PERM_EDITOR")intent.putExtra("extra_pkgname", context.packageName)if (hasActivity(context, intent)) {return intent}intent.setClassName("com.miui.securitycenter","com.miui.permcenter.permissions.AppPermissionsEditorActivity")return if (hasActivity(context, intent)) {intent} else MdefaultApi(context)}private fun oppoApi(context: Context): Intent? {val intent = Intent()intent.putExtra("packageName", context.packageName)intent.setClassName("com.color.safecenter","com.color.safecenter.permission.floatwindow.FloatWindowListActivity")if (hasActivity(context, intent)) {return intent}intent.setClassName("com.coloros.safecenter","com.coloros.safecenter.sysfloatwindow.FloatWindowListActivity")if (hasActivity(context, intent)) {return intent}intent.setClassName("com.oppo.safe", "com.oppo.safe.permission.PermissionAppListActivity")return if (hasActivity(context, intent)) {intent} else MdefaultApi(context)}private fun vivoApi(context: Context): Intent? {val intent = Intent()intent.setClassName("com.iqoo.secure","com.iqoo.secure.ui.phoneoptimize.FloatWindowManager")intent.putExtra("packagename", context.packageName)if (hasActivity(context, intent)) {return intent}intent.setClassName("com.iqoo.secure","com.iqoo.secure.safeguard.SoftPermissionDetailActivity")return if (hasActivity(context, intent)) {intent} else MdefaultApi(context)}private fun meizuApi(context: Context): Intent? {val intent = Intent("com.meizu.safe.security.SHOW_APPSEC")intent.putExtra("packageName", context.packageName)intent.component = ComponentName("com.meizu.safe", "com.meizu.safe.security.AppSecActivity")return if (hasActivity(context, intent)) {intent} else MdefaultApi(context)}companion object {private val MARK = Build.MANUFACTURER.lowercase(Locale.getDefault())const val REQUEST_OVERLY = 7562private fun LdefaultApi(context: Context): Intent {val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)intent.data = Uri.fromParts("package", context.packageName, null)return intent}private fun appDetailsApi(context: Context): Intent {val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)intent.data = Uri.fromParts("package", context.packageName, null)return intent}private fun MdefaultApi(context: Context): Intent? {var intent: Intent? = nullif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)}intent!!.data = Uri.fromParts("package", context.packageName, null)return if (hasActivity(context, intent)) {intent} else appDetailsApi(context)}private fun meiZuApi(context: Context): Intent? {val intent = Intent("com.meizu.safe.security.SHOW_APPSEC")intent.putExtra("packageName", context.packageName)intent.setClassName("com.meizu.safe", "com.meizu.safe.security.AppSecActivity")return if (hasActivity(context, intent)) {intent} else MdefaultApi(context)}private fun hasActivity(context: Context, intent: Intent?): Boolean {val packageManager = context.packageManagerreturn packageManager.queryIntentActivities(intent!!,PackageManager.MATCH_DEFAULT_ONLY).size > 0}}
}
权限说明
“Draw Over Other Apps”(在其他应用上层绘制)权限:
这个权限允许应用在其他应用的上层绘制悬浮窗口,例如悬浮通知、悬浮工具栏、聊天头像等。通过这个权限,应用可以在其他应用的界面上显示自己的内容,但是这些窗口通常会有一定的限制,不会覆盖系统级别的UI元素(如状态栏、导航栏等)。
“Background Pop-ups”(后台弹窗)权限:
这个权限控制应用在后台是否允许弹出窗口,即使应用处于后台运行状态。这意味着即使应用不在前台,它仍然可以显示一些弹窗、通知或者提醒。这可以让应用在后台运行时继续向用户展示重要的信息。
总结
- 先判断是否是特殊机型,如果是则需要申请后台弹出界面权限
- 如果不是特殊机型,则有两种方案,一是全屏通知,二是申请在其他应用上层绘制权限
相关文章:
Android 后台启动Activity适配
在Android 9及以下版本,后台启动Activity相对自由,但是如果在Activity上下文之外启动Activity会有限制。 Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag所以此时需要给intent添加flag&#x…...

使用element-ui中的el-table回显已选中数据时toggleRowSelection报错
最近在写一个后台,需要在表格中多选,然后点击编辑按钮的时候,需要回显已经选中的表单项 <el-table v-loading"loading" :data"discountList" :row-key"(row) > row.id" refmultipleTable selection-cha…...

Ubuntu18.04系统下通过ROS控制Kinova真实机械臂-多种实现方式
所用测试工作空间test_ws:包含官网最原始的功能包 一、使用Kinova官方Development center控制真实机械臂 0.在ubuntu系统安装Kinova机械臂的Development center,这一步自行安装,很简单。 1.使用USB连接机械臂和电脑 2.Development center…...

聊聊如何玩转spring-boot-admin
前言 1、何为spring-boot-admin? Spring Boot Admin 是一个监控工具,旨在以良好且易于访问的方式可视化 Spring Boot Actuators 提供的信息 快速开始 如何搭建spring-boot-admin-server 1、在服务端项目的POM引入相应的GAV <dependency><grou…...

rocky(centos) 安装redis,并设置开机自启动
一、下载并安装 1、官网下载Redis 并安装 Download | RedisRedisYou can download the last Redis source files here. For additional options, see the Redis downloads section below.Stable (7.2)Redis 7.2 …https://redis.io/download/ 2、上传下载好的redis压缩包到 /…...

Flask狼书笔记 | 06_电子邮件
文章目录 6 电子邮件6.1 使用Flask-Mail发送6.2 使用事务邮件服务SendGrid6.3 电子邮件进阶6.4 小结 6 电子邮件 Web中,我们常在用户注册账户时发送确认邮件,或是推送信息。邮件必要的字段包含发信方(sender),收信方(to),邮件主题…...

ChatGPT追祖寻宗:GPT-1论文要点解读
论文地址:《Improving Language Understanding by Generative Pre-Training》 最近一直忙着打比赛,好久没更文了。这两天突然想再回顾一下GPT-1和GPT-2的论文, 于是花时间又整理了一下,也作为一个记录~话不多说,让我们…...

回归拟合 | 灰狼算法优化核极限学习机(GWO-KELM)MATLAB实现
这周有粉丝私信想让我出一期GWO-KELM的文章,因此乘着今天休息就更新了(希望不算晚) 作者在前面的文章中介绍了ELM和KELM的原理及其实现,ELM具有训练速度快、复杂度低、克服了传统梯度算法的局部极小、过拟合和学习率的选择不合适等优点,而KEL…...

Mysql JSON
select json_extract(c2, $.a) select c2->"$.a" // json_extract的语法糖 (取出的值会保留"双引号" so不适合实战) 注:mysql若是引擎Mariadb则不支持json操作符->>语法糖 select c2->…...

使用Vue + axios实现图片上传,轻松又简单
目录 一、Vue框架介绍 二、Axios 介绍 三、实现图片上传 四、Java接收前端图片 一、Vue框架介绍 Vue是一款流行的用于构建用户界面的开源JavaScript框架。它被设计用于简化Web应用程序的开发,特别是单页面应用程序。 Vue具有轻量级、灵活和易学的特点…...

C# 中什么是重写(子类改写父类方法)
方法重写是指在继承关系中,子类重新实现父类或基类的某个方法。这种方法允许子类根据需要修改或扩展父类或基类的方法功能。在面向对象编程中,方法重写是一种多态的表现形式,它使得子类可以根据不同的需求和场景提供不同的方法实现。 方法重…...

【Leetcode-面试经典150题-day22】
目录 97. 交错字符串 97. 交错字符串 题意: 给定三个字符串 s1、s2、s3,请你帮忙验证 s3 是否是由 s1 和 s2 交错 组成的。 两个字符串 s 和 t 交错 的定义与过程如下,其中每个字符串都会被分割成若干 非空 子字符串: s s1 s2 …...

LDAP服务器如何重启
1、find / -name ldap 该命令只会从根路径下查看ldap文件夹 find / -name ldap2、该命令会从根路径/查看所有包含ldap路径的文件夹,会查询出所有,相当于全局查询 find / -name *ldap*2、启动OpenLADP 找到LDAP安装目录后,执行以下命令 #直…...

AP51656 LED车灯电源驱动IC 兼容替代PT4115 PT4205 PWM和线性调光
产品描述 AP51656是一款连续电感电流导通模式的降压恒流源 用于驱动一颗或多颗串联LED 输入电压范围从 5V 到 60V,输出电流 可达 1.5A 。根据不同的输入电压和 外部器件, 可以驱动高达数十瓦的 LED。 内置功率开关,采用高端电流采样设置 …...

浅析安防视频监控平台EasyCVR视频融合平台接入大量设备后是如何维持负载均衡的
安防视频监控平台EasyCVR视频融合平台可拓展性强、视频能力灵活、部署轻快,可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等,以及支持厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。视频汇聚融合管理平台EasyCVR既具备…...

SIEM 中不同类型日志监控及分析
安全信息和事件管理(SIEM)解决方案通过监控来自网络的不同类型的数据来确保组织网络的健康安全状况,日志数据记录设备上发生的每个活动以及整个网络中的应用程序,若要评估网络的安全状况,SIEM 解决方案必须收集和分析不…...

【java基础复习】java中的数组在内存中是如何存储的?
基本数据类型与内存存储数组类型与内存存储为什么数组需要两块空间?感谢 💖 基本数据类型与内存存储 首先,让我们回顾一下基本数据类型的内存存储方式。对于一个基本类型变量,例如int类型的变量a,内存中只有一块内存空…...
MySQL数据库 MHA高可用
MySQL MHA 什么是 MHA MHA(MasterHigh Availability)是一套优秀的MySQL高可用环境下故障切换和主从复制的软件。 MHA 的出现就是解决MySQL 单点的问题。 MySQL故障切换过程中,MHA能做到0-30秒内自动完成故障切换操作。 MHA能在故障切换的…...

leetcode669. 修剪二叉搜索树(java)
修剪二叉搜索树 题目描述递归代码演示: 题目描述 难度 - 中等 LC - 669. 修剪二叉搜索树 给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树 不应该 改变保留…...

计算机网络的故事——确认访问用户身份的认证
确认访问用户身份的认证 HTTP使用的认证方式:BASIC认证(基本认证)、DIGEST(摘要认证)、SSL客户端认证、FormBase认证(基于表单认证)。 基于表单的认证:涉及到session管理以及cookie…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...

三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...

Linux中《基础IO》详细介绍
目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改,实现简单cat命令 输出信息到显示器,你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...
数据库——redis
一、Redis 介绍 1. 概述 Redis(Remote Dictionary Server)是一个开源的、高性能的内存键值数据库系统,具有以下核心特点: 内存存储架构:数据主要存储在内存中,提供微秒级的读写响应 多数据结构支持&…...
从实验室到产业:IndexTTS 在六大核心场景的落地实践
一、内容创作:重构数字内容生产范式 在短视频创作领域,IndexTTS 的语音克隆技术彻底改变了配音流程。B 站 UP 主通过 5 秒参考音频即可克隆出郭老师音色,生成的 “各位吴彦祖们大家好” 语音相似度达 97%,单条视频播放量突破百万…...
CppCon 2015 学习:REFLECTION TECHNIQUES IN C++
关于 Reflection(反射) 这个概念,总结一下: Reflection(反射)是什么? 反射是对类型的自我检查能力(Introspection) 可以查看类的成员变量、成员函数等信息。反射允许枚…...