android U广播详解(一)

news2024/11/22 21:00:26

概念介绍

进程队列

BroadcastQueueModernImpl 的设计围绕着为设备上的每个潜在进程维护一个单独的 BroadcastProcessQueue 实例。表明用于传送到特定进程的Pending {@link BroadcastRecord} 条目队列。整个类都标记为 {@code NotThreadSafe},因为调用者有责任始终与持有的相关锁进行交互。

结构

在内部,每个队列由一个等待调度的待处理广播和一个当前正在调度的活动广播组成。给等待调度的广播分别维护了紧急、普通、负载三个有序集合,优先级为紧急(3) > 普通(10) > 负载,但是当优先级高的集合被处理的广播数量超过一定限制时,优先级低的广播也有机会得到执行。

可运行

每个队列都有一个在未来特定时间“可运行”(runnable at 时间戳)的概念,它支持在每个进程的基础上任意暂停或延迟交付。当它下一次符合执行条件时,该值会受到各种策略的影响,例如:

  • 哪些广播正在等待发送给给定进程。 例如,“紧急”(前台、源于用户交互、闹钟)广播通常会导致较早的runnable at(-120s)时间,或者“延迟”广播通常会导致较晚的runnable at时间。
  • 进程或 UID 的当前状态。 例如,“cached”(procState > PROCESS_STATE_RECEIVER)进程通常会导致较晚的runnable at(+120s)时间,或者“instrumented”进程通常会导致较早的runnable at(-120s)时间。
  • 阻塞等待较早的接收器完成。 例如,“有序”或“优先”广播通常会导致not currently runnable值。

并行调度

调度

给定具有有效 runnable at 时间戳的每个进程队列的集合,BroadcastQueueModernImpl 然后愿意将这些 runnable队列提升为 running 状态。 我们根据 runnable at 时间戳的排序顺序选择下一个要提升的每个进程队列,首先选择等待时间最长的进程,旨在减少整体广播调度延迟。
限制

  • 为了保持系统健康(因为有序或静态注册的会在binder线程接着分发),在任何给定时间最多允许“BroadcastConstants.MAX_RUNNING_PROCESS_QUEUES”(4,低内存2个)进程处于running状态,额外会在此基础上再允许运行一个有紧急广播的队列。
  • 在任何给定时间最多允许一个进程被冷启动。 (对于后台,通过fork和专门化 zygote 的冷启动进程是一项相对繁重的操作,因此将我们自己限制在单个挂起的冷启动减少了系统范围的资源争用。)等前一个进程冷启结束后可立刻安排下一次调度。
分批派发

考虑将进程队列中任何其他Pending广播分派到该进程,旨在分批分派以更好地分摊 OOM 调整的成本。每个进程队列一次可最多分派MAX_RUNNING_ACTIVE_BROADCASTS(16,低内存8)个广播,以避免其他进程处于饥饿状态。

有序

收集一次可分发到的receivers时,如果遇到有序广播或静态注册的receiver,则直接中断收集 ,将已有receivers分发到app进程。等app进程完成分发后告知系统,仍然在binder线程继续完成当前进程队列的分发。

饥饿注意事项

仔细关注几种类型的潜在资源匮乏,以及缓解机制:

  • 进程队列的应用延迟runnable at 策略可能会导致Pending列表变得非常大。 当队列变得太大时,“BroadcastConstants.MAX_PENDING_BROADCASTS”会绕过任何延迟来缓解这种情况。
  • 具有大量Pending广播的进程队列可能会独占有限的 runnable 插槽之一。 这可以通过使用“BroadcastConstants.MAX_RUNNING_ACTIVE_BROADCASTS”来暂时“退出”正在运行的进程以让其他进程有机会运行来缓解。
  • 一个“紧急”广播被发送到一个有大量“非紧急”广播积压的进程可能会有很大的发送延迟。 这可以通过维护一个单独的紧急事件的 mPendingUrgent 队列来缓解,我们更愿意在正常的 mPending 队列之前调度它。
  • 具有有序广播的进程希望执行,但严重的 CPU 争用可能会导致进程在触发 ANR 超时之前无法接收到足够的资源。 这可以通过将“软”ANR 超时延长至原始超时长度的两倍来缓解。

BroadcastQueueModernImpl

替代 {@link BroadcastQueue} 实现,它以每个进程为基础调度广播。
每个进程现在都有自己的广播队列,由 {@link BroadcastProcessQueue} 实例表示。 每个队列都有一个在未来特定时间“可运行”的概念,它支持在每个进程的基础上任意暂停或延迟交付。
为了让事情更容易推理,有一种非常强烈的偏好,即让广播交互以这种特定顺序通过一组一致的方法流动:

  • {@link #updateRunnableList} 在每个进程队列有相关的未决广播时将其提升为可运行
  • {@link #updateRunningList} 促进可运行队列运行并安排第一次广播的传送
  • {@link #scheduleReceiverColdLocked} 请求任何需要的冷启动,结果{@link#onApplicationAttachedLocked} 报告
  • {@link #scheduleReceiverWarmLocked} 请求将当前活动的广播发送到正在运行的应用程序,并通过 {@link #finishReceiverLocked} 报告结果

主要特点

android U广播机制重构原因:

  • 遗留广播队列以串行方式一次一个地向接收方应用程序发送广播(有序)
  • 最初是一个合理的选择,因为这些是后台任务
  • 这些年来,病态的案例不断涌现,就像在许多接收器中翻滚一样。
  • 除了一些迭代改进外,整体架构自Android~1.0以来保持不变

高效的广播传输

允许多个进程并行处理广播,并通过一次将多个广播分派给一个进程来最小化 OOm 调整成本。

  • 引入每个进程队列来处理广播传输并在每个进程的基础上设置策略(暂停/延迟)
  • 允许多个进程(4+1)并行处理广播,同时保留来自每个应用程序 PoV 的广播传送顺序
  • 通过连续向一个进程传送多个广播(16)来最小化 OOM 调整成本

广播从入队到分发的大致流程,更新mRunning列表在ActivityManager线程。
在这里插入图片描述

允许多个进程(4+1)并行处理广播:

  • 有序广播分发后会等到receiver finish后重新在binder线程继续分发当前进程队列里等待分发的广播,直到没有广播分发或达到分发上限。
  • 同一时间内只会允许冷启动一个进程,进程启动成功后会在binder线程中分发当前进程队列里等待分发的广播。

所以最多会有5个线程可同时针对5个各自的进程队列进行广播反而分发,大大提高了广播的分发速度(尤其是有序广播或功能清单注册的接收器)。

在这里插入图片描述

消除冗余广播

丢弃和合并频繁发送的广播

允许系统丢弃和合并某些高频广播的新 API

广播发送者可以指定如何处理他们的广播

  • 使用 setDeliveryGroupMatching*() API 指示应如何对广播进行分组
  • 使用 setDeliveryGroupPolicy() API 来指示如何“合并”属于一个组的广播
    在这里插入图片描述
    下发组策略:
    // 下发组策略,表示下发组内的所有广播都需要按原样下发。
    @SystemApi
    public static final int DELIVERY_GROUP_POLICY_ALL = 0;
        // 下发组策略,表示只下发该下发组中最新的广播,其余的可以丢弃。
    @SystemApi
    public static final int DELIVERY_GROUP_POLICY_MOST_RECENT = 1;
        // 交付组策略,指示交付组中广播的额外数据需要合并到单个广播中,其余数据可以丢弃。
    public static final int DELIVERY_GROUP_POLICY_MERGED = 2;
例子

将 MOST_RECENT 策略应用于 CONNECTIVITY_ACTION

  • 将networkType设置为匹配键,表示特定网络类型对应的所有广播都属于同一个投递组
  • 设置MOST_RECENT为policy,表示只需要传递一个传递组中最近的广播,其余的可以丢弃
    在这里插入图片描述
应用策略的示例广播

在这里插入图片描述

减少后台工作(已被废弃)

阻止处于缓存状态的应用程序使用广播(已废弃,改为阻止冻结状态)
防止处于Cached状态的应用使用广播消耗系统资源

  • 添加了一个新的BroadcastOptions BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE以允许发送者指示他们对缓存应用程序的广播可以延迟到它们变为活跃状态

限制cache进程接收广播的更改历史:

  • 谷歌从开始的限制(procState > 11)的进程接收广播;
  • 到后面限制(procState > 15)的进程接收广播;
  • 再到现在只限制冻结的进程接收广播。
/** {@hide} */
@IntDef(prefix = { "DEFERRAL_POLICY_" }, value = {
        DEFERRAL_POLICY_DEFAULT,
        DEFERRAL_POLICY_NONE,
        DEFERRAL_POLICY_UNTIL_ACTIVE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface DeferralPolicy {}

public @NonNull BroadcastOptions setDeferralPolicy(@DeferralPolicy int deferralPolicy) {
    mDeferralPolicy = deferralPolicy;
    return this;
}

如何分析

通过执行如下命令可以查看历史的广播分发记录等信息。

adb shell dumpsys activity broadcasts

整体结构

  // 完整的广播队列列表,如果没有Active状态的广播则不打印当前的进程队列
  📋 Per-process queues:
  
  // mRunnableHead 中保存的进程队列(可运行的)
  🧍 Runnable:
    (none)
  
  // mRunning 中保存的进程队列(正在运行的)
  🏃 Running:
      (none)
      (none)
      (none)
      (none)
      (none)
      
  // 可暂不管,系统配置的是否忽略传递组策略
  Broadcasts with ignored delivery group policies:
    {}
  
  // mUidForeground ,保存当前处于前台(procstate为PROCESS_STATE_TOP)的uid
  Foreground UIDs:
    {}
  
  // 一些常量
  Broadcast parameters (key=bcast_fg_constants, observing=true):
    bcast_timeout=+10s0ms 
    bcast_slow_time=+5s0ms 
    bcast_deferral=+5s0ms 
    bcast_deferral_decay_factor=0.75 
    bcast_deferral_floor=0 
    bcast_allow_bg_activity_start_timeout=+10s0ms 
  
  Broadcast parameters (namespace=activity_manager_native_boot):
    modern_queue_enabled=true  // 使用现代广播队列逻辑处理广播
    bcast_max_running_process_queues=4 // 一次最多可同时进行分发的广播队列数量
    bcast_max_running_active_broadcasts=16 // 单个广播队列一次最多可分发的广播数量
    bcast_max_core_running_blocking_broadcasts=16 
    bcast_max_core_running_non_blocking_broadcasts=64 
    bcast_max_pending_broadcasts=256  
    bcast_delay_normal_millis=+500ms 
    bcast_delay_cached_millis=+2m0s0ms 
    bcast_delay_urgent_millis=-2m0s0ms 
    bcast_delay_foreground_proc_millis=-2m0s0ms 
    bcast_delay_persistent_proc_millis=-2m0s0ms 
    bcast_max_history_complete_size=256  // 最多可保存的完整的历史已分发完成的广播数量
    bcast_max_history_summary_size=1024 // 最多可保存的简要的历史已分发完成的广播数量
    bcast_max_consecutive_urgent_dispatches=3 
    bcast_max_consecutive_normal_dispatches=10 
    bcast_core_defer_until_active=true 
    pending_cold_start_check_interval_millis=30000 
  
    // 广播的历史记录
    // 正在被分发或等待被分发的广播
    Pending broadcasts:
      <empty>
  
    // 已经分发完毕的广播(完整的信息)
    Historical broadcasts [modern]:
    Historical Broadcast modern #0:
    
    // 已经分发完毕的广播(简要的信息)
    Historical broadcasts summary [modern]:
    #0: act=miui.intent.action.CYCLE_CHECK flg=0x40000010
      0 dispatch +5ms finish
      enq=2023-10-12 19:04:51.070 disp=2023-10-12 19:04:51.070 fin=2023-10-12 19:04:51.075

广播的分发状态

    @IntDef(flag = false, prefix = { "DELIVERY_" }, value = {
            DELIVERY_PENDING, // 初始状态:等待未来运行
            DELIVERY_DELIVERED, // 终端状态:成功完成
            DELIVERY_SKIPPED, // 终端状态:由于内部政策而跳过
            DELIVERY_TIMEOUT, // 终端状态:尝试投递时超时
            DELIVERY_SCHEDULED, // 中间状态:当前正在执行
            DELIVERY_FAILURE, // 终端状态:派送失败
    })
PENDING
    // 设置进程队列中的息屏广播正在等待被分发
    ba971da 5456:com.android.settings/1000 not runnable because BLOCKED
      runningOomAdjusted:true
      e:10 d:8 f:3 fd:1 o:0 a:0 p:8 pd:8 int:0 rt:8 ins:0 m:9 csi:0 adcsi:0 ccu:3 ccn:2
      -334ms 67ebdbd android.intent.action.SCREEN_OFF/u-1
          PENDING URGENT for registered 9f581c2 // 广播receiver状态为PENDING,广播为urgent(前台)
          blocked until 20, currently at 10 of 94 // 有序广播,需要等待前面的广播分发完成

App注意事项

如何让广播更快被分发

这里针对的是广播发送断

  1. 标记广播的intent为interactive
BroadcastOptions options = BroadcastOptions.makeBasic();
options.setInteractive(true);
pendingIntent.send(options.toBundle());

一般用于标记用户启动的 PendingIntent,需要声明如下权限:

<uses-permission android:name="android.permission.COMPONENT_OPTION_INTERACTIVE" />
  1. 给广播的intent添加前台标记
Intent intent = new Intent(XXX);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);

App须知

  1. 耗时时间长&接受者多的广播建议加上负载标志FLAG_RECEIVER_OFFLOAD,目前而言就开机广播;
  2. 短时间内发送频率高的广播,建议发送时按需指定DELIVERY_GROUP_POLICY_MOST_RECENT或DELIVERY_GROUP_POLICY_MERGED的policy。
final Bundle mostRecentDeliveryOptions = BroadcastOptions.makeBasic()
                        .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
                        .toBundle();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1085337.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux 用户层、内核层和MMU

一、Linux 用户层、内核层 在 Linux 中&#xff0c;所有设备都以文件的形式存放在/dev 目录下&#xff0c;都是通过文件的方式进行访问&#xff0c;设备节点是Linux 内核对设备的抽象&#xff0c;一个设备节点就是一个文件。应用程序通过一组标准化的调用执行访问设备&#xff…

SwiftUI 中如何实现文件导入和导出

文章目录 前言导入导出文件移动总结 前言 之前谈论了在 SwiftUI 中选择照片和视频的问题。今天我们将学习如何在 SwiftUI 视图中导入和导出文件。通过使用新的 fileImporter 和 fileExporter 视图修饰符&#xff0c;可以很方便实现这个功能。 导入 SwiftUI 框架提供了 fileI…

RAMday9

设置按键中断&#xff0c;按键1按下&#xff0c;LED亮&#xff0c;再按一次,灭&#xff1b;按键2按下&#xff0c;蜂鸣器响&#xff0c;再按一次&#xff0c;不响&#xff1b;按键3按下&#xff0c;风扇转,再按一次,风扇停 代码 do_irq.c #include "key.h" extern…

火狐浏览器页面翻译

打开扩展 扩展搜索tran 点击谷歌快译组件 设置翻译语言 安装完成后即可右键翻译页面

【数据库——MySQL(实战项目1)】(3)图书借阅系统——存储函数

目录 1. 简述2. 功能代码2.1 创建存储函数&#xff0c;根据图书编号查借阅人姓名&#xff0c;并调用该函数查询‘ **小邓在森林** ’已借未还的图书情况&#xff1b;2.2 创建存储函数&#xff0c;计算某借阅人还能借阅的图书数目&#xff0c;学生限额 5 本&#xff0c;教师限额…

两个连续变量乘积线性化——McCormick envelope近似

两个连续变量乘积的形式&#xff0c;可以进行近似线性化转化&#xff08;没有完全等价的线性化方法&#xff09;。要说明的是这是一种近似线性化&#xff0c;并不是完全等价&#xff0c;存在一定的误差。转化的方法是&#xff0c;McCormick envelope近似&#xff08;McCormick …

快速解决“找不到msvcr120.dll无法执行代码”问题,总结5解决方法

计算机已经成为我们生活和工作中不可或缺的一部分。然而&#xff0c;在使用计算机的过程中&#xff0c;我们常常会遇到各种问题&#xff0c;其中之一就是找不到msvcr120.dll文件。这个问题可能会可能导致计算机程序软件&#xff0c;游戏无法正常运行&#xff0c;影响到我们的工…

试图带你一文搞懂transformer注意力机制(Self-Attention)的本质

这篇文章主要想搞懂以下几个问题&#xff1a; 1、什么是自注意力&#xff08;Self-Attention&#xff09; 2、Q,K,V是什么 好了废话不多说&#xff0c;直接进入正题 Q,K,V分别代表query&#xff0c;key和value&#xff0c;这很容易让人联想到python的字典数据结构&#xff…

西安交大转子数据集故障诊断(Python代码,MSCNN结合LSTM结合注意力机制模型)

1.运行效果&#xff1a;西安交大转子数据集故障诊断&#xff08;Python代码&#xff0c;MSCNN结合LSTM结合注意力机制模型&#xff09;_哔哩哔哩_bilibili 2.环境库版本 如果库版本不一样&#xff0c; 一般也可以运行&#xff0c;这里展示我运行时候的库版本&#xff0c;是为了…

多线程 - 锁策略 CAS

常见的锁策略 此处谈到的锁策略,不局限于 Java,C,Python,数据库,操作系统……但凡是涉及到锁,都是可以应用到下列的锁策略的 乐观锁 vs 悲观锁 锁的实现者,预测接下来锁冲突(锁竞争,两个线程针对一个对象加锁,产生阻塞等待了)的概率是大,还是不大,根据这个冲突的概率,来接下…

30+程序员:如何成为工作领域专家丨IDCF

想要独立完成有一定复杂度的开发和维护工作&#xff0c;或者带领团队完成产品发布、项目交付&#xff1f;要成为研发经理、核心项目负责人&#xff1f; 但又苦于没有丰富的实战经验、或是有经验但是没有可以证明能力水平的认证。 在IT行业&#xff0c;年龄是一个敏感话题。特…

AI一体化运维监控方案助力医院监控体系信息化、智能化

智慧医院是现代医疗领域的新兴概念&#xff0c;是通过先进的物联网技术和新兴技术联合人工智能&#xff0c;实现一体化医院运维监控管理&#xff0c;旨在提升医院的安全性、运行效率和服务质量。 具体方案 1、 视频监控系统 医院可以安装视频监控摄像头&#xff0c;覆盖关键区…

vue-mixin

1.vue中&#xff0c;混入(mixin)是一种特殊的使用方式。一个混入对象可以包含任意的组件配置选项(data, props, components, watch,computed…)可以根据需求"封装"一些可复用的单元&#xff0c;并在使用时根据一定的策略合并到组件的选项中&#xff0c;使用时和组件自…

记录使用vant组件库的Popup的问题

使用过程中,需要实现点击遮罩层不关闭,智能点击关闭按钮或提交按钮才能关闭遮罩层,看了官网要使用close-on-click-overlay属性, 一开始的写法是错误的: close-on-click-overlayfalse 这个写法明显传递的是string, 而官网中明确要求要穿布尔值, 所以需要在前面加冒号 :clos…

【基础篇】三、Flink集群角色、系统架构以及作业提交流程

文章目录 1、集群角色2、部署模式3、Flink系统架构3.1 作业管理器&#xff08;JobManager&#xff09;3.2 任务管理器&#xff08;TaskManager&#xff09; 4、独立部署会话模式下的作业提交流程5、Yarn部署的应用模式下作业提交流程 1、集群角色 Flink提交作业和执行任务&…

04在命令行中使用Maven命令创建Maven版的Web工程,并将工程部署到服务器的步骤

创建Maven版的Web工程 使用命令生成Web工程 使用mvn archetype:generate命令生成Web工程时&#xff0c;需要使用一个专门生成Web工程骨架的archetype(参照官网看到它的用法) -D表示后面要附加命令的参数&#xff0c;字母D和后面的参数是紧挨着的&#xff0c;中间没有任何其它…

Mac下通过nvm管理node

背景 本地有两个项目&#xff0c;老项目需要用到node 14&#xff0c;新项目需要用node 16&#xff0c;所以只能通过nvm来管理node了 卸载原始的node 我的node是通过官网的.pkg文件安装的&#xff0c;可以通过以下命令进行删除 sudo rm -rf /usr/local/{bin/{node,npm},lib/…

第四篇Android--TextView使用详解

TextView是View体系中的一员&#xff0c;继承自View&#xff0c;用于在界面中展示文字。 基本用法&#xff1a; <TextViewandroid:id"id/textview"android:layout_width"wrap_content"android:layout_height"wrap_content"android:padding&q…

一文带你上手自动化测试中的PO模式!

在UI的自动化测试中&#xff0c;我们需要把测试使用到的数据分离到文件中&#xff0c;如果单纯的写在我们的测试模块里面&#xff0c;不是一个好的设计&#xff0c;所以不管是什么类型的自动化测试&#xff0c;都是需要把数据分离出来的。当然分离到具体的文件里面&#xff0c;…

Arcgis实现Tiff合并

Arcgis实现Tiff合并 现有四幅Tiff影像 打开数据管理工具 输入使用这四幅影像 下面这个就是建立数据库&#xff0c;这个不对 点击确定 合成完毕