[ Android源码分析 ] 静态注册在源码中是如何实现的

[ Android源码分析 ] 静态注册在源码中是如何实现的

尊重原创,转载请注明出处!

前言

前段时间研究了广播的发送流程,发送广播时需要先获取接收该广播的静态注册和动态注册的 Receiver,再将广播加入到 BroadcastQueue 中进行处理。
当时主要是为了解决客户的一个问题,比较紧急,所以看源码时也是为了理清广播发送的主要流程,对很多细节实现都没有太过纠结。
现在空闲下来了,还是得抽空研究下静态注册的实现流程。

应用实现方式

对于静态注册的实现方式,相信所有的 Android 开发者都非常清楚了,流程上应付一下,放一个最简单的接收开机广播的代码。当然,权限啥的就不贴了,简单看看就好。

<?xml version="1.0" encoding="utf-8"?>
<manifest>
    ......
    <application>
        ......
        <receiver android:name=".BootReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
        </receiver>
    </application>
 
</manifest>

发送广播给静态注册接收器

很容易想到,在发送广播之前、或者在发送广播的时候,必须要获取到静态注册了该广播的 Receiver,这样才能确保广播能正确地发送给对应的 Receiver。

在之前写的博客 [ Android实战 ] 开机时通过广播启动应用,但是很长时间才能接收到,如何解决?中,分析过 Android 发送广播的流程。应用调用 sendBroadcast 后,经过调用流程,走到 AMS 里面的 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;
	// Need to resolve the intent to interested receivers...
	if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
			 == 0) {
		// 这里获取静态注册了该intent的receiver,放到receivers队列中
		receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
	}
	......
	return ActivityManager.BROADCAST_SUCCESS;
}

collectReceiverComponents,顾名思义,就是用来收集静态注册的 Receiver 的方法。它的实现中,最关键的代码就是调用了 PackageManagerService 中的 queryIntentReceivers 方法,查询对应的 Receiver。

private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
		int callingUid, int[] users) {
	// TODO: come back and remove this assumption to triage all broadcasts
	int pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING;

	List<ResolveInfo> receivers = null;
	try {
		......
		for (int user : users) {
			......
			List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
					.queryIntentReceivers(intent, resolvedType, pmFlags, user).getList();
			......
		}
	} catch (RemoteException ex) {
		// pm is in same process, this will never happen.
	}
	return receivers;
}

queryIntentReceivers 的实现其实就是调用了 queryIntentReceiversInternal,并对结果进行封装。

而 queryIntentReceiversInternal 中的核心代码则是调用了 mReceivers.queryIntent,查询 intent 对应的 receiver 列表。

/frameworks/base/service/core/java/com/android/server/pm/PackageManagerService.java

@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent,
		String resolvedType, int flags, int userId) {
	return new ParceledListSlice<>(
			queryIntentReceiversInternal(intent, resolvedType, flags, userId,
					false /*allowDynamicSplits*/));
}

private @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent,
		String resolvedType, int flags, int userId, boolean allowDynamicSplits) {
	......

	// reader
	synchronized (mPackages) {
		String pkgName = intent.getPackage();
		if (pkgName == null) {
			// 重点是这行代码,从mReceivers中查询对应的Receiver列表
			final List<ResolveInfo> result =
					mReceivers.queryIntent(intent, resolvedType, flags, userId);
			// 过滤结果,移除一些instant app以及安装失败的应用,暂时不管
			return applyPostResolutionFilter(
					result, instantAppPkgName, allowDynamicSplits, callingUid, userId);
		}
		final PackageParser.Package pkg = mPackages.get(pkgName);
		if (pkg != null) {
			// 重点是这行代码,从mReceivers中查询对应的Receiver列表
			final List<ResolveInfo> result = mReceivers.queryIntentForPackage(
					intent, resolvedType, flags, pkg.receivers, userId);
			return applyPostResolutionFilter(
					result, instantAppPkgName, allowDynamicSplits, callingUid, userId);
		}
		return Collections.emptyList();
	}
}

mReceiver 的定义,很清楚地说明了它就是 PKMS 中用来存放所有解析出来的静态注册的 Receiver 的一个容器。

// All available receivers, for your resolving pleasure.
final ActivityIntentResolver mReceivers = new ActivityIntentResolver();

看到这里,第一阶段的流程已经很清楚了:发送广播的时候,会从 PKMS 的 mReceivers 中查询得到 intent 对应静态注册的 receiver 列表,再逐一进行发送。

那么,我们接下来的问题就变成了,mReceivers 这个对象是如何维护的,静态注册的 Receiver 是在什么时候解析并插入到 mReceivers 中的?

解析静态注册接收器

众所周知,Android 启动后创建了第一个进程 init,而 init 解析 init.rc 脚本启动了 zygote,其他所有应用进程都是由它 fork 出来的,其中就包括 system_server。

在 SystemServer 中,启动了大量 Android 运行过程中所需的服务,其中就包括我们很熟悉的 PackageManagerService。

/frameworks/base/service/java/com/android/server/SystemServer.java

private void startBootstrapServices() {
	......
	mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
			mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
	......
}

PackageManagerService.main 就是通过构造函数 new 出了一个 PackageManagerService 的实例,并调用 ServiceManager.addService 方法,注册了 package 的系统服务。

/frameworks/base/service/core/java/com/android/server/pm/PackageManagerService.java

public static PackageManagerService main(Context context, Installer installer,
        boolean factoryTest, boolean onlyCore) {
    // Self-check for initial settings.
    PackageManagerServiceCompilerMapping.checkProperties();

    PackageManagerService m = new PackageManagerService(context, installer,
            factoryTest, onlyCore);
    m.enableSystemUserPackages();
    ServiceManager.addService("package", m);
    final PackageManagerNative pmn = m.new PackageManagerNative();
    ServiceManager.addService("package_native", pmn);
    return m;
}

在 PKMS 的构造函数中,忽略其他的一些流程,主要调用了 scanDirTracedLI,对我们熟知的 /system/app、/vendor/app、/data/app 等路径下的应用进行扫描。截取部分代码如下:

public PackageManagerService(Context context, Installer installer,
    boolean factoryTest, boolean onlyCore) {
    ......            
    // Collect vendor overlay packages. (Do this before scanning any apps.)
    // For security and version matching reason, only consider
    // overlay packages if they reside in the right directory.
    scanDirTracedLI(new File(VENDOR_OVERLAY_DIR), mDefParseFlags
            | PackageParser.PARSE_IS_SYSTEM
            | PackageParser.PARSE_IS_SYSTEM_DIR
            | PackageParser.PARSE_TRUSTED_OVERLAY, scanFlags | SCAN_TRUSTED_OVERLAY, 0);

    mParallelPackageParserCallback.findStaticOverlayPackages();

    // Find base frameworks (resource packages without code).
    scanDirTracedLI(frameworkDir, mDefParseFlags
            | PackageParser.PARSE_IS_SYSTEM
            | PackageParser.PARSE_IS_SYSTEM_DIR
            | PackageParser.PARSE_IS_PRIVILEGED,
            scanFlags | SCAN_NO_DEX, 0);

    // Collected privileged system packages.
    final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
    scanDirTracedLI(privilegedAppDir, mDefParseFlags
            | PackageParser.PARSE_IS_SYSTEM
            | PackageParser.PARSE_IS_SYSTEM_DIR
            | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);

    // Collect ordinary system packages.
    final File systemAppDir = new File(Environment.getRootDirectory(), "app");
    scanDirTracedLI(systemAppDir, mDefParseFlags
            | PackageParser.PARSE_IS_SYSTEM
            | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

    // Collect all vendor packages.
    File vendorAppDir = new File("/vendor/app");
    try {
        vendorAppDir = vendorAppDir.getCanonicalFile();
    } catch (IOException e) {
        // failed to look up canonical path, continue with original one
    }
    scanDirTracedLI(vendorAppDir, mDefParseFlags
            | PackageParser.PARSE_IS_SYSTEM
            | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

    // Collect all OEM packages.
    final File oemAppDir = new File(Environment.getOemDirectory(), "app");
    scanDirTracedLI(oemAppDir, mDefParseFlags
            | PackageParser.PARSE_IS_SYSTEM
            | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
    ......
    if (!mOnlyCore) {
        scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);

        scanDirTracedLI(mDrmAppPrivateInstallDir, mDefParseFlags
                | PackageParser.PARSE_FORWARD_LOCK,
                scanFlags | SCAN_REQUIRE_KNOWN, 0);
        ......
    }
    ......
}

scanDirTracedLI 也只是对 scanDirLI 的一个简单封装调用,并调用 trace 记录相关信息。

private void scanDirTracedLI(File dir, final int parseFlags, int scanFlags, long currentTime) {
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + dir.getAbsolutePath() + "]");
    try {
        scanDirLI(dir, parseFlags, scanFlags, currentTime);
    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
}

scanDirLI 解析指定目录中的所有应用,调用 scanPackageLI 遍历解析后的每一个 package,进行进一步的处理。

private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
    final File[] files = dir.listFiles();

    // 并行解析目录下的所有应用
    ......

    // 遍历处理解析后的结果
    for (; fileCount > 0; fileCount--) {
        ......
        if (throwable == null) {
            ......
            try {
                if (errorCode == PackageManager.INSTALL_SUCCEEDED) {
                    scanPackageLI(parseResult.pkg, parseResult.scanFile, parseFlags, scanFlags,
                            currentTime, null);
                }
            } catch (PackageManagerException e) {
                errorCode = e.error;
                Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
            }
        }
        ......
    }
    ......
}

scanPackageLI 继续往下调,到 scanPackageInternalLI 中进一步处理。

PS:这里有一个有意思的词:childPackages。之前没有接触过这个概念,在网上真正能搜到对它介绍的博客或文章也几乎没有,立个 flag,后续有时间的话去研究一下,写篇博客做个介绍。

/**
 *  Scans a package and returns the newly parsed package.
 *  @throws PackageManagerException on a parse error.
 */
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, File scanFile,
        final int policyFlags, int scanFlags, long currentTime, @Nullable UserHandle user)
        throws PackageManagerException {
    ......

    // Scan the parent
    PackageParser.Package scannedPkg = scanPackageInternalLI(pkg, scanFile, policyFlags,
            scanFlags, currentTime, user);

    // Scan the children
    final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
    for (int i = 0; i < childCount; i++) {
        PackageParser.Package childPackage = pkg.childPackages.get(i);
        scanPackageInternalLI(childPackage, scanFile, policyFlags, scanFlags,
                currentTime, user);
    }

    ......
    return scannedPkg;
}

Fine。。。 scanPackageInternalLI 中又调回 scanPackageLI 了,不过是另一个重载的函数。

/**
 *  Scans a package and returns the newly parsed package.
 *  @throws PackageManagerException on a parse error.
 */
private PackageParser.Package scanPackageInternalLI(PackageParser.Package pkg, File scanFile,
		int policyFlags, int scanFlags, long currentTime, @Nullable UserHandle user)
		throws PackageManagerException {
	......

	// Note that we invoke the following method only if we are about to unpack an application
	PackageParser.Package scannedPkg = scanPackageLI(pkg, policyFlags, scanFlags
			| SCAN_UPDATE_SIGNATURE, currentTime, user);

	......
	return scannedPkg;
}

scanPackageLI 又是一个简单的封装,继续往下调,到 scanPackageDirtyLI 中进一步处理。

private PackageParser.Package scanPackageLI(PackageParser.Package pkg, final int policyFlags,
        int scanFlags, long currentTime, @Nullable UserHandle user)
                throws PackageManagerException {
    boolean success = false;
    try {
        final PackageParser.Package res = scanPackageDirtyLI(pkg, policyFlags, scanFlags,
                currentTime, user);
        success = true;
        return res;
    } finally {
        if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
            // DELETE_DATA_ON_FAILURES is only used by frozen paths
            destroyAppDataLIF(pkg, UserHandle.USER_ALL,
                    StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
            destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);
        }
    }
}

private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
        final int policyFlags, final int scanFlags, long currentTime, @Nullable UserHandle user)
                throws PackageManagerException {
    ......

    if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
        ......
    } else {
        final int userId = user == null ? 0 : user.getIdentifier();
        // Modify state for the given package setting
        commitPackageSettings(pkg, pkgSetting, user, scanFlags,
                (policyFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/);
        ......
    }

    ......

    return pkg;
}

commitPackageSettings 的方法说明已经说得很清楚了,将一个扫描过的 package 加载到系统。执行完本函数后,这个应用可以被查询到。

在这个函数中,主要将解析得到的 provider,service,receiver,activity 等信息保存起来,便于后续使用过程中进行查询。

/**
 * Adds a scanned package to the system. When this method is finished, the package will
 * be available for query, resolution, etc...
 */
private void commitPackageSettings(PackageParser.Package pkg, PackageSetting pkgSetting,
		UserHandle user, int scanFlags, boolean chatty) throws PackageManagerException {
	final String pkgName = pkg.packageName;
	......

	Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");

	synchronized (mPackages) {
		......

		// 将签名信息保存KeySetManagerService,将解析出来的ContentProvider保存到mProviders,Service保存到mServices
		......

		// 遍历解析得到的Receiver进行处理
		N = pkg.receivers.size();
		r = null;
		for (i=0; i<N; i++) {
			PackageParser.Activity a = pkg.receivers.get(i);
			a.info.processName = fixProcessName(pkg.applicationInfo.processName,
					a.info.processName);
			// 核心代码,将解析出来的receiver都添加到mReceivers中
			mReceivers.addActivity(a, "receiver");
			......
		}

		// 将解析出来的Activity保存到mActivities,instrumentation保存到mInstrumentation,以及PermissionGroup、Permission的变更等
		......
	}

	Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}

当然,除了开机后扫描不同目录下的应用,还需要考虑新安装的应用。但是原理是一样的,虽然是走的 install 的流程,但最终还是会经过调用流程走到 commitPackageSettings,再将解析出来的 receiver 添加保存到 mReceivers 中。

至此,mRecivers 是如何得到的就很清晰明了了。

总结

最后简单做下总结:

Android 在 启动过程中,会扫描 /vendor/app,/system/app,/data/app 等目录下的应用,并进行解析,得到 Activity,Service,Provider,Receiver,Instrumentation 等信息,并保存起来。

应用安装、升级过程中,同样会进行解析,并将解析得到的信息也保存到系统中。

发送广播时,会从之前保存起来的 mReceivers 中,查询广播对应的静态注册的 Receiver,再通过 binder 调用启动应用进程,并在应用启动完成后,调用 BroadcastReceiver.onReceive 进行接收。

所以系统中只是保留了静态注册的 Receiver 信息,真正启动应用并接收广播是在广播发出后,通过查询信息得到对应的 Receiver,再进行相关操作的。


Android 系统的源码太过庞大,分析源码的过程中,经常会遇到跳来跳去,这里调那里的情况。如果在看源码的过程中太过注重细节,每个环节都想搞懂,往往会浪费自己大量的时间,并且跳出去后就很难回到主流程上。时间长了,慢慢地也放弃了。

所以一定要明确自己此次分析源码的目的是什么,先沿着自己关注的问题看下去。在此过程中,很多方法的调用都可以通过方法说明或博客先大概了解就行。先大概理清楚整个调用的主流程,再回过头来,看具体的细节,这样效率反而更高。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值