(本文来自于和博客上一个朋友的聊天,但可惜我回复后一直没收到这位朋友的回答。故在此把这个问题和大家介绍下,希望能抛砖引玉)
这位朋友的问题是这样的:
private static Class<?> loadPluginClass(Context launcherContext, String packageName, String className, int type) {
try {
Context context = launcherContext.createPackageContext(packageName,
Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE);
final String apkPath = context.getApplicationInfo().sourceDir;
PathClassLoader l = new PathClassLoader(apkPath, context.getClassLoader());
return l.loadClass(className);
} ......
}
public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
CompatibilityInfo compatInfo, ClassLoader baseLoader,
boolean securityViolation, boolean includeCode, boolean registerPackage) {
final int myUid = Process.myUid();
aInfo = adjustNativeLibraryPaths(aInfo);
......
mRegisterPackage = registerPackage; // 在这里被“绑定”了
mDisplayAdjustments.setCompatibilityInfo(compatInfo);
}
Android 5.0之后为什么会这样呢?进程A和APK B这种依赖关系有什么用呢?
原来,Android 5.0之后:
1 当进程A加载一个package的时候,framework将调用ActivityManagerService的addPackageDependency
这个函数将把进程A和APK B(也就是Package B)绑定到一起去
这个函数在哪里调用的呢?也在LoadedApk.java中:
public ClassLoader getClassLoader() {
synchronized (this) {
if (mClassLoader != null) {
return mClassLoader;
}
if (mIncludeCode && !mPackageName.equals("android")) {
......
if (mRegisterPackage) {
try {
ActivityManagerNative.getDefault().addPackageDependency(mPackageName);
} catch (RemoteException e) {
}
}
2 进程B(也就是Package B本身运行时所在的进程)被杀时,有依赖关系的进程A也会被干掉。这里有几个注意点:
2.1 如果进程B是自己crash或者被shell kill掉,那么依赖关系不会影响进程A
2.2 如果进程B是被调用killBackgroundProcess或者forceStopPackage的话,由于ActivityManagerService真正调用的是killPackageProcessesLocked
那么依赖关系会导致A被干掉。 从设计角度来看,这本身也是对的,因为APK B运行在自己的进程B,同时也被加载到进程A去运行。
kill package B的时候就应该stop进程B和A。
当然,由于5.0之前google没有考虑这么细,所以没有处理这个问题。
当然,这种依赖关系的引入还有一个原因是Android 5.0在应用程序安装方面的一些新的特性:
1 以前的apk文件 side load到/daa/app等监控目录下不会导致PacakgeManagerService去安装它们。而是需要等到下次重启扫描后,系统才会扫描并安装它们
2 adb install安装的APK,在/data/app目录下会创建一个Package-name(比如com.google.xxx)的文件夹,而apk文件被放到这个package-name目录下,改名叫base.apk
这么搞的目的是因为:Android终于支持一个进程可以加载多个APK了(当然以前也可以,但现在安装的时候,base-apk是主要逻辑,以后升级了,或者添加新的功能,就不需要重新安装新的base,而是安装一个额外的新apk,这个新apk会被加载到base的那个进程里。)这个功能在SDK文档中已经有展示,但是内容不是很详细
总之,base apk和新apk需要package名, 签名都一致才可以。