当前位置: 首页 > article >正文

【阅读笔记】Android广播的处理流程

关于Android的解析,有很多优质内容,看了后记录一下阅读笔记,也是一种有意义的事情,

今天就看看“那个写代码的”这位大佬关于广播的梳理,

https://blog.csdn.net/a572423926/category_11509429.html

https://blog.csdn.net/a572423926/article/details/121760258

广播的原理很清晰,类似一种观察者模式,控制中心把广播发送给注册者(观察者),但是android中的实现细节较多。

这里通过一个am命令发送受保护的广播,查看抛出的异常,看相关堆栈。

am broadcast -n com.example.test/.AppInstallReceiver -a android.intent.action.SCREEN_ON

am broadcast -n com.example.test/.AppInstallReceiver -a android.intent.action.SCREEN_ON
Broadcasting: Intent { act=android.intent.action.SCREEN_ON flg=0x400000 cmp=com.example.test/.AppInstallReceiver }

Exception occurred while executing 'broadcast':
java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.intent.action.SCREEN_ON from pid=18179, uid=2000
        at com.android.server.am.ActivityManagerService.broadcastIntentLocked(ActivityManagerService.java:14369)
        at com.android.server.am.ActivityManagerService.broadcastIntentLocked(ActivityManagerService.java:14206)
        at com.android.server.am.ActivityManagerService.broadcastIntentWithFeature(ActivityManagerService.java:15092)
        at com.android.server.am.ActivityManagerShellCommand.runSendBroadcast(ActivityManagerShellCommand.java:806)
        at com.android.server.am.ActivityManagerShellCommand.onCommand(ActivityManagerShellCommand.java:221)
        at com.android.modules.utils.BasicShellCommandHandler.exec(BasicShellCommandHandler.java:97)
        at android.os.ShellCommand.exec(ShellCommand.java:38)
        at com.android.server.am.ActivityManagerService.onShellCommand(ActivityManagerService.java:9776)
        at android.os.Binder.shellCommand(Binder.java:1055)
        at android.os.Binder.onTransact(Binder.java:883)
        at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:4845)
        at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2833)
        at android.os.Binder.execTransactInternal(Binder.java:1291)
        at android.os.Binder.execTransact(Binder.java:1250)

调用到ActivityManagerService.broadcastIntentLocked

app中发送广播后,会调用到ActivityManagerService.broadcastIntentLocked

在PMS中查询,有哪些接收者

这里可以把ResolveInfo打印出来进行查看接收者是否被筛选出来了,具体分析可查看原文。

然后,进行分发,

会调用到BroadcastQueue.java 的processNextBroadcast,有些广播接收不到,就是在这里面进行了屏蔽。

processNextBroadcast的代码设计不好,

对于并行广播,搞了个循环来一下子处理完成。

“processNextBroadcast 每次被调用,只能分发给 mOrderedBroadcasts 中一个广播的其中一个 Receiver,如果静态注册的应用未启动,还需要等待应用启动后再进行处理。”

/frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

final void processNextBroadcast(boolean fromMsg) {
	synchronized(mService) {
		BroadcastRecord r;
		......
		
		// 这里的do-while只会从mOrderedBroadcasts中取出第一个BroadcastRecord进行后续的处理!
		do {
			......
			r = mOrderedBroadcasts.get(0);
			......
		} while (r == null);

		......
		
		final Object nextReceiver = r.receivers.get(recIdx); // 取出一个Receiver

		// 对动态注册receiver的处理,这里是分发有序广播
		if (nextReceiver instanceof BroadcastFilter) {
			......
			// 通过deliverToRegisteredReceiverLocked调用ActivityThread.scheduleRegisteredReceiver处理广播
			deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
			......
		}

		// 开始对静态注册receiver的处理!!!
		ResolveInfo info = (ResolveInfo)nextReceiver;
		......

		// 如果应用已经启动,则直接分发广播给该应用,并返回
		if (app != null && app.thread != null && !app.killed) {
			try {
				app.addPackage(info.activityInfo.packageName,
						info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
				// 通过processCurBroadcastLocked -> ActivityThread.scheduleReceiver -> receiver.onReceive处理当前广播
				processCurBroadcastLocked(r, app);
				return;
			} catch (RemoteException e) {
				Slog.w(TAG, "Exception when sending broadcast to "
					  + r.curComponent, e);
			} catch (RuntimeException e) {
				Slog.wtf(TAG, "Failed sending broadcast to "
						+ r.curComponent + " with " + r.intent, e);
				......
				return;
			}
		}

		......

		// 如果应用未启动,则在这里启动应用进程,广播将在AMS启动完成后被调用处理
		if ((r.curApp=mService.startProcessLocked(targetProcess,
				info.activityInfo.applicationInfo, true,
				r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
				"broadcast", r.curComponent,
				(r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
						== null) {
			Slog.w(TAG, "Unable to launch app "
                    + info.activityInfo.applicationInfo.packageName + "/"
                    + info.activityInfo.applicationInfo.uid + " for broadcast "
                    + r.intent + ": process is bad");
			......
			return;
		}

		// 将BroadcastRecord赋值为mPendingBroadcast,等待应用启动完成后处理
		mPendingBroadcast = r;
		mPendingBroadcastRecvIndex = recIdx;
	}
}

这里面

通过 deliverToRegisteredReceiverLocked 调用到 ActivityThread.scheduleRegisteredReceiver,处理动态注册的 receiver;通过 processCurBroadcastLocked,调用到 ActivityThread.scheduleReceiver,处理静态注册的 receiver

这样,就传送给app端了。

参考资料:https://blog.csdn.net/a572423926/article/details/121760258
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/a572423926/article/details/121760258


http://www.kler.cn/a/418805.html

相关文章:

  • 【Oracle11g SQL详解】GROUP BY 和 HAVING 子句:分组与过滤
  • leetcode 之二分查找(Java实现)(1)
  • Maya 中创建游戏角色的头发,并将其导出到 Unreal Engine 5
  • 探索温度计的数字化设计:一个可视化温度数据的Web图表案例
  • pycharm链接neo4j(导入文件)
  • React进阶面试题(四)
  • Vue 2.0->3.0学习笔记(Vue 3 (四)- Composition API 的优势)
  • 第29天 MCU入门
  • C#基础教程
  • OD E卷 - 实现 【虚拟理财游戏】
  • 【青牛科技】电动工具调速控制电路芯片GS069介绍
  • 安装Fcitx5输入框架和输入法自动部署脚本(来自Mark24)-Ubuntu通用
  • D82【python 接口自动化学习】- pytest基础用法
  • 多线程篇-8--线程安全(死锁,常用保障安全的方法,安全容器,原子类,Fork/Join框架等)
  • windows下安装node.js和pnpm
  • YOLO 标注工具 AutoLabel 支持 win mac linux
  • 【Electron学习笔记(三)】Electron的主进程和渲染进程
  • 【论文复现】从零开始搭建图像去雾神经网络
  • 【软考速通笔记】系统架构设计师⑧——系统质量属性与架构评估
  • 14 - Java 面向对象(中级)
  • SqlServer REVERSE字符串值的逆序排序函数
  • 框架学习07 - SpringMVC 其他功能实现
  • Cisco WebEx 数据平台:统一 Trino、Pinot、Iceberg 及 Kyuubi,探索 Apache Doris 在 Cisco 的改造实践
  • 线性表-链式描述(C++)
  • 【联表查询】.NET开源 ORM 框架 SqlSugar 系列
  • 【C语言】扫雷游戏(一)