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…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
如何配置一个sql server使得其它用户可以通过excel odbc获取数据
要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据,你需要完成以下配置步骤: ✅ 一、在 SQL Server 端配置(服务器设置) 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到:SQL Server 网络配…...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
