Android AssetManager

Android AssetManager的創(chuàng)建

本文基于Android 6.0源碼分析

AssetManager的類圖

我們以一個(gè)"Hello World" APK(包名:com.jackyperf.assetmanagerdemo)為例梧疲。


  • ContextImpl:為Activity以及其他應(yīng)用組件提供基礎(chǔ)上下文,常用的Context API的實(shí)現(xiàn)
    都在這里

    • mPackageInfo:ContextImpl關(guān)聯(lián)的組件所在Package信息
    • mResourcesManager:單列對象再来,管理應(yīng)用內(nèi)部多個(gè)Resources包
  • LoadedApk:管理一個(gè)加載的apk包

    • mResources:apk包對應(yīng)的Resources對象
    • mResDir:資源存放路徑/data/app/com.jackyperf.assetmanagerdemo-1/base.apk
  • ResourcesManager

    • mActiveResources:應(yīng)用使用的Resources包的緩存
  • Resources:提供高級別的訪問應(yīng)用的資源的API

    • mSystem:系統(tǒng)Resources對象
    • mAssets:AssetManager對象
  • AssetManager:提供低級別的訪問應(yīng)用資源的API

    • sSystem:系統(tǒng)AssetMananger對象
    • mObject:指向Native層AssetManager
  • AssetManager(Native層)

    Every application that uses assets needs one instance of this. A
    single instance may be shared across multiple threads, and a single
    thread may have more than one instance
    (the latter is discouraged).

    The purpose of the AssetManager is to create Asset objects. To do
    this efficiently it may cache information about the locations of
    files it has seen.
    This can be controlled with the "cacheMode"
    argument.

    The asset hierarchy may be examined like a filesystem, using
    AssetDir objects to peruse a single directory.

    • mAssetPath:
    • mResources:代表APK中的資源表
    • mConfig:

AssetManager的創(chuàng)建流程


我們從ActivityThread的performLaunchActivity開始分析。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
    activity = mInstrumentation.newActivity(
            cl, component.getClassName(), r.intent);
    ...
    Application app = r.packageInfo.makeApplication(false, mInstrumentation);
    ...
    Context appContext = createBaseContextForActivity(r, activity, r.displayId);
    ...
}

代碼位于/android/frameworks/base/core/java/android/app/ActivityThread.java

  • 在performLaunchActivity中首先通過反射創(chuàng)建Activity對象
  • 調(diào)用LoadedApk對象的makeApplication(),獲取Activity所屬應(yīng)用的Application對象
  • 為Activity創(chuàng)建Base Context也就是ContextImpl對象蕉朵,AssetManager對象就是
    在這里創(chuàng)建的耿战。

接下來巴帮,我們分析createBaseContextForActivity()的實(shí)現(xiàn)晶密。

private Context createBaseContextForActivity(ActivityClientRecord r,
        final Activity activity, int activityDisplayId) {
    ...
    ContextImpl appContext = ContextImpl.createActivityContext(
            this, r.packageInfo, displayId, r.overrideConfig/* { Multi-Window */, r.token/* Multi-Window } */);
    appContext.setOuterContext(activity);
    ...
}

代碼位于/android/frameworks/base/core/java/android/app/ActivityThread.java

  • 調(diào)用ContextImpl的靜態(tài)方法createActivityContext()創(chuàng)建ContextImpl對象,然后將Activity
    保存到ContextImpl的mOuterContext中鱼喉。

createActivityContext()的實(shí)現(xiàn)計(jì)較簡單秀鞭,就是調(diào)用ContextImpl的構(gòu)造函數(shù),在
ContextImpl的構(gòu)造函數(shù)函數(shù)中會調(diào)用LoadedApk對象的getResouces()創(chuàng)建Resources對象扛禽,注意LoadedApk有
兩個(gè)構(gòu)造函數(shù)锋边,一個(gè)用于系統(tǒng)包,一個(gè)用于普通的應(yīng)用包
编曼。在getResources()中最終調(diào)用ResourcesManager的getTopLevelResources()
豆巨。

接下來分析ResourcesManager的getTopLevelResources()。

private final ArrayMap<ResourcesKey, WeakReference<Resources> > mActiveResources =
        new ArrayMap<>();
...        
/**
 * 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) {
    ...
    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);

        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;
         }
     }
     ...
     AssetManager assets = new AssetManager();
     if (resDir != null) {
         if (assets.addAssetPath(resDir) == 0) {
             return null;
         }
     }
     ...
     r = new Resources(assets, dm, config, compatInfo);
     ...
     synchronized (this) {
        WeakReference<Resources> wr = mActiveResources.get(key);
        Resources existing = wr != null ? wr.get() : null;
        if (existing != null && existing.getAssets().isUpToDate()) {
            r.getAssets().close();
            return existing;
        }

        mActiveResources.put(key, new WeakReference<>(r));
        return r;
     }
}

代碼位于/frameworks/base/core/java/android/app/ResourcesManager.java

  • mActiveResources是一個(gè)ArrayMap用于存放應(yīng)用內(nèi)部ResourcesKey到Resources的映射掐场,ResoucesKey實(shí)際上是
    資源路徑往扔、應(yīng)用縮放、userId等信息封裝的key熊户。
  • 首先根據(jù)應(yīng)用資源路徑萍膛、縮放、userId等信息創(chuàng)建ResourcesKey嚷堡,在mActiveResources中查找對應(yīng)的Resources對象
    如果已經(jīng)創(chuàng)建蝗罗,并且Resources內(nèi)部AssetManager的狀態(tài)與資源文件一致,直接返回。
  • 否則串塑,重建AssetManager對象沼琉,將應(yīng)用資源路徑添加AssetManager
  • 利用新的AssetManager、配置拟赊、兼容信息創(chuàng)建Resources對象
  • 最后將新創(chuàng)建的Resources對象以及ResourcesKey保存到mActiveResources刺桃,此時(shí)要考慮
    并發(fā)的問題,如果在locked之前已經(jīng)有其他線程創(chuàng)建好了Resources對象吸祟,并且狀態(tài)是最新的瑟慈,直接
    返回已有的Resources對象。

下面重點(diǎn)分析AssetManager以及Resources的構(gòu)造函數(shù)屋匕,先看AssetManager的構(gòu)造函數(shù)葛碧。

public AssetManager() {
    synchronized (this) {
        ...
        init(false);
        ensureSystemAssets();
    }
}
  • 調(diào)用native方法init()來創(chuàng)建并初始化Native層的AssetManager對象。
  • 調(diào)用ensureSystemAssets()來確保系統(tǒng)資源管理對象已經(jīng)創(chuàng)建并初始化过吻。

應(yīng)用內(nèi)部應(yīng)該使用Resources的getAssets()獲取AssetManager對象进泼。

下面分析Native層的AssetManager的創(chuàng)建以及初始化。

static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
{
    ...
    AssetManager* am = new AssetManager();
    ...
    am->addDefaultAssets();
    env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast<jlong>(am));
}

代碼位于/frameworks/base/core/jni/android_util_AssetManager.cpp

  • 在android_content_AssetManager_init()中首先創(chuàng)建Native層AssetManager對象
  • 調(diào)用AssetManager對象的addDefaultAssets添加系統(tǒng)資源
  • 最后將Native層的AssetManager對象地址保存在相應(yīng)的Java對象的mObject中

下面分析addDefaultAssets()的實(shí)現(xiàn)纤虽。

bool AssetManager::addDefaultAssets()
{
    const char* root = getenv("ANDROID_ROOT");

    String8 path(root);
    path.appendPath(kSystemAssets);

    return addAssetPath(path, NULL);
}

bool AssetManager::addAssetPath(const String8& path, int32_t* cookie)
{
    ...
    asset_path ap;
    ...
    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;
        }
    }
    ...
    mAssetPaths.add(ap);

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

    return true;
}
  • 在addDefaultAssets()中首先創(chuàng)建系統(tǒng)資源路徑乳绕,一般ANDROID_ROOT環(huán)境變量為"/system",kSystemAssets為
    "framework/framework-res.apk"逼纸,所以系統(tǒng)資源路徑為"/system/framework/framework-res.apk"洋措。然后調(diào)用addAssetPath()。
  • 在addAssetPath()中杰刽,首先檢查mAssetPaths中是否已經(jīng)包含了當(dāng)前資源路徑對應(yīng)的asset_path對象菠发,如果已經(jīng)存在,返回asset_path在
    mAssetPaths中的索引值+1,所以*cookie的值從1開始贺嫂。
  • 否則滓鸠,將asset_path添加到mAssetPaths中,同時(shí)給*cookie賦值第喳。
  • 如果資源表不為NULL糜俗,將asset_path添加到資源表。

最后我們再來分析下Resources的構(gòu)造函數(shù)曲饱。

public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config,
        CompatibilityInfo compatInfo) {
    mAssets = assets;
    ...
    updateConfiguration(config, metrics);
    assets.ensureStringBlocks();
}
  • 在Reosurces的構(gòu)造函數(shù)中吩跋,首先將之前創(chuàng)建的AssetManager對象保存到mAssets中。
  • 調(diào)用updateConfiguration()更新設(shè)備配置信息,如設(shè)備屏幕信息渔工、國家地區(qū)網(wǎng)絡(luò)信息以及鍵盤配置信息等,最終會將這些信息
    保存到Native層的AssetManager對象中去桥温。
  • 調(diào)用ensureStringBlocks將系統(tǒng)資源表以及應(yīng)用資源表中的字符串資源池地址保存到AssetManager的mStringBlocks中引矩。

參考

  1. http://blog.csdn.net/luoshengyang/article/details/8791064
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子旺韭,更是在濱河造成了極大的恐慌氛谜,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件区端,死亡現(xiàn)場離奇詭異值漫,居然都是意外死亡遭庶,警方通過查閱死者的電腦和手機(jī)烤宙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門驻子,熙熙樓的掌柜王于貴愁眉苦臉地迎上來杉女,“玉大人呐粘,你說我怎么就攤上這事酌心【脸幔” “怎么了悍及?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵唐全,是天一觀的道長埃跷。 經(jīng)常有香客問我,道長邮利,這世上最難降的妖魔是什么弥雹? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮延届,結(jié)果婚禮上剪勿,老公的妹妹穿的比我還像新娘。我一直安慰自己祷愉,他們只是感情好窗宦,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著二鳄,像睡著了一般赴涵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上订讼,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天髓窜,我揣著相機(jī)與錄音,去河邊找鬼欺殿。 笑死寄纵,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的脖苏。 我是一名探鬼主播程拭,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼棍潘!你這毒婦竟也來了恃鞋?” 一聲冷哼從身側(cè)響起崖媚,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎恤浪,沒想到半個(gè)月后畅哑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡水由,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年荠呐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片砂客。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡泥张,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鞭盟,到底是詐尸還是另有隱情圾结,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布齿诉,位于F島的核電站筝野,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏粤剧。R本人自食惡果不足惜歇竟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望抵恋。 院中可真熱鬧焕议,春花似錦、人聲如沸弧关。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽世囊。三九已至别瞭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間株憾,已是汗流浹背蝙寨。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嗤瞎,地道東北人墙歪。 一個(gè)月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像贝奇,于是被迫代替她去往敵國和親虹菲。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,504評論 25 707
  • Android與資源管理相關(guān)的類Resouces和AssetManager很有必要清楚他們的創(chuàng)建過程掉瞳。 與資源查找...
    小爨閱讀 3,153評論 4 14
  • Android提供了一種非常靈活的資源系統(tǒng)届惋,可以根據(jù)不同的條件提供可替代資源髓帽。因此,系統(tǒng)基于很少的改造就能支持新特...
    Gooooood閱讀 12,662評論 5 25
  • 7月5日衡查,湖州師范學(xué)院文學(xué)院“遺韻焯爍熠于今朝”湖州市非物質(zhì)遺產(chǎn)現(xiàn)狀調(diào)研暑期社會實(shí)踐團(tuán)的工作已經(jīng)進(jìn)行到第五天了瘩欺,今...
    桃之夭夭灼灼其華閱讀 279評論 0 0
  • 19日18點(diǎn)下班,我們一行人乘地鐵到浦東機(jī)場拌牲,正式開始了普吉島之旅俱饿。 飛機(jī)23時(shí)左右出發(fā),經(jīng)過5個(gè)小時(shí)的飛行塌忽,于泰...
    言吾小姐閱讀 199評論 0 0