android sharedUserId 使用知识盲点解析

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

android sharedUserId 使用知识盲点解析

Exploring   2023-03-21 我要评论

1. 背景

由于在工程中使用了 SPI 机制,通过 ServiceLoader 的配合来完成模块间的通信。但是突然收到线上客户反馈使用了 SDK 后无法进行模块加载,导致部分功能异常。

2. 分析排查

借助客户提供的测试包进行 debug 调试,发现在调试到 ServiceLoader.load() 方法时确实无法加载到对应的模块配置。查看 ServiceLoader 的状态信息如下:

其中的 loader 是 LoadApk$WarningContextClassLoader 对象,而正常情况下是 DexPathClassLoader。

2.1 查看 ServiceLoader.loader 定义

ServiceLoader API 文档:developer.android.com/reference/j…

根据接口定义 load 方法会根据指定的 serviceType 创建新的 ServiceLoader 对象返回,ServiceLoader 内部根据当前线程对应的 ContextClassLoader 对象去加载配置,所以到这里可以分析到 load 方法的加载结果会受 ContextClassLoader 的影响,进一步推理可能收到插件化、热修复等框架影响,确认后并没有使插件化、热修复等框架。

2.2 WarningContextClassLoader 为何物?

查找 Android famework 源码,找到 WarningContextClassLoader 是定义在 LoaderApk 文件中的内部类(部分版本是 ActivityThread 类中的内部类)。

private void initializeJavaContextClassLoader() {
	IPackageManager pm = ActivityThread.getPackageManager();
	android.content.pm.PackageInfo pi =
			PackageManager.getPackageInfoAsUserCached(
					mPackageName,
					PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
					UserHandle.myUserId());
	if (pi == null) {
		throw new IllegalStateException("Unable to get package info for "
				+ mPackageName + "; is package not installed?");
	}
	/*
	 * Two possible indications that this package could be
	 * sharing its virtual machine with other packages:
	 *
	 * 1.) the sharedUserId attribute is set in the manifest,
	 *     indicating a request to share a VM with other
	 *     packages with the same sharedUserId.
	 *
	 * 2.) the application element of the manifest has an
	 *     attribute specifying a non-default process name,
	 *     indicating the desire to run in another packages VM.
	 */
	boolean sharedUserIdSet = (pi.sharedUserId != null);
	boolean processNameNotDefault =
		(pi.applicationInfo != null &&
		 !mPackageName.equals(pi.applicationInfo.processName));
	boolean sharable = (sharedUserIdSet || processNameNotDefault);
	ClassLoader contextClassLoader =
		(sharable)
		? new WarningContextClassLoader()
		: mClassLoader;
	Thread.currentThread().setContextClassLoader(contextClassLoader);
}
private static class WarningContextClassLoader extends ClassLoader {
	private static boolean warned = false;
	private void warn(String methodName) {
		if (warned) {
			return;
		}
		warned = true;
		Thread.currentThread().setContextClassLoader(getParent());
		Slog.w(ActivityThread.TAG, "ClassLoader." + methodName + ": " +
			  "The class loader returned by " +
			  "Thread.getContextClassLoader() may fail for processes " +
			  "that host multiple applications. You should explicitly " +
			  "specify a context class loader. For example: " +
			  "Thread.setContextClassLoader(getClass().getClassLoader());");
	}
	...
}

在应用创建时会调用 ActivityThread 类中的 attach 方法中,attach 方法进而调用 LoadedApk 类中的 makeApplicationInner() 用于创建对应的 Application 对象。在 makeApplicationInner() 方法的内部调用 initializeJavaContextClassLoader 方法创建对应的 ContentClassLoader 对象,在 initializeJavaContextClassLoader 方法的内部可以看到,如果当前 App 在 manifest 中设置 sharedUserId 属性,则当前应用使用的是 WarningContextClassLoader。下面我们就是查看 App 中的配置。

最终验证了我们的猜想,使用 demo 设置 sharedUserId 属性问题可正常复现。

2.3 sharedUserId 属性

查看官方文档该属性配置 API 级别 29 中已弃用此常量。共享用户 ID 会在软件包管理器中导致具有不确定性的行为。因此,强烈建议您不要使用它,并且我们在未来的 Android 版本中会将其移除。

2.总结

排查问题还是比较费神,在没有明显错误的时候,只能针对每个可疑的信息去分析,期望发现蛛丝马迹。

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们