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

前一段时间,leader 找我说,我们的程序启动后,打印了很多警告

W/ResourceType: No package identifier when getting name for resource number 0x00000000

让我找一下原因,并解决一下,现在原因已经找到,并且问题也完美解决,在此记录一下

直接 Google 了一下关键字,发现只有一个是关键字完全匹配的,其他的虽然能匹配大部分但是和我不是同一个问题,贴出链接
去除 No package identifier when getting name ...
点进去看了一下,主要的修改就是将 View 的 background 的具体颜色值修改为 @color/xxx 的形式
为了验证这个修改是否真的有用,于是就去看了一下 View 是如何设置背景色的

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 的构造函数里 调用了 TypedArray.java 的 getDrawable() 函数

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);
        }
        // 调用 Resources.java 的 loadDrawable() 函数
        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() 函数中,有一步是对 resourceId 赋值,但这里的值是 0,因为在布局文件中,背景使用的是具体颜色值,所以是没有 id 的
接下来继续看 Resources.java 的 loadDrawable() 函数,这个函数乐视的 Eui 系统组开发人员修改过,增加了一段逻辑

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() 函数很可疑,继续向下跟踪

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));
}

这里调用了 AssetManager.java 的 getResourceName() 函数,它是个native 函数

native final String getResourceName(int resid);

对应的 C++ 实现是在 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;

实现在 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;

这里也列出了其他后面要用到的一些声明,

// 构造函数
ResTable::ResTable()
    : mError(NO_INIT), mNextPackageId(2) // 初始化 mNextPackageId 为 2
{
    ...
    // mPackageMap 数组所有元素设置为 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 数组所有元素设置为 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 的结果是 0
mPackageMap[0] = 0,因为在 ResTable 的构造函数中 mNextPackageId 被初始化为 2 ,所以 0 和 1 位置没有被修改
所以 getResourcePackageIndex() 返回 -1 ,所以,会进入 p < 0 的分支,Res_GETPACKAGE() 返回 -1 ,所以执行了

ALOGW("No package identifier when getting name for resource number 0x%08x", resID);

0x%08x 是表示:以 0x 开头,将 resID 格式化为 8 位的 16 进制数字,不足 8 位用 0 补齐,x 表示 16 进制
进而打印出了 No package identifier when getting name for resource number 0x00000000 警告
至此,这个问题已经分析完毕,在 ResourceTypes.cpp 内搜索 No package identifier when getting 发现,不止 getResourceName() 会打印这个警告,还有 getResource() , getBagLocked() 以及 getResourceFlags() 函数都包含有和 getResourceName() 相同的逻辑,这三个函数具体什么时候会调用就不再深入分析了

总结,在开发 Android 程序的时候,颜色资源最好放到 values/colors.xml 文件内,不要硬编码,对于管理也是很方便,何乐而不为呢

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容