知識(shí)
不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價(jià)值,我們?cè)谧非笃湟曈X表現(xiàn)的同時(shí),更側(cè)重于功能的便捷,營(yíng)銷的便利,運(yùn)營(yíng)的高效,讓網(wǎng)站成為營(yíng)銷工具,讓軟件能切實(shí)提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序?yàn)楹笃谏?jí)提供便捷的支持!
Android中資源管理機(jī)制詳細(xì)分析
發(fā)表時(shí)間:2020-10-19
發(fā)布人:葵宇科技
瀏覽次數(shù):69
尊敬原創(chuàng):http://blog.csdn.net/yuanzeyao/article/details/42386549
在Android中,所有的資本都在res目次下存放,包含drawable,layout,strings,anim等等,當(dāng)我們向工程中參加任何一個(gè)資本時(shí),會(huì)在R類中響應(yīng)會(huì)為該 資本分派一個(gè)id,我們?cè)趹?yīng)用中就是經(jīng)由過程這個(gè)id來(lái)拜訪資本的,信賴做過Andorid開辟的同伙對(duì)于這些肯定不會(huì)陌生,所以這個(gè)也不是我今天想要說的,我今天想和大年夜家一路進(jìn)修的是Android是若何治理資本的,在Android體系中,資本大年夜部分都是經(jīng)由過程xml文件定義的(drawable是圖片),如layout,string,anim都是xml文件,而對(duì)于layout,anim和strings等xml文件僅僅是解析xml文件,攫取指定的值罷了,然則對(duì)于layout文件中控件的解析就比較復(fù)雜了,例如對(duì)于一個(gè)Button,須要解析它所有的屬性值,這個(gè)是若何實(shí)現(xiàn)的呢。
這里我們起重要推敲一個(gè)問題,就是一個(gè)控件有哪些屬性是若何定義的?比如TextView具有哪些屬性?為什憒我設(shè)置TextView的樣式只能用style而不克不及用android:theme?這些信息都是在哪里定義的,想要弄清跋扈這個(gè)問題,就必須大年夜源碼工程招謎底,我應(yīng)用的是android4.1工程,如不雅你應(yīng)用的是其他版本的,那么可能用些進(jìn)出。
先看三個(gè)文件
1、d:\android4.1\frameworks\base\core\res\res\values\attrs.xml
看到attrs.xml文件,不知道你有沒有想起什么?當(dāng)我們?cè)谧远x控件的時(shí)刻,是不是會(huì)創(chuàng)建一個(gè)attrs.xml文件?應(yīng)用attrs.xml文件的目標(biāo)其實(shí)就是給我們自定義的控件添加屬性,打開這個(gè)目次后,你會(huì)看到定義了一個(gè)叫"Theme"的styleable,如下(我只朝長(zhǎng)進(jìn)步部分)
<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" />
在這個(gè)文件中,定義了Android中大年夜部分可以應(yīng)用的屬性,這里我說的是“定義”而不是“聲明”,同名在語(yǔ)法膳綾擎最大年夜的差別就是定義要有format屬性,而聲明沒有format屬性。
2、d:\android4.1\frameworks\base\core\res\res\values\attrs_manifest.xml
這個(gè)文件的名字和膳綾擎的文件的名字很像,就是多了一個(gè)manifest,故名思議就是定義了AndroidManifest.xml文件中的屬性,這瑯綾擎有一個(gè)很重要的一句話
<attr name="theme" format="reference" />
定義了一個(gè)theme屬性,這個(gè)就是我們?nèi)粘F椒苍贏ctivity膳綾擎應(yīng)用的theme屬性
3、d:\android4.1\frameworks\base\core\res\res\values\themes.xml
這個(gè)文件開端定義了一個(gè)叫做"Theme" 的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>
這個(gè)就是我們?nèi)粘F椒苍贏pplication或者Activity中應(yīng)用的Theme,大年夜這里可以看出,Theme也是一種style,那為什么style只能永遠(yuǎn)View/ViewGorup,而Theme只能用于Activity或者Application呢?先記住此問題,我們后續(xù)會(huì)為你解答
我們?cè)賮?lái)整合這三個(gè)文件的內(nèi)容吧,起首在attrs.xml文件中,定義了Android中大年夜部分的屬性,也就是說今后所有View/Activity中大年夜部分的屬性就是在這里定義的,然后在attrs_manifest.xml中定義了一個(gè)叫做theme的屬性,它的值就是再themes文件中定義的Theme或者持續(xù)自“Theme”的style。
有了膳綾擎的常識(shí)后,我們?cè)賮?lái)分析膳綾擎說過的兩個(gè)問題:
1、TextView控件(其他控件也一樣)的屬性在哪里定義的。
2、既然Theme也是style,那為什么View只能用style,Activity只能應(yīng)用theme?
所有View的屬性定義都是在attrs.xml文件中的,所以我們到attrs.xml文件中尋找TextView的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" />
膳綾擎的屬性我只朝長(zhǎng)進(jìn)步了部分,請(qǐng)留意,這里所有的屬性都是進(jìn)行“聲明”,你去搜刮這個(gè)styleable,會(huì)發(fā)明在TextView的styleable中不會(huì)找到theme這個(gè)屬性的聲明,所以你給任何一個(gè)view設(shè)置theme屬性是沒有效不雅的。請(qǐng)看下面一段代碼就知道為什么了。
定義一個(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); //應(yīng)用TypeArray攫取自定義的屬性 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(); } }
在attrs.xml我為MyTextView定義了一個(gè)orientation屬性,然后再M(fèi)yTextView的構(gòu)造函數(shù)中去攫取這個(gè)屬性,這里就涉及到TypeArray這個(gè)類,我們發(fā)明獲得TypeArray須要傳入R.style.MyTextView這個(gè)值,這個(gè)就是體系為我們拜訪MyTextView這個(gè)styleable供給的一個(gè)id,當(dāng)我們須要拿到orientation這個(gè)屬性的值甌,我們經(jīng)由過程R.style.MyTextView_orientation拿到,因?yàn)镸yTextView中沒有定義或者聲明theme屬性,所以我們找不到R.styleable.MyTextView_theme這個(gè)id,所以導(dǎo)致我們無(wú)法解析它的theme屬性。同樣回到TextView這個(gè)styleable來(lái),因?yàn)門extView的styleable中沒有定義theme屬性,所以theme對(duì)于TextView是沒有效的。所以即使你在TextView瑯綾擎參加theme屬性,即使編譯器不會(huì)給你報(bào)錯(cuò),這個(gè)theme也是被忽視了的。
我們?cè)賮?lái)看看Activity的屬性是若何定義的,因?yàn)锳ctivity是在AndroidManigest.xml文件中定義的,所以我們到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對(duì)于的styleable中是聲清楚明了theme的,所以它可以解析theme屬性。
膳綾擎兩個(gè)問題都已經(jīng)解答完了,下面來(lái)評(píng)論辯論另一個(gè)話題,就是Resources的獲取過程。
在我的別的一篇文┞仿曾經(jīng)評(píng)論辯論過這個(gè)話題更深層次懂得Context 這里我們?cè)賮?lái)進(jìn)修一下Resources的獲取過程。
在Android體系中,獲取Resources重要有兩種辦法,經(jīng)由過程Context獲取和PackageManager獲取
起首,我們看看我們經(jīng)由過程Context獲取,下面這張圖是Context相干類的類圖
[img]http://img.blog.csdn.net/20150104103842705?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveXVhbnpleWFv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
大年夜圖中可以看出,Context有兩個(gè)子類,一個(gè)是ContextWrapper,另一個(gè)是ContextImpl,而ContextWrapper依附于ContextImpl。結(jié)合源碼,我們會(huì)發(fā)明,Context是一個(gè)抽象類,它的┞鋒正實(shí)現(xiàn)類就是ContextImpl,而ContextWrapper就像他的名字一樣,僅僅是對(duì)Context的一層包裝,它的功能都是經(jīng)由過程調(diào)用屬性mBase完成,該mBase本質(zhì)就是指向一個(gè)ContextImpl類型的變量。我們獲取Resources時(shí)就是調(diào)用Context的getResources辦法,那么我們直接看看ContextImpl的getResources辦法吧
@Override public Resources getResources() { return mResources; }
我們發(fā)明這個(gè)辦法很簡(jiǎn)單,就是返回mResources屬性,那么這個(gè)屬性是在哪里 賦值的呢,經(jīng)由過程尋找發(fā)明,其實(shí)就是在創(chuàng)建ContextImpl,經(jīng)由過程調(diào)用Init進(jìn)行賦值的(具體邏輯參照《更深層次懂得Context》).這里我先給出getResource辦法的時(shí)序圖,然后跟蹤源碼。
[img]http://img.blog.csdn.net/20150104110812840?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveXVhbnpleWFv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
先大年夜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); }
我們發(fā)明,對(duì)mResource進(jìn)行賦值,是經(jīng)由過程調(diào)用LoadedApk中的getResource進(jìn)行的,傳入了ActivityThead類型的參數(shù)
public Resources getResources(ActivityThread mainThread) { if (mResources == null) { mResources = mainThread.getTopLevelResources(mResDir, this); } return mResources; }
在getResources辦法中,其實(shí)就是調(diào)用了ActivityThrad的getTopLevelResources辦法,個(gè)中mResDir就是apk文件的路徑(對(duì)于用戶安裝的app,此路徑就在/data/app下面的某一個(gè)apk),大年夜時(shí)序圖中可以知道,getTopLevelResources其實(shí)就是調(diào)用了一個(gè)同名辦法,我們直接看它的同名辦法吧
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; } }
這段代碼的邏輯不復(fù)雜,起首大年夜mActiveResouuces中經(jīng)由過程key拿到資本,如不雅資本不為null,并且是最新的,那么直接返回,不然創(chuàng)建一個(gè)AssetManager對(duì)象,并調(diào)用AssetManager的addAssetPath辦法,然后應(yīng)用創(chuàng)建的AssetManager為參數(shù),創(chuàng)建一個(gè)Resources對(duì)象,保存并返回。經(jīng)由過程膳綾擎的時(shí)序圖,我們發(fā)明在創(chuàng)建AssetManager的時(shí)刻,在其構(gòu)造函數(shù)中調(diào)用init辦法,我們看看init辦法做了什么吧
private native final void init();
居然是一個(gè)本處所法,那么我們只有看看對(duì)應(yīng)的Jni代碼了
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); }
這個(gè)瑯綾擎調(diào)用了本地的AssetManager的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); }
這例的ANDROID_ROOT保存的就是/system路徑,而kSystemAssets是
static const char* kSystemAssets = "framework/framework-res.apk";
還記得framework-res.apk是什么嗎,就是體系所有的資本文件。
到這里終于明白了,道理就是將體系的資本加載進(jìn)來(lái)。
接下來(lái)看看addAssetPath辦法吧,進(jìn)入源碼后,你會(huì)發(fā)明它也是一個(gè)本處所法,也須要看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; }
這里調(diào)用了本地AssetManager辦法的addAssetPath辦法。和體系資本一樣,都被加載進(jìn)來(lái)了。
下面看看PackageManager獲取Resource的流程吧
在PackageManager瑯綾擎獲取資本調(diào)用的是getResourcesForApplication辦法,getResourcesForApplication也有一個(gè)同名辦法,我們看辦正事的那個(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); }起首斷定包名是否是system,如不雅不是那么直接調(diào)用ActivityThread的getTopLevelResources辦法。不過這里會(huì)根據(jù)當(dāng)前應(yīng)用的應(yīng)用的uid和過程Id相等,如不雅相等則傳入app.sourceDir,不然傳入publicSourceDir,然則根據(jù)經(jīng)驗(yàn)時(shí)代sourceDir和publicSource一般情況下是雷同的。后面的邏輯和Context中的是一樣的,這里就不在說了。
相關(guān)案例查看更多
相關(guān)閱讀
- web
- 云南etc小程序
- 汽車回收系統(tǒng)
- 云南網(wǎng)站維護(hù)
- 安家微信小程序
- 報(bào)廢車拆解管理系統(tǒng)
- 云南網(wǎng)站建設(shè)開發(fā)
- 昆明小程序哪家好
- 云南小程序被騙
- 霸屏推廣
- 政府網(wǎng)站建設(shè)服務(wù)
- 網(wǎng)站上首頁(yè)
- 昆明網(wǎng)站建設(shè)公司
- 云南做網(wǎng)站
- 網(wǎng)站優(yōu)化
- 昆明小程序代建
- 汽車報(bào)廢軟件
- 云南網(wǎng)站建設(shè)選
- 網(wǎng)站排名優(yōu)化
- 人人商城
- 跳轉(zhuǎn)小程序
- 網(wǎng)站建設(shè)方法
- 報(bào)廢車拆解軟件
- 日歷組件
- 網(wǎng)站開發(fā)公司哪家好
- 網(wǎng)站維護(hù)
- 云南小程序開發(fā)費(fèi)用
- 小程序
- 百度排名
- 百度小程序