[原]Android 5.0 进程A和APK B依赖关系问题的研究

admin 10年前

(本文来自于和博客上一个朋友的聊天,但可惜我回复后一直没收到这位朋友的回答。故在此把这个问题和大家介绍下,希望能抛砖引玉)

这位朋友的问题是这样的:

应用程序A运行时跑在进程A中,它可以在运行时通过ClassLoader加载另外一个应用程序B。

当然,应用程序B也是可以运行的,它运行在进程B中。

在Android 5.0以前,进程B和进程A是没有关系。但是5.0以后,如果进程B被stop的话,进程A一样会被干掉。

这就是Android 5.0带来的进程A/B依赖关系。

这个问题有几个关键技术点:

1 进程A如何加载应用程序B。这是通过ClassLoader来完成的,主要代码如下所示:

(来自这位朋友的提示:)

     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);                  } ......        }  


在load的时候:

1 在Android 5.0以前,进 程A和应用程序B的关系:没有显示的关系。就是普通的加载

2 在Android 5.0及以后,进程A和应用程序B的关系:显示的绑定。代码在LoadedApk.java中

-->每一个被加载的Apk都对应一个LoaderApk对象。代码在LoadedApk.java中

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名, 签名都一致才可以。

原文  http://blog.csdn.net/innost/article/details/43899041

</div> </div>