Android資源加載過(guò)程簡(jiǎn)要分析

1性芬、相關(guān)類圖
QQ截圖20160818093629.png

這里主要說(shuō)明一下换团,為什么Resources和AssetManager都有一個(gè)mSystem屬性和getSystem()方法,這是因?yàn)槲覀兊膽?yīng)用不但要使用我們自己的資源,還要使用系統(tǒng)資源柠辞,也就是framework-res.apk中的資源尘颓,所以這里的mSystem是用來(lái)獲取系統(tǒng)資源的走触。

2、初始化過(guò)程

在Activity啟動(dòng)過(guò)程中了解到疤苹,當(dāng)zogyte進(jìn)程fork一個(gè)應(yīng)用進(jìn)程用于運(yùn)行子進(jìn)程時(shí)互广,會(huì)跳轉(zhuǎn)到ActivityThread.main()中進(jìn)行一系列操作,這其中有一步就是創(chuàng)建應(yīng)用程序運(yùn)行的上下文環(huán)境ContextImpl:
ActivityThread.ApplicationThread.handleBindApplication()

 private void handleBindApplication(AppBindData data) {
      //..........
      // Context初始化(ContextImpl)
      final ContextImpl appContext = ContextImpl.createAppContext(this/*ActivityThread*/, data.info/*LoadedApk*/);
      //........
  }
      /**
    *ActivityThread mainThread = mainThread
    *LoadedApk packageInfo = packageInfo
    *boolean restricted = false
    *int createDisplayWithId = Display.INVALID_DISPLAY
    */
    private ContextImpl(ContextImpl container, ActivityThread mainThread,
            LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
            Display display, Configuration overrideConfiguration, int createDisplayWithId) {
            //.......
            // LoadedApk賦值 
            mPackageInfo = packageInfo;
            mResourcesManager = ResourcesManager.getInstance();
            // resources初始化:通過(guò)LoadedApk.getResources來(lái)創(chuàng)建一個(gè)Resources實(shí)例
            Resources resources = packageInfo.getResources(mainThread);
            mResources = resources;// 賦值
            //......
            mContentResolver = new ApplicationContentResolver(this, mainThread, user);
    }

LoadedApk.getResources()

    public Resources getResources(ActivityThread mainThread) {
        if (mResources == null) {
            // ActivityThread.getTopLevelResources()
            mResources = mainThread.getTopLevelResources(mResDir/*APK文件位置*/, mSplitResDirs, mOverlayDirs,
                    mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, this);
        }
        return mResources;
    }

ActivityThread.getTopLevelResources()

    /**
     * Creates the top level resources for the given package.
     */
    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());
    }

ResourcesManager.getTopLevelResources

/**
     * Creates the top level Resources for applications with the given compatibility info.
     *
     * @param resDir the resource directory.
     * @param splitResDirs split resource directories.
     * @param overlayDirs the resource overlay directories.
     * @param libDirs the shared library resource dirs this app references.
     * @param displayId display Id.
     * @param overrideConfiguration override configurations.
     * @param compatInfo the compatibility info. Must not be null.
     */
    Resources getTopLevelResources(String resDir, String[] splitResDirs,
            String[] overlayDirs, String[] libDirs, int displayId,
            Configuration overrideConfiguration, CompatibilityInfo compatInfo) {
        final float scale = compatInfo.applicationScale;
        Configuration overrideConfigCopy = (overrideConfiguration != null)
                ? new Configuration(overrideConfiguration) : null;
        ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfigCopy, scale);
        Resources r;
        synchronized (this) {
            // Resources is app scale dependent.
            if (DEBUG) Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale);
            // Resources是以ResourcesKey為key以弱應(yīng)用的方式保存在mActiveResources這個(gè)Map中
            WeakReference<Resources> wr = mActiveResources.get(key);
            r = wr != null ? wr.get() : null;
            //if (r != null) Log.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate());
            if (r != null && r.getAssets().isUpToDate()) {/
                // 緩存里面有卧土,并且是最新的
                if (DEBUG) Slog.w(TAG, "Returning cached resources " + r + " " + resDir
                        + ": appScale=" + r.getCompatibilityInfo().applicationScale
                        + " key=" + key + " overrideConfig=" + overrideConfiguration);
                return r;
            }
        }

        //if (r != null) {
        //    Log.w(TAG, "Throwing away out-of-date resources!!!! "
        //            + r + " " + resDir);
        //}
        // AssetManager創(chuàng)建
        AssetManager assets = new AssetManager();
        // resDir can be null if the 'android' package is creating a new Resources object.
        // This is fine, since each AssetManager automatically loads the 'android' package
        // already.
        if (resDir != null) {
            if (assets.addAssetPath(resDir) == 0) {
                return null;
            }
        }

        if (splitResDirs != null) {
            for (String splitResDir : splitResDirs) {
                if (assets.addAssetPath(splitResDir) == 0) {
                    return null;
                }
            }
        }

        if (overlayDirs != null) {
            for (String idmapPath : overlayDirs) {
                assets.addOverlayPath(idmapPath);
            }
        }

        if (libDirs != null) {
            for (String libDir : libDirs) {
                if (libDir.endsWith(".apk")) {
                    // Avoid opening files we know do not have resources,
                    // like code-only .jar files.
                    if (assets.addAssetPath(libDir) == 0) {
                        Log.w(TAG, "Asset path '" + libDir +
                                "' does not exist or contains no resources.");
                    }
                }
            }
        }

        //Log.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
        DisplayMetrics dm = getDisplayMetricsLocked(displayId);
        Configuration config;
        final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
        final boolean hasOverrideConfig = key.hasOverrideConfiguration();
        if (!isDefaultDisplay || hasOverrideConfig) {
            config = new Configuration(getConfiguration());
            if (!isDefaultDisplay) {
                applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config);
            }
            if (hasOverrideConfig) {
                config.updateFrom(key.mOverrideConfiguration);
                if (DEBUG) Slog.v(TAG, "Applied overrideConfig=" + key.mOverrideConfiguration);
            }
        } else {
            config = getConfiguration();
        }
        // 創(chuàng)建Resources
        r = new Resources(assets, dm, config, compatInfo);
        if (DEBUG) Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
                + r.getConfiguration() + " appScale=" + r.getCompatibilityInfo().applicationScale);

        synchronized (this) {
            // 可能其他線程已經(jīng)創(chuàng)建好了惫皱,則直接返回
            WeakReference<Resources> wr = mActiveResources.get(key);
            Resources existing = wr != null ? wr.get() : null;
            if (existing != null && existing.getAssets().isUpToDate()) {
                // Someone else already created the resources while we were
                // unlocked; go ahead and use theirs.
                r.getAssets().close();
                return existing;
            }

            // XXX need to remove entries when weak references go away
            // 把最新的對(duì)象保存到緩存中
            mActiveResources.put(key, new WeakReference<>(r));
            if (DEBUG) Slog.v(TAG, "mActiveResources.size()=" + mActiveResources.size());
            return r;
        }
    }

過(guò)程是:先從緩存中取,如果緩存中有且沒(méi)有過(guò)時(shí)夸溶,則直接返回逸吵,否則依次創(chuàng)建AssetManager 和Resources
AssetManager構(gòu)造函數(shù)

/**
     * Create a new AssetManager containing only the basic system assets.
     * Applications will not generally use this method, instead retrieving the
     * appropriate asset manager with {@link Resources#getAssets}.    Not for
     * use by applications.
     * {@hide}
     */
    public AssetManager() {
        synchronized (this) {
            //......
            init(false);
            // 確保有能夠訪問(wèn)系統(tǒng)資源的AssetManager對(duì)象
            ensureSystemAssets();
        }
    }

android_util_AssetManager.android_content_AssetManager_init()

static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
{
    if (isSystem) {// false
        verifySystemIdmaps();
    }
    AssetManager* am = new AssetManager();
    if (am == NULL) {
        jniThrowException(env, "java/lang/OutOfMemoryError", "");
        return;
    }

    am->addDefaultAssets();

    ALOGV("Created AssetManager %p for Java object %p\n", am, clazz);
    env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast<jlong>(am));
}

AssetManager.cpp:addDefaultAssets()

bool AssetManager::addDefaultAssets()
{
    // root = /system/
    const char* root = getenv("ANDROID_ROOT");
    LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_ROOT not set");

    String8 path(root);
    // path = /system/framework/framework-res.apk
    path.appendPath(kSystemAssets);

    return addAssetPath(path, NULL);
}

bool AssetManager::addAssetPath(const String8& path, int32_t* cookie)
{
    AutoMutex _l(mLock);

    asset_path ap;

    String8 realPath(path);
    if (kAppZipName) {
        // 如果kAppZipName不為NULL(classes.jar),這里這個(gè)值是為NULL的
        realPath.appendPath(kAppZipName);
    }
    ap.type = ::getFileType(realPath.string());
    if (ap.type == kFileTypeRegular) {// kAppZipName不為NULL
        ap.path = realPath;
    } else {
    // kAppZipName為NULL
        ap.path = path;//ap.path指向APK文件
        ap.type = ::getFileType(path.string());
        if (ap.type != kFileTypeDirectory && ap.type != kFileTypeRegular) {
            ALOGW("Asset path %s is neither a directory nor file (type=%d).",
                 path.string(), (int)ap.type);
            return false;
        }
    }

    // Skip if we have it already.
    for (size_t i=0; i<mAssetPaths.size(); i++) {
        if (mAssetPaths[i].path == ap.path) {
            if (cookie) {
                *cookie = static_cast<int32_t>(i+1);
            }
            return true;
        }
    }

    ALOGV("In %p Asset %s path: %s", this,
         ap.type == kFileTypeDirectory ? "dir" : "zip", ap.path.string());

    // Check that the path has an AndroidManifest.xml
    Asset* manifestAsset = const_cast<AssetManager*>(this)->openNonAssetInPathLocked(
            kAndroidManifest, Asset::ACCESS_BUFFER, ap);
    if (manifestAsset == NULL) {
        // This asset path does not contain any resources.
        delete manifestAsset;
        return false;
    }
    delete manifestAsset;

    mAssetPaths.add(ap);

    // new paths are always added at the end
    if (cookie) {
        *cookie = static_cast<int32_t>(mAssetPaths.size());
    }

#ifdef __ANDROID__
    // Load overlays, if any
    asset_path oap;
    for (size_t idx = 0; mZipSet.getOverlay(ap.path, idx, &oap); idx++) {
        mAssetPaths.add(oap);
    }
#endif

    if (mResources != NULL) {
        appendPathToResTable(ap);
    }

    return true;
}

以上,是AssetManager.java構(gòu)造函數(shù)的第一步:init(false)缝裁,其主要工作是加載系統(tǒng)資源(framework-res.apk),以供后續(xù)應(yīng)用程序使用扫皱。而其第二部:ensureSystemAssets()也是為了創(chuàng)建系統(tǒng)資源使用對(duì)象AssetManager

private static void ensureSystemAssets() {
        synchronized (sSync) {
            if (sSystem == null) {
                AssetManager system = new AssetManager(true);
                system.makeStringBlocks(null);
                sSystem = system;
            }
        }
    }

初始化了AssetManager之后,就可以通過(guò)AssetManager.addAssetPath()加載本身資源了.
在AssetManager加載完相關(guān)資源后捷绑,就可以創(chuàng)建Resources了:

   /**
     * Creates a new Resources object with CompatibilityInfo.
     *
     * @param assets Previously created AssetManager.
     * @param metrics Current display metrics to consider when
     *                selecting/computing resource values.
     *              設(shè)備分辨率相關(guān)信息:屏幕分辨率韩脑,density,font scaling
     * @param config Desired device configuration to consider when
     *               selecting/computing resource values (optional).
     *              該配置信息用來(lái)決定使用哪套資源
     * @param compatInfo this resource's compatibility info. Must not be null.
     *              資源兼容性信息
     * @hide
     */
    public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config,
            CompatibilityInfo compatInfo) {
        mAssets = assets;
        mMetrics.setToDefaults();
        if (compatInfo != null) {
            mCompatibilityInfo = compatInfo;
        }
        // 設(shè)備相關(guān)配置信息更新處理
        updateConfiguration(config, metrics);
        // 創(chuàng)建字符串資源池
        assets.ensureStringBlocks();
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市粹污,隨后出現(xiàn)的幾起案子段多,更是在濱河造成了極大的恐慌,老刑警劉巖壮吩,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件进苍,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡鸭叙,警方通過(guò)查閱死者的電腦和手機(jī)觉啊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)沈贝,“玉大人杠人,你說(shuō)我怎么就攤上這事。” “怎么了嗡善?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵辑莫,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我罩引,道長(zhǎng)各吨,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任蜒程,我火速辦了婚禮绅你,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘昭躺。我一直安慰自己忌锯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布领炫。 她就那樣靜靜地躺著偶垮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪帝洪。 梳的紋絲不亂的頭發(fā)上似舵,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音葱峡,去河邊找鬼砚哗。 笑死,一個(gè)胖子當(dāng)著我的面吹牛砰奕,可吹牛的內(nèi)容都是我干的蛛芥。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼军援,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼仅淑!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起胸哥,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤涯竟,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后空厌,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體庐船,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年嘲更,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了醉鳖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡哮内,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情北发,我是刑警寧澤纹因,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站琳拨,受9級(jí)特大地震影響瞭恰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜狱庇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一惊畏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧密任,春花似錦颜启、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至淹遵,卻和暖如春口猜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背透揣。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工济炎, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人辐真。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓须尚,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親拆祈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子恨闪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容