IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android 9.0系统源码_广播(四)优化系统开机广播耗时时间 -> 正文阅读

[移动开发]Android 9.0系统源码_广播(四)优化系统开机广播耗时时间

前言

近来在对车机智能仓Android系统进行版本性能优化,在优化的过程中发现一个问题:
在系统刚刚启动的时候,很多通过监听系统开机广播【ACTION_BOOT_COMPLETED】进行自启动的应用,需要40秒左右才能收到开机广播并成功进行启动。

一、测试

1.1 开机立即发送测试广播

为了排查这个问题的原因,本人使用adb命令来模拟发送广播,当系统刚刚开机的时候,执行如下adb命令

adb shell am broadcast -a act=android.intent.action.Test
Broadcast completed: result=0

最终发现需要等待40秒左右,才能收到回调。

1.2 开机等待一段时候以后再发送测试广播

进一步测试发现,当系统开机时间达到55秒以后,再次执行同样的adb命令:

发现只需要等待1秒即可收到回调。

1.3 分析测试结果

通过上面的两种测试结果可以很明显看出,这个问题是由于系统启动阶段做了什么事情,才导致广播接收慢的。
要处理这个问题,必须研究下 Android 发送广播的流程,看看到底哪个环节出了问题。另外想要实现软件开机自启动,基本都是通过静态注册的方式来配置广播接收者的。

本系列前篇几篇文章已经很详细的分析过了广播的注册、注销、发送流程;本篇文章我们忽略无序动态广播,简单来梳理一下静态注册的广播接收者广播发送接受的流程。

二、广播的发送流程

/frameworks/base/core/java/android/app/ContextImpl.java

@Override
public void sendBroadcast(Intent intent) {
    warnIfCallingFromSystemProcess();
    String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
    try {
        intent.prepareToLeaveProcess(this);
        ActivityManager.getService().broadcastIntent(
                mMainThread.getApplicationThread(), intent, resolvedType, null,
                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                getUserId());
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

发送广播一般都是通过调用sendBroadcast 方法,该方法最终调用到 AMS 里面的 broadcastIntent 方法。

    // 发送广播
    public final int broadcastIntent(IApplicationThread caller,
                                     Intent intent, String resolvedType, IIntentReceiver resultTo,
                                     int resultCode, String resultData, Bundle resultExtras,
                                     String[] requiredPermissions, int appOp, Bundle bOptions,
                                     boolean serialized, boolean sticky, int userId) {
        enforceNotIsolatedCaller("broadcastIntent");
        synchronized (this) {
            // 验证广播的Intent是否合法
            // 如果这个时候系统正在启动,还会验证intent所描述的广播是否只发送给动态注册的广播接收者。
            // 在系统的启动过程中,PMS可能还未启动,这种情况下,AMS是无法获取静态注册广播接收者的,因此,就禁止发送广播给静态注册的广播接收者
            intent = verifyBroadcastLocked(intent);
            // 根据caller从缓存mLruProcesses中获取进程对象ProcessRecord
            final ProcessRecord callerApp = getRecordForAppLocked(caller);
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
			
            int res = broadcastIntentLocked(callerApp,
                    callerApp != null ? callerApp.info.packageName : null,
                    intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                    requiredPermissions, appOp, bOptions, serialized, sticky,
                    callingPid, callingUid, userId);
            Binder.restoreCallingIdentity(origId);
            return res;
        }
    }

broadcastIntent又会调用broadcastIntentLocked方法,所以我们直接对broadcastIntentLocked方法的流程进行分析。

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

//发送广播的核心代码
	final int broadcastIntentLocked(ProcessRecord callerApp,
			String callerPackage, Intent intent, String resolvedType,
			IIntentReceiver resultTo, int resultCode, String resultData,
			Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
			boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
			......
			// Figure out who all will receive this broadcast.
			List receivers = null;
			List<BroadcastFilter> registeredReceivers = null;
			// Need to resolve the intent to interested receivers...
			if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)== 0) {
				// 这里先获取静态注册了该intent的receiver,放到receivers队列中
				//collectReceiverComponents的实现其实就是调用了PMS的queryIntentReceivers方法查询静态注册了该intent的
				//receiver,而PMS是在开机启动时扫描package的时候,从manifest中解析出Receiver,保存到mReceivers这个全局变量中。
				receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
			}
			if (intent.getComponent() == null) {
		          // 这里调用 mReceiverResolver.queryIntent 获取动态注册了该intent的receiver,放到registeredReceivers队列中
		          ......
			}
			......
			int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
			// 对无序且动态注册的广播加入到广播队列等待分发,这部分广播是可以并行处理的
			if (!ordered && NR > 0) {
		        // 将BroadcastRecord加入到mParallelBroadcasts,调用BroadcastQueue.scheduleBroadcastsLocked进行处理
		        ......
			}
			......
			// 对有序且动态注册的广播以及静态注册的广播进行处理,这部分广播是串行处理的
			if ((receivers != null && receivers.size() > 0)
					|| resultTo != null) {
				BroadcastQueue queue = broadcastQueueForIntent(callerPackage, intent);
				BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
						callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
						requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
						resultData, resultExtras, ordered, sticky, false, userId);
		
				final BroadcastRecord oldRecord =
						replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;
				if (oldRecord != null) {
					......
				} else {
					// 这里是核心代码,将构造的BroadcastRecord加入到mOrderedBroadcasts
					queue.enqueueOrderedBroadcastLocked(r);
					// 调用BroadcastQueue.scheduleBroadcastsLocked,从广播队列中取出广播进行处理
					queue.scheduleBroadcastsLocked();
				}
			} else {
				// 没有receivers接收该广播,只作记录,不做其他处理
				......
			}
			return ActivityManager.BROADCAST_SUCCESS;
	}

由于这个函数代码非常多,这里我们忽略掉了动态注册的处理以及其他的一些流程,只针对静态注册的 receiver 进行分析;
可以看到 broadcastIntentLocked 中,针对静态注册的 receiver 主要做了 2 件事情:

  • collectReceiverComponents 获取到静态注册的 receiver,放到 receivers 队列中;
  • 构造 BroadcastRecord,调用 BroadcastQueue.enqueueOrderedBroadcastLocked加入到广播队列中,再调用BroadcastQueue 等待分发。

三、广播的分发流程

广播分发的主要代码在 BroadcastQueue中实现。

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

public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
	mOrderedBroadcasts.add(r);
	enqueueBroadcastHelper(r);
}

可以看到 enqueueOrderedBroadcastLocked 只是简单地把 BroadcastRecord 加入到 mOrderedBroadcasts 队列中。

public void scheduleBroadcastsLocked() {
    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
            + mQueueName + "]: current="
            + mBroadcastsScheduled);

    if (mBroadcastsScheduled) {
        return;
    }
    mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
    mBroadcastsScheduled = true;
}

private final class BroadcastHandler extends Handler {

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case BROADCAST_INTENT_MSG: {
                if (DEBUG_BROADCAST) Slog.v(
                        TAG_BROADCAST, "Received BROADCAST_INTENT_MSG");
                processNextBroadcast(true);
            } break;
        }
    }
}

scheduleBroadcastsLocked 也只是发送了一个 message 给 Handler,Handler最终调用 processNextBroadcast 进行真正的分发流程。

final void processNextBroadcast(boolean fromMsg) {
	synchronized(mService) {
		BroadcastRecord r;
		......

		// 首先,直接遍历mParallelBroadcasts,对并行广播(无序广播+动态注册的receiver)进行处理
		while (mParallelBroadcasts.size() > 0) {
			r = mParallelBroadcasts.remove(0);
			......
			final int N = r.receivers.size();
			// 遍历广播的所有receivers,进行分发
			for (int i=0; i<N; i++) {
				Object target = r.receivers.get(i);
				// deliverToRegisteredReceiverLocked -> performReceiveLocked -> ActivityThread.scheduleRegisteredReceiver异步处理广播
				deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
			}
			......
		}

		......
		
		// mPendingBroadcast需要在应用启动完成后进行处理。在AMS.attachApplicationLocked中会调用sendPendingBroadcastsLocked进行分发
		if (mPendingBroadcast != null) {
			boolean isDead;
			synchronized (mService.mPidsSelfLocked) {
				ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);
				isDead = proc == null || proc.crashing;
			}
			// 如果应用启动,会在AMS调用函数处理静态广播,所以这里直接return
			if (!isDead) {
				// It's still alive, so keep waiting
				return;
			} else {
				Slog.w(TAG, "pending app  ["
						+ mQueueName + "]" + mPendingBroadcast.curApp
						+ " died before responding to broadcast");
				mPendingBroadcast.state = BroadcastRecord.IDLE;
				mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
				mPendingBroadcast = null;
			}
		}

		boolean looped = false;
		
		// 这里的do-while只会从mOrderedBroadcasts中取出第一个BroadcastRecord进行后续的处理!
		do {
			......
			r = mOrderedBroadcasts.get(0);
			boolean forceReceive = false;

			// 后面主要是广播分发超时后的处理,以及广播分发给所有receiver后的处理,这里暂时跳过
			......
		} while (r == null);

		// Get the next receiver...
		int recIdx = r.nextReceiver++; // 取出一个Receiver

		......
		
		final Object nextReceiver = r.receivers.get(recIdx);

		// 对动态注册receiver的处理,这里应该是分发有序广播
		if (nextReceiver instanceof BroadcastFilter) {
			// 通过deliverToRegisteredReceiverLocked调用ActivityThread.scheduleRegisteredReceiver处理广播,暂时跳过
			......
		}

		// 开始对静态注册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;
	}
}

processNextBroadcast 是 BroadcastQueue 中分发广播的核心方法,对并行广播和串行广播进行处理。
由于代码太多,本篇博客主要关注静态注册广播的发送流程,所以删掉了大量的代码,只留下了和静态注册广播比较重要的部分。

概括起来,processNextBroadcast 在分发广播中主要做了这几件事情:

1、先处理并行广播队列(无序广播 + 动态注册的 receiver),遍历 mParallelBroadcasts 所有的 BroadcastRecord 以及 Receiver,异步发送广播。

2、再处理串行广播,从 mOrderedBroadcasts 中取出一个 BroadcastRecord,一次只处理其中的一个 Receiver

3、如果当前 receiver 为动态注册,调用 deliverToRegisteredReceiverLocked 进行处理

4、如果当前 receiver 为静态注册,分为两种情况:

4.1、应用已启动:调用 processCurBroadcastLocked 进行处理

4.2、应用未启动:调用 AMS.startProcessLocked 启动应用,将要处理的 BroadcastRecord 保存到 mPendingBroadcast,等待应用启动完成后在 AMS.attachApplicationLocked 中调用 BroadcastQueue.sendPendingBroadcastsLocked,sendPendingBroadcastsLocked方法最终又会调用processCurBroadcastLocked进行处理

四、开机广播接受速度缓慢的原因

其实分析到这里大概就能知道 应用在开机后启动慢的问题所在了。

  • 静态注册的广播需要串行处理,如果应用未启动,需要等待应用启动完成后才能处理广播,然后才能继续分发给下一个接收器。
  • 系统刚启动时,大量的应用通过静态注册被开机广播拉起来,再加上一些系统状态信息的广播,必然导致客户应用发出的广播需要在队列中等待很长的时间,才能分发并处理。

五、解决方案

1、尽量减少系统应用接收开机广播,尽量少地通过开机广播拉起应用。每启动一个应用都需要至少几百毫秒的耗时,加上系统的一些其他广播,使得第三方应用发出的广播都需要等很久。
2、接收开机广播的 Receiver,在 onReceive 中一定不要做耗时操作。由于静态注册是串行处理,如果在 onReceive 中做了耗时操作,会影响其他广播的处理,这块一定要注意!
3、应用内减少广播的使用,如果一个应用在系统启动的时候刚被唤醒,又发送了其他广播,这些广播也会按照优先级被插在广播队列中,这样同样会加剧开机广播耗时时间。
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-07-17 16:34:46  更:2022-07-17 16:36:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 3:34:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码