文章基于API25分析,源码地址:http://androidxref.com/7.1.1_r6/
什么是LoadedApk 看下源码中对LoadedApk的注释:
1 2 3 4 5 /** * Local state maintained about a currently loaded .apk. * @hide */ public final class LoadedApk {}
翻译过来就是维护当前加载的 .apk 的本地状态。大白话讲LoadedApk就是一个.apk文件在内存中的表现形式。
换句话说LoadedApk是apk安装文件在内存中的数据,可以在LoadedApk中得到apk文件中的代码、资源文件、Activity、Service组件、manifest配置文件等等信息,这些都封装在LoadedApk这个类中。
我们知道应用启动是通过Zygote进程fork出一个应用进程之后调用ActivityThread.main()方法才走到应用自身。
1个apk应用对应1个ActivityThread
1个apk应用也对应1个LoadedApk
早前我在阅读源码的时候一直有一个疑惑,既然LoadedApk是apk文件在内存中的表现形态,那我们的应用的ActivityThread里面为什么mPackages、mResourcePackages这俩属性为什么要用map来定义呢?那不是说明会有很多个LoadedApk?而且我之前看一个文章上有一句话:一个进程可以加载多个apk 。我一直不理解?
直到后来我了解插件化的时候我想明白了!插件化有一种方式是定义一个插件LoadedApk然后插入ActivityThread的mPackages集合中,这样在宿主工程中就能轻松访跳转插件中的Activity。原理是使用插件LoadedApk获取到插件的ClassLoader并加载出插件中的Activity类(具体原理我会再行撰文)。这就理解了一个进程是可以加载多个apk的。
既然LoadedApk是apk在内存中的表现形式,其内部包含了整个mApplicationInfo
,那就可以理解用它能做很多事
创建一个Application放在这个类中再合适不过,因为LoadedApk本身就包含了manifest配置文件中的信息。
用它来获取Resources也合情合理,因为资源文件也包含其中。
… 也就是说其实在各个地方只要获取到这个LoadedApk对象就能获取应用的所有信息。
LoadedApk创建过程源码分析 我们了解了App启动流程之后(如不了解看我另一文章 xxx)知道,一个App进程启动之后会立马调用到ActivityThread.main()方法中来,在内部会执行attach()然后调用AMS.attachApplication(IApplicationThread)方法让AMS进程去初始化应用最后将结果通过IApplication这个接口返回给应用进程。此时应用进程会通过H这个Handler走到handleBindApplication()这个方法,应用就开始处理并创建Application了,LoadedApk也是在这一步生成的。
ActivityThread.attach() -> AMS.attachApplication(IApplicationThread thread) -> ApplicationThread.bindApplication() -> H -> ActivityThread.handleBindApplication() -> ActivityThread. getPackageInfoNoCheck(data.appInfo, data.compatInfo) -> LoadedApk.构造()
直接分析getPackageInfoNoCheck():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 ActivityThread.java public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,CompatibilityInfo compatInfo) { return getPackageInfo(ai, compatInfo, null, false, true, false); } private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo, ClassLoader baseLoader, boolean securityViolation, boolean includeCode, boolean registerPackage) { // 判断应用uid,一般正常安装的apk differentUser值为false,除非是手动去加载的其他apk final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid)); synchronized (mResourcesManager) { WeakReference<LoadedApk> ref; if (differentUser) { // Caching not supported across users ref = null; } else if (includeCode) { // 传入的includeCode为true,所以从缓存中取出LoadedApk对象 ref = mPackages.get(aInfo.packageName); } else { ref = mResourcePackages.get(aInfo.packageName); } LoadedApk packageInfo = ref != null ? ref.get() : null; // 如果是刚进应用则缓存中就不存在该apk对应的LoadedApk,就会去走创建流程 if (packageInfo == null || (packageInfo.mResources != null && !packageInfo.mResources.getAssets().isUpToDate())) { if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package " : "Loading resource-only package ") + aInfo.packageName + " (in " + (mBoundApplication != null ? mBoundApplication.processName : null) + ")"); // 创建一个新的LoadedApk对象,里面包含了ApplicationInfo,CompatibilityInfo等信息 packageInfo = new LoadedApk(this, aInfo, compatInfo, baseLoader, securityViolation, includeCode && (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage); if (mSystemThread && "android".equals(aInfo.packageName)) { packageInfo.installSystemApplicationInfo(aInfo, getSystemContext().mPackageInfo.getClassLoader()); } if (differentUser) { // Caching not supported across users } else if (includeCode) { // 创建完成之后会将其缓存起来,后面系统流程中需要使用的时候直接根据对应的包名从缓存中获取 mPackages.put(aInfo.packageName, new WeakReference<LoadedApk>(packageInfo)); } else { mResourcePackages.put(aInfo.packageName, new WeakReference<LoadedApk>(packageInfo)); } } return packageInfo; } }
这就是应用的LoadedApk的创建流程,只是简单理解不去深究里面的ApplicationInfo、CompatibilityInfo这些是如何构造的就很简单,要深究的话一篇也讲不完。
在App的启动过程中,LoadedApk做的一个很关键的事情就是创建Application对象,我们简单看看handleBindApplication
内通过LoadedApk去创建一个Application的过程:
1 2 3 4 5 6 7 ActivityThread.java private void handleBindApplication(AppBindData data) { ... Application app = data.info.makeApplication(data.restrictedBackupMode, null); ... }
调用到LoadedApk.makeApplication()
方法去了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 public Application makeApplication(boolean forceDefaultAppClass,Instrumentation instrumentation) { if (mApplication != null) { return mApplication; } Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication"); Application app = null; // 获取manifest配置文件中指定的Application,如果我们自定义了那么appClass就是我们自定义Application的类名,否则系统默认创建类名为"android.app.Application"的默认Application对象 String appClass = mApplicationInfo.className; if (forceDefaultAppClass || (appClass == null)) { appClass = "android.app.Application"; } try { // 获取应用类加载器,正常情况下取出来的会是PathClassLoader java.lang.ClassLoader cl = getClassLoader(); // 表示非系统应用 if (!mPackageName.equals("android")) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "initializeJavaContextClassLoader"); initializeJavaContextClassLoader(); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } // 先创建一个ContextImpl对象后续赋值给Application对应的mBase,完成Application对ContextImpl的代理,此处会将本LoadedApk实例传给Application对应的ContextImpl,其实如果了解Activity的构建的话可以去看下,Activity对应的ContextImpl中也传入了这个LoadedApk对象。正常情况如果没有加载多个apk的话,全局都是共用的这一个LoadedApk对象 ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this); // 调用ActivityThread的mInstrumentation传入类名和类加载器执行创建实例操作,内部是用的反射来实现的创建。 app = mActivityThread.mInstrumentation.newApplication( cl, appClass, appContext); appContext.setOuterContext(app); } catch (Exception e) { if (!mActivityThread.mInstrumentation.onException(app, e)) { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); throw new RuntimeException( "Unable to instantiate application " + appClass + ": " + e.toString(), e); } } mActivityThread.mAllApplications.add(app); mApplication = app; if (instrumentation != null) { try { instrumentation.callApplicationOnCreate(app); } catch (Exception e) { if (!instrumentation.onException(app, e)) { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); throw new RuntimeException( "Unable to create application " + app.getClass().getName() + ": " + e.toString(), e); } } } // Rewrite the R 'constants' for all library apks. SparseArray<String> packageIdentifiers = getAssets(mActivityThread) .getAssignedPackageIdentifiers(); final int N = packageIdentifiers.size(); for (int i = 0; i < N; i++) { final int id = packageIdentifiers.keyAt(i); if (id == 0x01 || id == 0x7f) { continue; } rewriteRValues(getClassLoader(), packageIdentifiers.valueAt(i), id); } Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); return app; }
代码注释中清楚解释了如何使用LoadedApk去构建Application。其中涉及到了Instrumentation对象,其实这个对象只是一个对Application和Activity创建和生命周期流程等操作的统一封装,这样能使代码结构更加清晰。并且其内部还有一些模拟按键的方法。
小结
LoadedApk是apk安装文件在内存中的数据,可以在LoadedApk中得到apk文件中的代码、资源文件、Activity、Service组件、manifest配置文件等等信息,这些都封装在LoadedApk这个类中。
在正常情况下同一个安装包下的Activity和Application等这些组件都是公用的一个LoadedApk对象。
在任何场景下只要获取了LoadedApk对象就能获取到应用的信息,例如Activity中获取资源其实也是通过ContextImpl获取了应用的LoadedApk对象实例然后去调用的ActivityThread.getTopLevelResources();
方法。