Android中資源管理機制詳細分析 - 新聞資訊 - 雲南小程序開發|雲南軟件開發|雲南網站建設-昆明融晨信息技術有限公司

159-8711-8523

雲南網建設/小程序開發/軟件開發

知識

不(bù)管是(shì)網站,軟件還是(shì)小程序,都要(yào / yāo)直接或間接能爲(wéi / wèi)您産生價值,我們在(zài)追求其視覺表現的(de)同時(shí),更側重于(yú)功能的(de)便捷,營銷的(de)便利,運營的(de)高效,讓網站成爲(wéi / wèi)營銷工具,讓軟件能切實提升企業内部管理水平和(hé / huò)效率。優秀的(de)程序爲(wéi / wèi)後期升級提供便捷的(de)支持!

您當前位置>首頁 » 新聞資訊 » 技術分享 >

Android中資源管理機制詳細分析

發表時(shí)間:2020-10-19

發布人(rén):融晨科技

浏覽次數:69


尊敬原創:http://blog.csdn.net/yuanzeyao/article/details/42386549
在(zài)Android中,所有的(de)資本都在(zài)res目次下存放,包含drawable,layout,strings,anim等等,當我們向工程中參加任何一個(gè)資本時(shí),會在(zài)R類中響應會爲(wéi / wèi)該 資本分派一個(gè)id,我們在(zài)應用中就(jiù)是(shì)經由過程這(zhè)個(gè)id來(lái)拜訪資本的(de),信賴做過Andorid開辟的(de)同夥對于(yú)這(zhè)些肯定不(bù)會陌生,所以(yǐ)這(zhè)個(gè)也(yě)不(bù)是(shì)我今天想要(yào / yāo)說(shuō)的(de),我今天想和(hé / huò)大(dà)年夜家一路進修的(de)是(shì)Android是(shì)若何治理資本的(de),在(zài)Android體系中,資本大(dà)年夜部分都是(shì)經由過程xml文件定義的(de)(drawable是(shì)圖片),如layout,string,anim都是(shì)xml文件,而(ér)對于(yú)layout,anim和(hé / huò)strings等xml文件僅僅是(shì)解析xml文件,攫取指定的(de)值罷了(le/liǎo),然則對于(yú)layout文件中控件的(de)解析就(jiù)比較複雜了(le/liǎo),例如對于(yú)一個(gè)Button,須要(yào / yāo)解析它所有的(de)屬性值,這(zhè)個(gè)是(shì)若何實現的(de)呢。
這(zhè)裏我們起重要(yào / yāo)推敲一個(gè)問題,就(jiù)是(shì)一個(gè)控件有哪些屬性是(shì)若何定義的(de)?比如TextView具有哪些屬性?爲(wéi / wèi)什愦我設置TextView的(de)樣式隻能用style而(ér)不(bù)克不(bù)及用android:theme?這(zhè)些信息都是(shì)在(zài)哪裏定義的(de),想要(yào / yāo)弄清跋扈這(zhè)個(gè)問題,就(jiù)必須大(dà)年夜源碼工程招謎底,我應用的(de)是(shì)android4.1工程,如不(bù)雅你應用的(de)是(shì)其他(tā)版本的(de),那麽可能用些進出(chū)。
先看三個(gè)文件
1、d:\android4.1\frameworks\base\core\res\res\values\attrs.xml
看到(dào)attrs.xml文件,不(bù)知道(dào)你有沒有想起什麽?當我們在(zài)自定義控件的(de)時(shí)刻,是(shì)不(bù)是(shì)會創建一個(gè)attrs.xml文件?應用attrs.xml文件的(de)目标其實就(jiù)是(shì)給我們自定義的(de)控件添加屬性,打開這(zhè)個(gè)目次後,你會看到(dào)定義了(le/liǎo)一個(gè)叫"Theme"的(de)styleable,如下(我隻朝長進步部分)
<declare-styleable name="Theme">
        <!-- ============== -->
        <!-- Generic styles -->
        <!-- ============== -->
        <eat-comment />

        <!-- Default color of foreground imagery. -->
        <attr name="colorForeground" format="color" />
        <!-- Default color of foreground imagery on an inverted background. -->
        <attr name="colorForegroundInverse" format="color" />
        <!-- Color that matches (as closely as possible) the window background. -->
        <attr name="colorBackground" format="color" />

在(zài)這(zhè)個(gè)文件中,定義了(le/liǎo)Android中大(dà)年夜部分可以(yǐ)應用的(de)屬性,這(zhè)裏我說(shuō)的(de)是(shì)“定義”而(ér)不(bù)是(shì)“聲明”,同名在(zài)語法膳绫擎最大(dà)年夜的(de)差别就(jiù)是(shì)定義要(yào / yāo)有format屬性,而(ér)聲明沒有format屬性。
2、d:\android4.1\frameworks\base\core\res\res\values\attrs_manifest.xml
這(zhè)個(gè)文件的(de)名字和(hé / huò)膳绫擎的(de)文件的(de)名字很像,就(jiù)是(shì)多了(le/liǎo)一個(gè)manifest,故名思議就(jiù)是(shì)定義了(le/liǎo)AndroidManifest.xml文件中的(de)屬性,這(zhè)琅绫擎有一個(gè)很重要(yào / yāo)的(de)一句話
<attr name="theme" format="reference" />

定義了(le/liǎo)一個(gè)theme屬性,這(zhè)個(gè)就(jiù)是(shì)我們日常平凡在(zài)Activity膳绫擎應用的(de)theme屬性
3、d:\android4.1\frameworks\base\core\res\res\values\themes.xml
這(zhè)個(gè)文件開端定義了(le/liǎo)一個(gè)叫做"Theme" 的(de)sytle,如下(截圖部分)
<style name="Theme">

        <item name="colorForeground">@android:color/bright_foreground_dark</item>
        <item name="colorForegroundInverse">@android:color/bright_foreground_dark_inverse</item>
        <item name="colorBackground">@android:color/background_dark</item>
        <item name="colorBackgroundCacheHint">?android:attr/colorBackground</item>

這(zhè)個(gè)就(jiù)是(shì)我們日常平凡在(zài)Application或者Activity中應用的(de)Theme,大(dà)年夜這(zhè)裏可以(yǐ)看出(chū),Theme也(yě)是(shì)一種style,那爲(wéi / wèi)什麽style隻能永遠View/ViewGorup,而(ér)Theme隻能用于(yú)Activity或者Application呢?先記住此問題,我們後續會爲(wéi / wèi)你解答
我們再來(lái)整合這(zhè)三個(gè)文件的(de)内容吧,起首在(zài)attrs.xml文件中,定義了(le/liǎo)Android中大(dà)年夜部分的(de)屬性,也(yě)就(jiù)是(shì)說(shuō)今後所有View/Activity中大(dà)年夜部分的(de)屬性就(jiù)是(shì)在(zài)這(zhè)裏定義的(de),然後在(zài)attrs_manifest.xml中定義了(le/liǎo)一個(gè)叫做theme的(de)屬性,它的(de)值就(jiù)是(shì)再themes文件中定義的(de)Theme或者持續自“Theme”的(de)style。
有了(le/liǎo)膳绫擎的(de)常識後,我們再來(lái)分析膳绫擎說(shuō)過的(de)兩個(gè)問題:
1、TextView控件(其他(tā)控件也(yě)一樣)的(de)屬性在(zài)哪裏定義的(de)。
2、既然Theme也(yě)是(shì)style,那爲(wéi / wèi)什麽View隻能用style,Activity隻能應用theme?
所有View的(de)屬性定義都是(shì)在(zài)attrs.xml文件中的(de),所以(yǐ)我們到(dào)attrs.xml文件中尋找TextView的(de)styleable吧
 <declare-styleable name="TextView">
        <!-- Determines the minimum type that getText() will return.
             The default is "normal".
             Note that EditText and LogTextBox always return Editable,
             even if you specify something less powerful here. -->
        <attr name="bufferType">
            <!-- Can return any CharSequence, possibly a
             Spanned one if the source text was Spanned. -->
            <enum name="normal" value=http://www.sjsjw.com/100/000252MYM024469/"0" />
            
            
            
            
        
        
        
        
        
        
        

膳绫擎的(de)屬性我隻朝長進步了(le/liǎo)部分,請留意,這(zhè)裏所有的(de)屬性都是(shì)進行“聲明”,你去搜刮這(zhè)個(gè)styleable,會發明在(zài)TextView的(de)styleable中不(bù)會找到(dào)theme這(zhè)個(gè)屬性的(de)聲明,所以(yǐ)你給任何一個(gè)view設置theme屬性是(shì)沒有效不(bù)雅的(de)。請看下面一段代碼就(jiù)知道(dào)爲(wéi / wèi)什麽了(le/liǎo)。
定義一個(gè)attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyTextView">
        <attr name="orientation">
            <enum name="horizontal" value=http://www.sjsjw.com/100/000252MYM024469/"0" />
        	
        
    
定義一個(gè)MyTextView
public class MyTextView extends TextView {
  private static final String TAG = "MyTextView";
  public MyTextView(Context context) 
  {
    super(context);
  }
  public MyTextView(Context context, AttributeSet attrs) 
  {
    super(context, attrs);
    //應用TypeArray攫取自定義的(de)屬性
    TypedArray ta=context.obtainStyledAttributes(attrs, R.styleable.MyTextView);
    String value=http://www.sjsjw.com/100/000252MYM024469/ta.getString(R.styleable.MyTextView_orientation);
    Log.d("yzy", "value1--->"+value);
    ta.recycle();
  }
}

在(zài)attrs.xml我爲(wéi / wèi)MyTextView定義了(le/liǎo)一個(gè)orientation屬性,然後再MyTextView的(de)構造函數中去攫取這(zhè)個(gè)屬性,這(zhè)裏就(jiù)涉及到(dào)TypeArray這(zhè)個(gè)類,我們發明獲得TypeArray須要(yào / yāo)傳入R.style.MyTextView這(zhè)個(gè)值,這(zhè)個(gè)就(jiù)是(shì)體系爲(wéi / wèi)我們拜訪MyTextView這(zhè)個(gè)styleable供給的(de)一個(gè)id,當我們須要(yào / yāo)拿到(dào)orientation這(zhè)個(gè)屬性的(de)值瓯,我們經由過程R.style.MyTextView_orientation拿到(dào),因爲(wéi / wèi)MyTextView中沒有定義或者聲明theme屬性,所以(yǐ)我們找不(bù)到(dào)R.styleable.MyTextView_theme這(zhè)個(gè)id,所以(yǐ)導緻我們無法解析它的(de)theme屬性。同樣回到(dào)TextView這(zhè)個(gè)styleable來(lái),因爲(wéi / wèi)TextView的(de)styleable中沒有定義theme屬性,所以(yǐ)theme對于(yú)TextView是(shì)沒有效的(de)。所以(yǐ)即使你在(zài)TextView琅绫擎參加theme屬性,即使編譯器不(bù)會給你報錯,這(zhè)個(gè)theme也(yě)是(shì)被忽視了(le/liǎo)的(de)。
我們再來(lái)看看Activity的(de)屬性是(shì)若何定義的(de),因爲(wéi / wèi)Activity是(shì)在(zài)AndroidManigest.xml文件中定義的(de),所以(yǐ)我們到(dào)attrs_manifest.xml中查找。
    <declare-styleable name="AndroidManifestActivity" parent="AndroidManifestApplication">
        <!-- Required name of the class implementing the activity, deriving from
            {@link android.app.Activity}.  This is a fully
            qualified class name (for example, com.mycompany.myapp.MyActivity); as a
            short-hand if the first character of the class
            is a period then it is appended to your package name. -->
        <attr name="name" />
        <attr name="theme" />
        <attr name="label" />
        <attr name="description" />
        <attr name="icon" />
        <attr name="logo" />
        <attr name="launchMode" />
        <attr name="screenOrientation" />
        <attr name="configChanges" />
        <attr name="permission" />
        <attr name="multiprocess" />
        <attr name="process" />
        <attr name="taskAffinity" />
        <attr name="allowTaskReparenting" />
        <attr name="finishOnTaskLaunch" />
        <attr name="finishOnCloseSystemDialogs" />
        <attr name="clearTaskOnLaunch" />
        <attr name="noHistory" />
        <attr name="alwaysRetainTaskState" />
        <attr name="stateNotNeeded" />
        <attr name="excludeFromRecents" />
        <!-- Specify whether the activity is enabled or not (that is, can be instantiated by the system).
             It can also be specified for an application as a whole, in which case a value of "false"
             will override any component specific values (a value of "true" will not override the
             component specific values). -->
        <attr name="enabled" />
        <attr name="exported" />
        <!-- Specify the default soft-input mode for the main window of
             this activity.  A value besides "unspecified" here overrides
             any value in the theme. -->
        <attr name="windowSoftInputMode" />
        <attr name="immersive" />
        <attr name="hardwareAccelerated" />
        <attr name="uiOptions" />
        <attr name="parentActivityName" />
    </declare-styleable>

很明顯,Activity對于(yú)的(de)styleable中是(shì)聲清楚明了(le/liǎo)theme的(de),所以(yǐ)它可以(yǐ)解析theme屬性。
膳绫擎兩個(gè)問題都已經解答完了(le/liǎo),下面來(lái)評論辯論另一個(gè)話題,就(jiù)是(shì)Resources的(de)獲取過程。
在(zài)我的(de)别的(de)一篇文┞仿曾經評論辯論過這(zhè)個(gè)話題更深層次懂得Context 這(zhè)裏我們再來(lái)進修一下Resources的(de)獲取過程。
在(zài)Android體系中,獲取Resources重要(yào / yāo)有兩種辦法,經由過程Context獲取和(hé / huò)PackageManager獲取
起首,我們看看我們經由過程Context獲取,下面這(zhè)張圖是(shì)Context相幹類的(de)類圖
[img]http://img.blog.csdn.net/20150104103842705?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveXVhbnpleWFv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
大(dà)年夜圖中可以(yǐ)看出(chū),Context有兩個(gè)子(zǐ)類,一個(gè)是(shì)ContextWrapper,另一個(gè)是(shì)ContextImpl,而(ér)ContextWrapper依附于(yú)ContextImpl。結合源碼,我們會發明,Context是(shì)一個(gè)抽象類,它的(de)┞鋒正實現類就(jiù)是(shì)ContextImpl,而(ér)ContextWrapper就(jiù)像他(tā)的(de)名字一樣,僅僅是(shì)對Context的(de)一層包裝,它的(de)功能都是(shì)經由過程調用屬性mBase完成,該mBase本質就(jiù)是(shì)指向一個(gè)ContextImpl類型的(de)變量。我們獲取Resources時(shí)就(jiù)是(shì)調用Context的(de)getResources辦法,那麽我們直接看看ContextImpl的(de)getResources辦法吧
 @Override
    public Resources getResources() {
        return mResources;
    }

我們發明這(zhè)個(gè)辦法很簡單,就(jiù)是(shì)返回mResources屬性,那麽這(zhè)個(gè)屬性是(shì)在(zài)哪裏 賦值的(de)呢,經由過程尋找發明,其實就(jiù)是(shì)在(zài)創建ContextImpl,經由過程調用Init進行賦值的(de)(具體邏輯參照《更深層次懂得Context》).這(zhè)裏我先給出(chū)getResource辦法的(de)時(shí)序圖,然後跟蹤源碼。
[img]http://img.blog.csdn.net/20150104110812840?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveXVhbnpleWFv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
先大(dà)年夜init辦法開端吧
final void init(LoadedApk packageInfo,
                IBinder activityToken, ActivityThread mainThread,
                Resources container, String basePackageName) {
        mPackageInfo = packageInfo;
        mBasePackageName = basePackageName != null ? basePackageName : packageInfo.mPackageName;
        mResources = mPackageInfo.getResources(mainThread);

        if (mResources != null && container != null
                && container.getCompatibilityInfo().applicationScale !=
                        mResources.getCompatibilityInfo().applicationScale) {
            if (DEBUG) {
                Log.d(TAG, "loaded context has different scaling. Using container's" +
                        " compatiblity info:" + container.getDisplayMetrics());
            }
            mResources = mainThread.getTopLevelResources(
                    mPackageInfo.getResDir(), container.getCompatibilityInfo());
        }
        mMainThread = mainThread;
        mContentResolver = new ApplicationContentResolver(this, mainThread);

        setActivityToken(activityToken);
    }

我們發明,對mResource進行賦值,是(shì)經由過程調用LoadedApk中的(de)getResource進行的(de),傳入了(le/liǎo)ActivityThead類型的(de)參數
  public Resources getResources(ActivityThread mainThread) {
        if (mResources == null) {
            mResources = mainThread.getTopLevelResources(mResDir, this);
        }
        return mResources;
    }

在(zài)getResources辦法中,其實就(jiù)是(shì)調用了(le/liǎo)ActivityThrad的(de)getTopLevelResources辦法,個(gè)中mResDir就(jiù)是(shì)apk文件的(de)路徑(對于(yú)用戶安裝的(de)app,此路徑就(jiù)在(zài)/data/app下面的(de)某一個(gè)apk),大(dà)年夜時(shí)序圖中可以(yǐ)知道(dào),getTopLevelResources其實就(jiù)是(shì)調用了(le/liǎo)一個(gè)同名辦法,我們直接看它的(de)同名辦法吧
Resources getTopLevelResources(String resDir, CompatibilityInfo compInfo) {
        ResourcesKey key = new ResourcesKey(resDir, compInfo.applicationScale);
        Resources r;
        synchronized (mPackages) {
            // Resources is app scale dependent.
            if (false) {
                Slog.w(TAG, "getTopLevelResources: " + resDir + " / "
                        + compInfo.applicationScale);
            }
            WeakReference<Resources> wr = mActiveResources.get(key);
            r = wr != null ? wr.get() : null;
            //if (r != null) Slog.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate());
            if (r != null && r.getAssets().isUpToDate()) {
                if (false) {
                    Slog.w(TAG, "Returning cached resources " + r + " " + resDir
                            + ": appScale=" + r.getCompatibilityInfo().applicationScale);
                }
                return r;
            }
        };


        //if (r != null) {
        //    Slog.w(TAG, "Throwing away out-of-date resources!!!! "
        //            + r + " " + resDir);
        //}

        AssetManager assets = new AssetManager();
        if (assets.addAssetPath(resDir) == 0) {
            return null;
        }

        //Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
        DisplayMetrics metrics = getDisplayMetricsLocked(null, false);
        r = new Resources(assets, metrics, getConfiguration(), compInfo);
        if (false) {
            Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
                    + r.getConfiguration() + " appScale="
                    + r.getCompatibilityInfo().applicationScale);
        }
        
        synchronized (mPackages) {
            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
            mActiveResources.put(key, new WeakReference<Resources>(r));
            return r;
        }
    }

這(zhè)段代碼的(de)邏輯不(bù)複雜,起首大(dà)年夜mActiveResouuces中經由過程key拿到(dào)資本,如不(bù)雅資本不(bù)爲(wéi / wèi)null,并且是(shì)最新的(de),那麽直接返回,不(bù)然創建一個(gè)AssetManager對象,并調用AssetManager的(de)addAssetPath辦法,然後應用創建的(de)AssetManager爲(wéi / wèi)參數,創建一個(gè)Resources對象,保存并返回。經由過程膳绫擎的(de)時(shí)序圖,我們發明在(zài)創建AssetManager的(de)時(shí)刻,在(zài)其構造函數中調用init辦法,我們看看init辦法做了(le/liǎo)什麽吧
private native final void init();

居然是(shì)一個(gè)本處所法,那麽我們隻有看看對應的(de)Jni代碼了(le/liǎo)
static void android_content_AssetManager_init(JNIEnv* env, jobject clazz)
{
    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->SetIntField(clazz, gAssetManagerOffsets.mObject, (jint)am);
}

這(zhè)個(gè)琅绫擎調用了(le/liǎo)本地(dì / de)的(de)AssetManager的(de)addDefaultAssets辦法
bool AssetManager::addDefaultAssets()
{
    const char* root = getenv("ANDROID_ROOT");
    LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_ROOT not set");

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

    return addAssetPath(path, NULL);
}

這(zhè)例的(de)ANDROID_ROOT保存的(de)就(jiù)是(shì)/system路徑,而(ér)kSystemAssets是(shì) 
static const char* kSystemAssets = "framework/framework-res.apk";

還記得framework-res.apk是(shì)什麽嗎,就(jiù)是(shì)體系所有的(de)資本文件。
到(dào)這(zhè)裏終于(yú)明白了(le/liǎo),道(dào)理就(jiù)是(shì)将體系的(de)資本加載進來(lái)。
接下來(lái)看看addAssetPath辦法吧,進入源碼後,你會發明它也(yě)是(shì)一個(gè)本處所法,也(yě)須要(yào / yāo)看jni代碼
static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz,
                                                       jstring path)
{
    ScopedUtfChars path8(env, path);
    if (path8.c_str() == NULL) {
        return 0;
    }

    AssetManager* am = assetManagerForJavaObject(env, clazz);
    if (am == NULL) {
        return 0;
    }

    void* cookie;
    bool res = am->addAssetPath(String8(path8.c_str()), &cookie);

    return (res) ? (jint)cookie : 0;
}

這(zhè)裏調用了(le/liǎo)本地(dì / de)AssetManager辦法的(de)addAssetPath辦法。和(hé / huò)體系資本一樣,都被加載進來(lái)了(le/liǎo)。
下面看看PackageManager獲取Resource的(de)流程吧
在(zài)PackageManager琅绫擎獲取資本調用的(de)是(shì)getResourcesForApplication辦法,getResourcesForApplication也(yě)有一個(gè)同名辦法,我們看辦正事的(de)那個(gè)吧,
    @Override public Resources getResourcesForApplication(
        ApplicationInfo app) throws NameNotFoundException {
        if (app.packageName.equals("system")) {
            return mContext.mMainThread.getSystemContext().getResources();
        }
        Resources r = mContext.mMainThread.getTopLevelResources(
            app.uid == Process.myUid() ? app.sourceDir
            : app.publicSourceDir, mContext.mPackageInfo);
        if (r != null) {
            return r;
        }
        throw new NameNotFoundException("Unable to open " + app.publicSourceDir);
    }
起首斷定包名是(shì)否是(shì)system,如不(bù)雅不(bù)是(shì)那麽直接調用ActivityThread的(de)getTopLevelResources辦法。不(bù)過這(zhè)裏會根據當前應用的(de)應用的(de)uid和(hé / huò)過程Id相等,如不(bù)雅相等則傳入app.sourceDir,不(bù)然傳入publicSourceDir,然則根據經驗時(shí)代sourceDir和(hé / huò)publicSource一般情況下是(shì)雷同的(de)。後面的(de)邏輯和(hé / huò)Context中的(de)是(shì)一樣的(de),這(zhè)裏就(jiù)不(bù)在(zài)說(shuō)了(le/liǎo)。

相關案例查看更多