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…...
告别混乱信号!用CANdb++ Editor从零搭建汽车CAN网络DBC文件(保姆级图文教程)
告别混乱信号!用CANdb Editor从零搭建汽车CAN网络DBC文件(保姆级图文教程) 在汽车电子开发领域,CAN总线如同神经脉络般贯穿整车系统。我曾参与过一个新能源整车项目,由于早期缺乏规范的DBC文件,不同ECU厂商…...
从零构建可定制对话系统:模块化架构与RAG实战指南
1. 项目概述:从零构建一个可定制的对话系统最近在折腾一个挺有意思的东西,我把它叫做“定制化聊天系统”。起因很简单,市面上现成的聊天机器人,无论是开源的还是商业的,总感觉差了那么点意思。要么是功能太臃肿&#x…...
时空镜像立体成像楼宇全态透明智慧管控技术解析方案
时空镜像立体成像楼宇全态透明智慧管控技术解析方案一、方案概述当前传统楼宇管控普遍存在二维监控信息碎片化、空间感知能力薄弱、人员定位依赖外设、跨镜头轨迹断裂、身份核验存在漏洞、设备运维滞后、区域管控存在盲区等行业共性痛点,多数系统仅实现视频录像与基…...
CircuitPython嵌入式游戏开发:基于TileGrid的迷宫寻蛋与JSON数据持久化实践
1. 项目概述与核心价值如果你和我一样,对嵌入式开发充满热情,同时又对游戏开发抱有好奇心,那么将两者结合——在微控制器上编写一个完整的2D游戏——绝对是一次令人兴奋的挑战。这不仅仅是让LED闪烁或读取传感器数据,而是要在资源…...
终极指南:如何使用League-Toolkit英雄联盟工具箱快速提升游戏效率
终极指南:如何使用League-Toolkit英雄联盟工具箱快速提升游戏效率 【免费下载链接】League-Toolkit An all-in-one toolkit for LeagueClient. Gathering power 🚀. 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit 还在为英雄联盟中…...
基于Vanilla JS与IndexedDB构建本地化Markdown笔记工具
1. 项目概述:从零开始构建一个轻量级笔记工具最近在整理个人知识库时,发现市面上的笔记软件要么功能过于臃肿,要么云端同步存在隐私顾虑,要么就是定制化程度不够。作为一个有十多年开发经验的从业者,我决定自己动手&am…...
Linux磁盘挂载与开机自启配置
Linux磁盘挂载与开机自启配置磁盘挂载是 Linux 存储管理中的基础操作。很多线上问题都与挂载配置有关,例如重启后数据盘没挂上、路径指向错误分区、应用因挂载点缺失而启动失败。中级阶段不仅要会临时挂载,更要理解永久挂载的配置方式和风险控制。一、先…...
【限时解密】Midjourney未公开的Tea印相冷启动协议:如何绕过默认sampler干扰,直触胶片模拟内核(仅剩37位开发者掌握)
更多请点击: https://intelliparadigm.com 第一章:Midjourney Tea印相冷启动协议的起源与本质 Midjourney Tea印相冷启动协议(Tea-Init Protocol)并非官方标准,而是由东亚AI艺术协作社区在2023年自发演化出的一套轻量…...
3分钟快速上手:m4s-converter让B站缓存视频秒变MP4格式
3分钟快速上手:m4s-converter让B站缓存视频秒变MP4格式 【免费下载链接】m4s-converter 一个跨平台小工具,将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 在当今数字内容时代ÿ…...
ESP-SR深度解析:嵌入式语音识别系统的架构设计与性能优化实战指南
ESP-SR深度解析:嵌入式语音识别系统的架构设计与性能优化实战指南 【免费下载链接】esp-sr Speech recognition 项目地址: https://gitcode.com/gh_mirrors/es/esp-sr 在物联网设备智能化浪潮中,语音交互已成为人机交互的重要入口。ESP-SR作为乐鑫…...
