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

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

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

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




郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。