前一段時間,leader 找我說,我們的程序啟動后,打印了很多警告
W/ResourceType: No package identifier when getting name for resource number 0x00000000
讓我找一下原因,并解決一下,現(xiàn)在原因已經(jīng)找到,并且問題也完美解決,在此記錄一下
直接 Google 了一下關(guān)鍵字,發(fā)現(xiàn)只有一個是關(guān)鍵字完全匹配的,其他的雖然能匹配大部分但是和我不是同一個問題,貼出鏈接
去除 No package identifier when getting name ...
點(diǎn)進(jìn)去看了一下,主要的修改就是將 View 的 background 的具體顏色值修改為 @color/xxx 的形式
為了驗(yàn)證這個修改是否真的有用,于是就去看了一下 View 是如何設(shè)置背景色的
public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);
...
final int N = a.getIndexCount();
for (int i = 0; i < N; i++) {
int attr = a.getIndex(i);
switch (attr) {
case com.android.internal.R.styleable.View_background:
// 獲取 drawable
background = a.getDrawable(attr);
break;
...
}
}
...
}
View 的構(gòu)造函數(shù)里 調(diào)用了 TypedArray.java 的 getDrawable() 函數(shù)
public Drawable getDrawable(int index) {
if (mRecycled) {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
final TypedValue value = mValue;
// 向 TypedValue 里面填充信息
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);
}
// 調(diào)用 Resources.java 的 loadDrawable() 函數(shù)
return mResources.loadDrawable(value, value.resourceId, mTheme);
}
return null;
}
private boolean getValueAt(int index, TypedValue outValue) {
final int[] data = mData;
final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return false;
}
outValue.type = type;
outValue.data = data[index+AssetManager.STYLE_DATA];
outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
// 為 resourceId 賦值
outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID];
outValue.changingConfigurations = data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS];
outValue.density = data[index+AssetManager.STYLE_DENSITY];
outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;
return true;
}
在 getValueAt() 函數(shù)中,有一步是對 resourceId 賦值,但這里的值是 0,因?yàn)樵诓季治募?,背景使用的是具體顏色值,所以是沒有 id 的
接下來繼續(xù)看 Resources.java 的 loadDrawable() 函數(shù),這個函數(shù)樂視的 Eui 系統(tǒng)組開發(fā)人員修改過,增加了一段邏輯
Drawable loadDrawable(TypedValue value, int id, Theme theme) throws NotFoundException {
...
String resourceName = "";
String drawableName = "";
try {
resourceName = getResourceName(id);
drawableName = ResourceHelper.getResName(resourceName);
} catch(Exception e) {
}
...
}
getResourceName() 函數(shù)很可疑,繼續(xù)向下跟蹤
public String getResourceName(@AnyRes int resid) throws NotFoundException {
String str = mAssets.getResourceName(resid);
if (str != null) return str;
throw new NotFoundException("Unable to find resource ID #0x"
+ Integer.toHexString(resid));
}
這里調(diào)用了 AssetManager.java 的 getResourceName() 函數(shù),它是個native 函數(shù)
native final String getResourceName(int resid);
對應(yīng)的 C++ 實(shí)現(xiàn)是在 android_util_AssetManager.cpp 中
static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject clazz, jint resid)
{
AssetManager* am = assetManagerForJavaObject(env, clazz);
if (am == NULL) {
return NULL;
}
ResTable::resource_name name;
if (!am->getResources().getResourceName(resid, true, &name)) {
return NULL;
}
...
}
getResources() 定義在 AssetManager.h 中
const ResTable& getResources(bool required = true) const;
實(shí)現(xiàn)在 AssetManager.cpp 中
const ResTable& AssetManager::getResources(bool required) const
{
const ResTable* rt = getResTable(required);
return *rt;
}
const ResTable* AssetManager::getResTable(bool required) const
{
ResTable* rt = mResources;
if (rt) {
return rt;
}
...
mResources = new ResTable();
...
return mResources;
}
ResTable 定義在 ResourceTypes.h 里,我們來看 ResourceTypes.cpp
#define Res_GETPACKAGE(id) ((id>>24)-1)
class ResTable
{
public:
ResTable();
ResTable(const void* data, size_t size, const int32_t cookie,
bool copyData=false);
~ResTable();
...
bool getResourceName(uint32_t resID, bool allowUtf8, resource_name* outName) const;
...
};
// Mapping from resource package IDs to indices into the internal
// package array.
uint8_t mPackageMap[256];
uint8_t mNextPackageId;
這里也列出了其他后面要用到的一些聲明,
// 構(gòu)造函數(shù)
ResTable::ResTable()
: mError(NO_INIT), mNextPackageId(2) // 初始化 mNextPackageId 為 2
{
...
// mPackageMap 數(shù)組所有元素設(shè)置為 0
memset(mPackageMap, 0, sizeof(mPackageMap));
...
}
ResTable::ResTable(const void* data, size_t size, const int32_t cookie, bool copyData)
: mError(NO_INIT), mNextPackageId(2) // 初始化 mNextPackageId 為 2
{
...
// mPackageMap 數(shù)組所有元素設(shè)置為 0
memset(mPackageMap, 0, sizeof(mPackageMap));
...
}
bool ResTable::getResourceName(uint32_t resID, bool allowUtf8, resource_name* outName) const
{
if (mError != NO_ERROR) {
return false;
}
const ssize_t p = getResourcePackageIndex(resID);
if (p < 0) {
if (Res_GETPACKAGE(resID)+1 == 0) {
ALOGW("No package identifier when getting name for resource number 0x%08x", resID);
} else {
ALOGW("No known package when getting name for resource number 0x%08x", resID);
}
return false;
}
...
}
inline ssize_t ResTable::getResourcePackageIndex(uint32_t resID) const
{
return ((ssize_t)mPackageMap[Res_GETPACKAGE(resID)+1])-1;
}
Res_GETPACKAGE() 是將 id 右移 24 位后減 1,這里 id 是 0 所以返回值是 -1,Res_GETPACKAGE(resID)+1 的結(jié)果是 0
mPackageMap[0] = 0,因?yàn)樵?ResTable 的構(gòu)造函數(shù)中 mNextPackageId 被初始化為 2 ,所以 0 和 1 位置沒有被修改
所以 getResourcePackageIndex() 返回 -1 ,所以,會進(jìn)入 p < 0 的分支,Res_GETPACKAGE() 返回 -1 ,所以執(zhí)行了
ALOGW("No package identifier when getting name for resource number 0x%08x", resID);
0x%08x 是表示:以 0x 開頭,將 resID 格式化為 8 位的 16 進(jìn)制數(shù)字,不足 8 位用 0 補(bǔ)齊,x 表示 16 進(jìn)制
進(jìn)而打印出了 No package identifier when getting name for resource number 0x00000000 警告
至此,這個問題已經(jīng)分析完畢,在 ResourceTypes.cpp 內(nèi)搜索 No package identifier when getting 發(fā)現(xiàn),不止 getResourceName() 會打印這個警告,還有 getResource() , getBagLocked() 以及 getResourceFlags() 函數(shù)都包含有和 getResourceName() 相同的邏輯,這三個函數(shù)具體什么時候會調(diào)用就不再深入分析了
總結(jié),在開發(fā) Android 程序的時候,顏色資源最好放到 values/colors.xml 文件內(nèi),不要硬編碼,對于管理也是很方便,何樂而不為呢