解決 Android 打印 No package identifier ... number 0x0000000 警告

前一段時間,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),不要硬編碼,對于管理也是很方便,何樂而不為呢

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容