我们知道一个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.cppstatic 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.cppbool 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.cppstatic 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.cppstatic 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.javapublic 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.javastatic 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.javapublic 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.javapublic 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.javapublic 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.javaResources 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.javapublic 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,更深入的分析我们放到后面。

更多相关文章

  1. Android(安卓)Eclipse JNI 调用 .so文件加载问题
  2. android如何支持多屏幕
  3. Android(安卓)、资源分目录存放
  4. Android资源String中html标签的使用
  5. Native层HIDL服务的获取原理-Android10.0 HwBinder通信原理(七)
  6. Android中Fragment的使用
  7. Android上webview界面切换动画效果
  8. android屏幕旋转可能带来的问题
  9. Android(安卓)机器人:使用系统资源

随机推荐

  1. Android ListView CheckBox点击事件
  2. Android月历控件(DatePicker)和时间控件(Tim
  3. Android - 文件读写操作 总结
  4. Android
  5. Android在开机时自动启动一个应用程序
  6. android 分隔线
  7. EditText小总结
  8. 【Android】Android 代码判断是否获取ROO
  9. Android Gradle 编译问题汇总
  10. Android SDK大连东软镜像地址及地址列表