1. 概述
這節(jié)課我們來(lái)分析下Google是如何進(jìn)行 資源加載的,比如我們直接在布局文件中寫(xiě)一個(gè) ImageView,然后直接用 android:src="@mipmap/ic_launcher" 就能引入一張圖片,到底是為什么,那么接下來(lái)我們就來(lái)看下, 系統(tǒng)到底是如何加載資源的,下邊我們就以 ImageView里邊的 src 為例,來(lái)進(jìn)行源碼分析,或者文字的textColor都是一樣的,都是根據(jù)下邊源碼分析的。
2. 源碼分析流程如下
2.1>:直接點(diǎn)擊ImageView,進(jìn)入源碼,然后看下ImageView的第三個(gè)構(gòu)造方法:
public ImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initImageView();
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.ImageView, defStyleAttr, defStyleRes);
final Drawable d = a.getDrawable(R.styleable.ImageView_src);
if (d != null) {
setImageDrawable(d);
}
mBaselineAlignBottom = a.getBoolean(R.styleable.ImageView_baselineAlignBottom, false);
mBaseline = a.getDimensionPixelSize(R.styleable.ImageView_baseline, -1);
setAdjustViewBounds(a.getBoolean(R.styleable.ImageView_adjustViewBounds, false));
setMaxWidth(a.getDimensionPixelSize(R.styleable.ImageView_maxWidth, Integer.MAX_VALUE));
setMaxHeight(a.getDimensionPixelSize(R.styleable.ImageView_maxHeight, Integer.MAX_VALUE));
final int index = a.getInt(R.styleable.ImageView_scaleType, -1);
}
從上邊我們看到,解析ImageView的 src
final Drawable d = a.getDrawable(R.styleable.ImageView_src);
然后點(diǎn)擊 getDrawable進(jìn)去,進(jìn)入 TypedArray源碼 中,看到 getDrawable()方法如下:
@Nullable
public Drawable getDrawable(@StyleableRes int index) {
if (mRecycled) {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
final TypedValue value = mValue;
if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
"Failed to resolve attribute at index " + index + ": " + value);
}
return mResources.loadDrawable(value, value.resourceId, mTheme);
}
return null;
}
可以看到,不管是解析圖片資源ImageView、還是文字的 textColor資源,其實(shí)最終都是調(diào)用的
mResources.loadDrawable(value, value.resourceId, mTheme);
而這個(gè) mResources 其實(shí)就是 Resource,也就是說(shuō)資源加載都是通過(guò) Resource對(duì)象去讀取的。既然資源的加載都是通過(guò) Resource類(lèi)加載的,那么如果我想獲取另外一個(gè)apk中的資源,是不是可以自己實(shí)例化一個(gè)Resource。
只要清楚是怎么去實(shí)例化 Rescource就好,也就是說(shuō)是如何 new Rescource對(duì)象,下邊可以看到源碼中它也是直接 new Rescource對(duì)象的
r = new Resources(assets, dm, config, compatInfo);
// 這個(gè)是 點(diǎn)擊Resources進(jìn)去的 ,它的構(gòu)造方法,參數(shù)1 assets表示 AssetManager資源管理
Resources(AssetManager assets, DisplayMetrics metrics, Configuration config)
在這里我們只需要搞清楚 AssetManager資源管理是怎么實(shí)例化的就ok,
@VisibleForTesting
protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key) {
AssetManager assets = new AssetManager();
if (key.mResDir != null) {
if (assets.addAssetPath(key.mResDir) == 0) {
Log.e(TAG, "failed to add asset path " + key.mResDir);
return null;
}
}
}
return assets;
}
通過(guò)下邊的方法可以知道,它是直接 new AssetManager的,同時(shí)也調(diào)用了
assets.addAssetPath(key.mResDir)方法,里邊的 mResDir 就是 apk的目錄
/**
* Add an additional set of assets to the asset manager. This can be
* either a directory or ZIP file. Not for use by applications. Returns
* the cookie of the added asset, or 0 on failure.
* {@hide}
*/
public final int addAssetPath(String path) {
synchronized (this) {
int res = addAssetPathNative(path, appAsLib);
makeStringBlocks(mStringBlocks);
return res;
}
}
通過(guò)以上分析,總結(jié)資源加載
所有的資源加載是通過(guò) Resource ->
而Rescource是直接 new = new Resources(assets, dm, config, compatInfo);對(duì)象,參數(shù)assets是非常重要的 ->
AssetsManager,這個(gè)其實(shí)是 Resource的核心的實(shí)例 ->
其實(shí)最終獲取資源是通過(guò) AssetsManager獲取的;
AssetsManager實(shí)例化如下,并且調(diào)用 addAssetPath(key.mResDir)方法,注意下邊的key.mResDir可以是一個(gè) ZIP路徑,其實(shí)就是 apk的目錄
AssetManager assets = new AssetManager();
assets.addAssetPath(key.mResDir)
以上就是 資源加載的源碼分析,注意其實(shí) .apk也是 .zip,都是一樣的。