Android资源管理框架-------之Android中的资源包(二)

        我们知道一个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里查找。
只有资源的framework-res.apk
        既然说到了系统资源包,那就顺便说一下android-sdk里的android.jar吧,它也是一个不太守规矩的家伙。你以为它就是个jar包?错!错!错!
android.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.javaRuntimeInit.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我们简单说一下就略过了哈,当我们的应用进程起来后会走到ActivityThreadmain方法:

//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,更深入的分析我们放到后面。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值