我们知道一个APK中主要包含了dex字节码、AndroidManifest.xml、res目录下的各种资源、以及resources.arsc等等,也就是说一般情况下我们的一个APK既是一个dex包,也是一个资源包。注意我们前面说的是一般情况下,既然有一般,那么肯定也就有二般~ ~。典型的二般情况有种:资源共享库和Android系统资源包。关于资源共享库,Android资源管理中的SharedLibrary和Dynamic Reference-------之资源共享库(一)这个系列的文章详细介绍了其概念、原理、实现、应用,如有兴趣,可以详细阅读;而Android系统资源包则是指framework-res.apk,这个APK里则是只有资源,系统的代码在用到这些资源的时候是可以从这个包中获取的,其实我们的APK也离不开这个包里的资源,比如我们在xml文件里用到的android:
打头的资源,在运行时都会去framework-res.apk里查找。
既然说到了系统资源包,那就顺便说一下android-sdk里的android.jar吧,它也是一个不太守规矩的家伙。你以为它就是个jar包?错!错!错!
我们看看android.jar里可不只有class文件这么简单,还有AndroidManifest.xml,还有res目录,而且AndroidManifest.xml和res目录下面的xml类型的资源还都是编译过的,最关键的是它还有resources.arsc!看到这些,我们能想到的就是,这哪里是一个普通的jar包,这分明是一个APK!不过,有一点和APK不一样,就是这里面直接放的是class文件,没有用dx工具转化成dex,毕竟这个是给我们应用开发的时候编译用的,弄成dex就不好编译了。不知道大家有没有疑问,android.jar里的资源和framework-res.apk里的资源有什么异同?其实,这两者里面的资源大部分都是相同的,差别在于,对于一些非公开的资源,android.jar里是引用不到的,而framework-res.apk里则包括了系统全部的资源,毕竟android.jar只是编译使用,不想公开的不放进去就可以了,但framework-res.apk则是运行时要加载的,少了资源会崩溃的。当然,android.jar里的class和framework.jar的异同也基本类似。另外还有一点,framework-res.apk的包名非常简洁,就叫android,我们引用Android系统资源的时候通常是形如android:color/white这种形式,这里冒号前面的android指的就是framework-res.apk的包名(更确切地说,xmlns:android="http://schemas.android.com/apk/res/android
这样的namespace中,res/androd
中的android指的是framework-res.apk的包名)。
说完Android的系统资源包,我们再说一般的应用包,也就是APK。一个APK里面只要有资源,那么它本身也是一个资源包。但是这并不意味着这个App在运行的时候只会依赖并加载它本身这一个资源包。相反,只依赖本身资源的APK,基本没有,一个App可以没有布局,没有界面,但总要有AndroidManifest.xml吧,这里面,它总会用到android打头的属性吧,那么不论在编译时还是运行时,这个App都会依赖framework-res.apk这个系统资源包了。其实,一个跑在mtk平台上的APK,很可能要加载四个资源包:Android本身的资源包framework-res.apk、mtk自己的系统资源包、手机厂商的系统资源包以及APK本身。如果考虑到资源共享库和RRO(Runtime Resources Overlay,Android资源管理中的Runtime Resources Overlay-------之概述(一)这里有RRO详细的介绍)包,那么这个APK要加载的资源包就会更多了。另外,我们说的加载一个系统包,是指加载包内的resources.arsc文件,而非一定是要加载其整个包。
既然一个应用在运行时要加载那么多的包,那么这些包被加载到了哪里呢?我们知道Resources
类是一个App资源相关的接口类,我们资源相关的大部分操作都要通过它来完成。不过,资源包的加载并不是通过resources
来完成的,而是更加low level的AssetManager
类,其实Resources
类是对AssetManager
类的一个封装,Resources
类的大部分功能都是通过AssetManager
来实现的。当然,Resources
中还封装了Theme相关的东西,关于Theme,Android资源管理中的Theme和Style-------之总述(一)系列文章已经详细描述,这里不再多说。
下面我们看一下AssetManager的构造方法,一共有两个,一个是public的,一个是private的:
//frameworks/base/core/java/android/content/res/AssetManager.java
private AssetManager(boolean isSystem) {
//...省略非核心代码
init(true);
}
public AssetManager() {
synchronized (this) {
//...省略非核心代码
init(false);
ensureSystemAssets();
}
}
//如果系统的AssetManager对象没有创建,则创建之
private static void ensureSystemAssets() {
synchronized (sSync) {
//sSystem是系统的AssetManager对象
if (sSystem == null) {
AssetManager system = new AssetManager(true);
//创建java层的Global String Pool
system.makeStringBlocks(null);
sSystem = system;
}
}
}
这两个方法构造方法都非常简洁,其中private的构造方法是用来创建系统资源对应的AssetManager
对象的,public的则是用来创建一般应用的AssetManager
对象的。其中每一个应用的AssetManager
对象中都有一个系统的AssetManager
对象sSystem。当我们创建一个非System的AssetManager对象时,如果System的AssetManager对象还没有创建,我们会把sSystem也给创建了。至于system.makeStringBlocks(null);
这句创建java 层Global String Pool的实现,我们后面再讲,这里先看init方法的实现,init是一个native方法,我们直接看native层:
//frameworks/base/core/jni/android_util_AssetManager.cpp
static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
{
/** *RRO相关的处理,系统的overlay package的idmap是在这里直接做的,不再经过PMS, *如果没有生成framework-res.apk的idmap文件,则会在这里生成 */
if (isSystem) {
verifySystemIdmaps();
}
//创建native层的AssetManager对象
AssetManager* am = new AssetManager();
if (am == NULL) {
jniThrowException(env, "java/lang/OutOfMemoryError", "");
return;
}
//给AssetManager对象添加默认的资源包了
am->addDefaultAssets();
ALOGV("Created AssetManager %p for Java object %p\n", am, clazz);
//在java层的AssetManager中,有一个成员 mObject,记录native层创建的这个AssetManager对象的地址,
//在这里给它赋值
env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast<jlong>(am));
}
init
方法主要就是创建native层的AssetManager
对象,并把其地址存到java层的AssetManager
对象的mObject成员变量中,再有就是给AssetManager对象添加默认的资源包了,我们看看都添加了哪些资源包:
//frameworks/base/core/libs/androidfw/AssetManager.cpp
bool AssetManager::addDefaultAssets()
{
const char* root = getenv("ANDROID_ROOT");
LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_ROOT not set");
//ALOGD("AssetManager-->addDefaultAssets CIP path not exsit!");
String8 path(root);///system
//static const char* kSystemAssets = "framework/framework-res.apk";
//所以这里就是添加系统/system/framework/framework-res.apk这个资源包了
path.appendPath(kSystemAssets);
//添加进去
bool isOK1 =addAssetPath(path, NULL);
String8 path2(root);
//static const char* kMediatekAssets = "framework/mediatek-res/mediatek-res.apk";
//所以这里就是添加MTK的/system/framework/mediatek-res/mediatek-res.apk这个资源包了
path2.appendPath(kMediatekAssets);
bool isOK2 =addAssetPath(path2, NULL);
if(!isOK2){
ALOGW("AssetManager-->addDefaultAssets isok2 is false");
}
//添加手机厂商自己的系统资源包,具体路径这里就不方便贴出来了
String8 path3(root);
path3.appendPath(kBmigoAssets);
bool isOK3 =addAssetPath(path3, NULL);
if(!isOK3){
ALOGW("AssetManager-->add mogo-framework-res failed");
return isOK3;
}
我们看到,不论是APK的AssetManager
,还是sSystem这个系统的AssetManager
,这里都会添加3个系统资源包,分别是Android本身的、MTK的、手机厂商的。也就是说,在构造一个java层的AssetManager
对象的时候,这三个资源包,都会作为系统资源包,添加到构造的AssetManager
对象中去。另外,我们的apk进程在构造完AssetManager
对象后,还会把自己添加到这个AssetManager
中,这样我们的App的AssetManager
中就有四个资源包了。
前面我们讲到system.makeStringBlocks(null);
时没有讲其实现,下面我们简单看一下它的实现。首先这一句是给system(AssetManager
的一个对象,表示是系统AssetManager
)创建Global String Pool。至于什么是Global String Pool,我们在讲resources.arsc文件时会详细来讲,这里我们只要知道当我们从资源管理框架查找某一资源,拿到的数据类型是字符串时,我们拿到的数据不是结果字符串,而是两个信息,第一个信息表示在哪个Global String Pool中,第二个信息表示结果字符串在这个Global String Pool中的索引,我们根据这两个信息到对应的Global String Pool中的对应位置,就可以拿到结果字符串了。
final void makeStringBlocks(StringBlock[] seed) {
final int seedNum = (seed != null) ? seed.length : 0;
/** * 总的Global String Pool的个数,由于一个资源包有且只有一个Global String Pool * 所以,也就是已经加载的资源包的个数,在这里我们添加了android、mtk、手机厂商三个资源包,所以num = 3 */
final int num = getStringBlockCount();
mStringBlocks = new StringBlock[num];
if (localLOGV) Log.v(TAG, "Making string blocks for " + this
+ ": " + num);
for (int i=0; i<num; i++) {
//seed会被放到最前面,表示已经创建过了,所以不用new了
if (i < seedNum) {
mStringBlocks[i] = seed[i];
} else {
//去native层拿到后后面的StringBlock
mStringBlocks[i] = new StringBlock(getNativeStringBlock(i), true);
}
}
}
//frameworks/base/core/jni/android_util_AssetManager.cpp
static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz)
{
//assetManagerForJavaObject函数值得注意,马上分析
AssetManager* am = assetManagerForJavaObject(env, clazz);
if (am == NULL) {
return 0;
}
//其实就是我们已经加载的资源包的个数,后面会详细讲,这里就不细说了
return am->getResources().getTableCount();
}
//frameworks/base/core/jni/android_util_AssetManager.cpp
static jlong android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz,
jint block)
{
//assetManagerForJavaObject函数值得注意,马上分析
AssetManager* am = assetManagerForJavaObject(env, clazz);
if (am == NULL) {
return 0;
}
//去我们加载的第block资源包中取出Global String Pool的地址,后面会详细讲,这里就不细说了
return reinterpret_cast<jlong>(am->getResources().getTableStringBlock(block));
}
还记的java层的AssetManager
在构造的时候会保存一个native层AssetManager
的地址在mObject
成员中吗?既然保存了它,当然有用,有什么用,怎么用呢?
AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj)
{
//拿到java层的mObject,它存的就是native层的AssetManager对象的地址
jlong amHandle = env->GetLongField(obj, gAssetManagerOffsets.mObject);
//直接强转,得到native层的AssetManager对象
AssetManager* am = reinterpret_cast<AssetManager*>(amHandle);
if (am != NULL) {
return am;
}
jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!");
return NULL;
}
看完这个函数的实现,java层AssetManager
中的mObject成员的作用一目了然。StringBlock的创建就先讲到这里了,后面还会深入将,下面我们看看Android的资源是如何加载的。这个问题得分开说:system_server和一般App中资源的加载还不太一样。system_server在起来以后,会创建ActivityThread、Context等对象:
//frameowrk/base/core/java/android/app/ActivityThread.java
public static ActivityThread systemMain() {
//...省略无关代码
ActivityThread thread = new ActivityThread();
//true表示是system_server进程,否则表示是应用进程
thread.attach(true);
return thread;
}
private void attach(boolean system) {
//...省略无关代码
ContextImpl context = ContextImpl.createAppContext(
this, getSystemContext().mPackageInfo);
//...省略无关代码
}
public ContextImpl getSystemContext() {
synchronized (this) {
if (mSystemContext == null) {
mSystemContext = ContextImpl.createSystemContext(this);
}
return mSystemContext;
}
}
我们看到system_server起来后,会先去创建ActivityThread对象,然后创建Context对象,我们看看系统的Context对象是如何创建的:
//framework/base/core/java/android/app/ContextImpl.java
static ContextImpl createSystemContext(ActivityThread mainThread) {
//在LoadedApk对象packageInfo构造的时候,会去加载系统资源
LoadedApk packageInfo = new LoadedApk(mainThread);
//创建Context对象
ContextImpl context = new ContextImpl(null, mainThread,
packageInfo, null, null, false, null, null);
//*把系统资源配置信息写入资源管理框架
context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
context.mResourcesManager.getDisplayMetricsLocked(Display.DEFAULT_DISPLAY));
return context;
}
//framework/basecore/java/android/app/LoadedApk.java
LoadedApk(ActivityThread activityThread) {
mActivityThread = activityThread;
mApplicationInfo = new ApplicationInfo();
mApplicationInfo.packageName = "android";
mPackageName = "android";
mAppDir = null;
mResDir = null;
mSplitAppDirs = null;
mSplitResDirs = null;
mOverlayDirs = null;
mSharedLibraries = null;
mDataDir = null;
mDataDirFile = null;
mLibDir = null;
mBaseClassLoader = null;
mSecurityViolation = false;
mIncludeCode = true;
mRegisterPackage = false;
mClassLoader = ClassLoader.getSystemClassLoader();
//关键在这里
mResources = Resources.getSystem();
}
我们看到,在创建Context的时候,要传入一个ActivityThread对象,同时也要传入一个LoadedApk对象,而LoadedApk里主要存储两个方面的信息:一个是包相关的信息,比如包名、路径等等;另外一个就是资源,也就是mResources对象。也就是说,对于system_server而言,一个systemContext的主要意义在于:存储system_server进程相关的信息;存储系统资源相关的信息。我们看到,system_server也会有ApplicationInfo,包名就叫android
,也就是framework-res.apk
的包名。
//framework/base/core/java/android/content/res/Resources.java
public static Resources getSystem() {
//典型的单例模式,上来就申请锁,不判断是否已经构造,差评~~
synchronized (sSync) {
Resources ret = mSystem;
if (ret == null) {
ret = new Resources();
mSystem = ret;
}
return ret;
}
}
private Resources() {
//拿到系统的AssetManager对象
mAssets = AssetManager.getSystem();
//默认资源配置
mConfiguration.setToDefaults();
mMetrics.setToDefaults();
updateConfiguration(null, null);
//创建java层的Global string pool
mAssets.ensureStringBlocks();
}
//framework/base/core/java/android/content/res/AssetManager.java
public static AssetManager getSystem() {
//这个方法我们前面讲过了,还有印象吗?~~
ensureSystemAssets();
return sSystem;
}
到这里,我们已经完全看到system_server自己的资源的构造过程了,system_server已经加载了android本身的系统资源包framework-res.apk
、MTK的系统资源包mediatek-res.apk
以及手机厂商的系统资源包。
接下来我们看看应用的资源包是如何加载的。应用的启动流程,我们在这里就不作介绍了,如果大家感兴趣,可以瞧瞧frameworks/base/cmds/app_process
(也就是Zygote)以及ZygoteInit.java
和RuntimeInit.java
相关的代码。不过这里有一点到时可以说说,就是Zygote进程的preload,由于所有Android进程(包括system_server进程)都是从Zygote进程fork出来的,所以Zygote进程起来后就会预先加载许多系统相关的东西,比如我们常用的各种类,系统资源等,这样就可以避免重复加载:
//framework/base/core/java/com/android/internal/os/ZygoteInit.java
static void preload() {
Log.d(TAG, "begin preload");
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "BeginIcuCachePinning");
beginIcuCachePinning();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadClasses");
preloadClasses();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadResources");
//我们重点关注这里
preloadResources();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL");
preloadOpenGL();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
preloadSharedLibraries();
preloadTextResources();
// Ask the WebViewFactory to do any initialization that must run in the zygote process,
// for memory sharing purposes.
WebViewFactory.prepareWebViewInZygote();
endIcuCachePinning();
warmUpJcaProviders();
Log.d(TAG, "end preload");
}
private static void preloadResources() {
//...省略无关代码
mResources = Resources.getSystem();
mResources.startPreloading();
//...省略无关代码
int N = preloadDrawables(ar);
//更新ar
N = preloadColorStateLists(ar);
//更新ar
N = preloadDrawables(ar);
mResources.finishPreloading();
}
preload我们简单说一下就略过了哈,当我们的应用进程起来后会走到ActivityThread
的main
方法:
//frameowrk/base/core/java/android/app/ActivityThread.java
//运行于App进程
public static void main(String[] args) {
//创建应用主线程的消息队列,用于主线程的消息循环
Looper.prepareMainLooper();
//创建ActivityThread对象
ActivityThread thread = new ActivityThread();
//false表示是应用进程,而非system_server
thread.attach(false);
//主线程开始进入消息循环。
Looper.loop();
//...省略无关代码
}
private void attach(boolean system) {
//...省略无关代码
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
/** * mAppThread这个参数,也是一个Binder,把这个传给system_server(AMS),AMS就可以主动调用应用进程 * 的接口方法,从而实现system_server主动和应用进程的通信 */
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
}
//...省略无关代码
}
我们看到应用程序会在主线程里先调用prepareMainLooper创建消息队列,然后通过Binder调用attachApplication方法,调到system_server。最后,开始消息循环,也就是我们的应用程序的主线程开始干活了,它主要用来处理应用进程和system_server交互的相关东西,比如四大组件的生命周期等,这也就是我们不能在应用程序主线程进行耗时操作的原因。毕竟主线程是用来管理应用以及UI显示的,如果把它用作其它耗时操作,那轻则各种卡顿,重则生命周期迟迟不能进行,甚至出错。
//framework/base/services/core/java/com/android/server/am/ActivityManagerService.java
//运行于system_server进程
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
//关键一句
attachApplicationLocked(thread, callingPid);
Binder.restoreCallingIdentity(origId);
}
}
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
//...省略无关代码
thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(mConfiguration), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked());
//...省略无关代码
}
我们看到AMS又通过thread.bindApplication方法回调到应用进程了:
//frameowrk/base/core/java/android/app/ActivityThread.java
//运行于App进程
public final void bindApplication(String processName, ApplicationInfo appInfo,
List<ProviderInfo> providers, ComponentName instrumentationName,
ProfilerInfo profilerInfo, Bundle instrumentationArgs,
IInstrumentationWatcher instrumentationWatcher,
IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent,
Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
Bundle coreSettings) {
//...省略无关代码
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.providers = providers;
data.instrumentationName = instrumentationName;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.instrumentationUiAutomationConnection = instrumentationUiConnection;
data.debugMode = debugMode;
data.enableOpenGlTrace = enableOpenGlTrace;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
data.config = config;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
sendMessage(H.BIND_APPLICATION, data);
//...省略无关代码
}
方法就是封装了一下这些参数,然后发送消息,消息在主线程中循环,最后走到:
private void handleBindApplication(AppBindData data) {
//frameowrk/base/core/java/android/app/ActivityThread.java
//运行于App进程
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
}
//frameworks/base/core/java/android/app/ContextImpl.java
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
return new ContextImpl(null, mainThread,
packageInfo, null, null, false, null, null);
}
private ContextImpl(ContextImpl container, ActivityThread mainThread,
LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
Display display, Configuration overrideConfiguration) {
//...省略无关代码
//对于一个Context而言,这两个成员是灵魂
mMainThread = mainThread;
mPackageInfo = packageInfo;
//创建mResourcesManager实例
mResourcesManager = ResourcesManager.getInstance();
//创建资源
Resources resources = packageInfo.getResources(mainThread);
//...省略无关代码
mResources = resources;
//...省略无关代码
}
同样,还是构造Context,对于一个Context而言,最重要的两个成员变量就是mainThread和mPackageInfo,为什么呢?个人认为,Context有三个方面的作用:一、代表应用进程和system_server交互,接受system_server的调度;二、提供应用相关的信息,比如包名、apk路径等;三、提供Android资源相关的接口。从这三个功能来讲,用来让system_server调度的接口IApplicationThread服务端的实现在mMainThread中;App的应用信息和资源均在mPackageInfo中,所以这两者对于Context来说非常非常重要。
//framework/base/core/java/android/app/LoadedApk.java
public Resources getResources(ActivityThread mainThread) {
if (mResources == null) {
mResources = mainThread.getTopLevelResources(mResDir, mSplitResDirs, mOverlayDirs,
mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, this);
}
return mResources;
}
又回到了ActivityThread
中:
//frameowrk/base/core/java/android/app/ActivityThread.java
Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs,
String[] libDirs, int displayId, Configuration overrideConfiguration,
LoadedApk pkgInfo) {
return mResourcesManager.getTopLevelResources(resDir, splitResDirs, overlayDirs, libDirs,
displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo(), null);
}
//frameworks/base/core/java/android/app/ResourcesManager.java
public Resources getTopLevelResources(String resDir, String[] splitResDirs,
String[] overlayDirs, String[] libDirs, int displayId,
Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) {
//先从缓存中查找,如果已经创建,则直接返回,这部分代码略过
/** * 创建AssetManager对象,根据我们前面的分析,这时候它会创建sSystem成员 * 并且assets中已经加载了android源生、MTK、手机厂商三个系统资源包 * 但是没有加载我们这个应用Apk本省 */
AssetManager assets = new AssetManager();
//resDir表示我们这个apk本身,在这里被添加到了AssetManager中!
//到这里,AssetManager总算把我们应用的apk添加进去了!!!
if (resDir != null) {
if (assets.addAssetPath(resDir) == 0) {
return null;
}
}
//添加Runtime Resources Overlay
if (overlayDirs != null) {
for (String idmapPath : overlayDirs) {
assets.addOverlayPath(idmapPath);
}
}
//添加资源共享库
if (libDirs != null) {
for (String libDir : libDirs) {
if (assets.addAssetPath(libDir) == 0) {
Slog.w(TAG, "Asset path '" + libDir +
"' does not exist or contains no resources.");
}
}
}
//其它处理,放入缓存,略过
}
ResourcesManager
是Android为了便于Resources复用,避免资源重复加载而设计的一个类,我们不用太关心。真正关键的是getTopLevelResources
这个方法,AssetManager
最终是在这里创建的,系统资源包和apk本身这个资源包,也是在这里加进去的。关于RRO(Runtime Resources Overlay)和资源共享库前文已经描述过,这里不再多说。
至此,system_server和应用进程资源的加载我们已经分析完成,当然,只分析到java层的AssetManager
,更深入的分析我们放到后面。