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…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...

Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...

以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...

招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...