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…...

C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
【Go语言基础【12】】指针:声明、取地址、解引用
文章目录 零、概述:指针 vs. 引用(类比其他语言)一、指针基础概念二、指针声明与初始化三、指针操作符1. &:取地址(拿到内存地址)2. *:解引用(拿到值) 四、空指针&am…...

人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...