android.content.res.Resources$NotFoundException: String resource ID #0x1

发布时间 2023-11-22 18:53:40作者: xzylcf

在Android开发中如果出现android.content.res.Resources$NotFoundException: String resource ID #0x1这样的错误,你想也不用想,一定是Textview控件显示数据出了问题:mTextview.setText(这里的传入的数据一定写成int类型了)。我们需要做的是eg:mTextview.setText(1+""),也就是参数转化成字符串,接下来我们看一源码:

 
如果控件TextView添加的数据写成 eg:mTextview.setText(1)时,也就是传入的是数字,源码分析如下:
 

 

/**
 * Sets the text to be displayed using a string resource identifier.
 *
 * @param resid the resource identifier of the string resource to be displayed
 *
 * @see #setText(CharSequence)
 *
 * @attr ref android.R.styleable#TextView_text
 */
@android.view.RemotableViewMethod
public final void setText(@StringRes int resid) {
    setText(getContext().getResources().getText(resid));
    mTextFromResource = true;
}

 

从该方法的注释我们可以看到参数 resid要是一个string的类型。接着我们往下看getText():

 

 

/**
 * Return the string value associated with a particular resource ID.  The
 * returned object will be a String if this is a plain string; it will be
 * some other type of CharSequence if it is styled.
 * {@more}
 *
 * @param id The desired resource identifier, as generated by the aapt
 *           tool. This integer encodes the package, type, and resource
 *           entry. The value 0 is an invalid identifier.
 *
 * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
 *
 * @return CharSequence The string data associated with the resource, plus
 *         possibly styled text information.
 */
@NonNull public CharSequence getText(@StringRes int id) throws NotFoundException {
    CharSequence res = mResourcesImpl.getAssets().getResourceText(id);
    if (res != null) {
        return res;
    }
    throw new NotFoundException("String resource ID #0x"
            + Integer.toHexString(id));
}

 

上面的意思大概是:返回与特定资源ID相关联的字符串值。返回的对象将是一个字符串,如果这是一个普通的字符串;@param id :所需的资源标识符,由aapt工具生成。这个整数编码了包、类型和资源条目,值0是无效标识符。接着继续往下看类AssetManager:

 

 


 



/**
 * Retrieves the string value associated with a particular resource
 * identifier for the current configuration.
 *检索与特定资源相关联的字符串值当前配置的标识符。
 * @param resId the resource identifier to load
 * @return the string value, or {@code null}
 */
@Nullable
final CharSequence getResourceText(@StringRes int resId) {
    synchronized (this) {
        final TypedValue outValue = mValue;
        if (getResourceValue(resId, 0, outValue, true)) {
            return outValue.coerceToString();
        }
        return null;
    }
}

返回字符序列CharSequence。那么是如何 检索与特定资源相关联的字符串 值。

继续看源码:




/**
 * Populates {@code outValue} with the data associated a particular
 * resource identifier for the current configuration.
 *
 * @param resId the resource identifier to load
 * @param densityDpi the density bucket for which to load the resource
 * @param outValue the typed value in which to put the data
 * @param resolveRefs {@code true} to resolve references, {@code false}
 *                    to leave them unresolved
 * @return {@code true} if the data was loaded into {@code outValue},
 *         {@code false} otherwise
 */
final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
        boolean resolveRefs) {
    synchronized (this) {
        final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs);
        if (block < 0) {
            return false;
        }

        // Convert the changing configurations flags populated by native code.
        outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
                outValue.changingConfigurations);

        if (outValue.type == TypedValue.TYPE_STRING) {
            outValue.string = mStringBlocks[block].get(outValue.data);
        }
        return true;
    }
}

/** Returns true if the resource was found, filling in mRetStringBlock and
 *  mRetData. */
private native final int loadResourceValue(int ident, short density, TypedValue outValue,
        boolean resolve);

如果找到对应的资源,则返回true。在这里我们也看到了native方法,

肯定是c/c++去做事了,我们不去考虑。如果找不到返回null,

我们从如下代码就可以知道:



@Nullable
final CharSequence getResourceText(@StringRes int resId) {
    synchronized (this) {
        final TypedValue outValue = mValue;
        if (getResourceValue(resId, 0, outValue, true)) {
            return outValue.coerceToString();
        }
        return null;
    }
}


如果找到对应的资源,我们就经过一系列的方法转化成字符序列,

代码如下:


/**
 * Regardless of the actual type of the value, 
try to convert it to a
 * string value.  For example, a color type will be converted 
to a
 * string of the form #aarrggbb.
 * 
 * @return CharSequence The coerced string value.  
If the value is
 *         null or the type is not known, null is returned.
 */
public final CharSequence coerceToString()
{
    int t = type;
    if (t == TYPE_STRING) {
        return string;
    }
    return coerceToString(t, data);
}
    2.------------------------------------------

/**
 * Perform type conversion as per {@link #coerceToString()} on an
 * explicitly supplied type and data.
 * 
 * @param type The data type identifier.
 * @param data The data value.
 * 
 * @return String The coerced string value.  If the value is
 *         null or the type is not known, null is returned.
 */
public static final String coerceToString(int type, int data)
{
    switch (type) {
    case TYPE_NULL:
        return null;
    case TYPE_REFERENCE:
        return "@" + data;
    case TYPE_ATTRIBUTE:
        return "?" + data;
    case TYPE_FLOAT:
        return Float.toString(Float.intBitsToFloat(data));
    case TYPE_DIMENSION:
        return Float.toString(complexToFloat(data)) + DIMENSION_UNIT_STRS[
            (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK];
    case TYPE_FRACTION:
        return Float.toString(complexToFloat(data)*100) + FRACTION_UNIT_STRS[
            (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK];
    case TYPE_INT_HEX:
        return "0x" + Integer.toHexString(data);
    case TYPE_INT_BOOLEAN:
        return data != 0 ? "true" : "false";
    }

    if (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT) {
        return "#" + Integer.toHexString(data);
    } else if (type >= TYPE_FIRST_INT && type <= TYPE_LAST_INT) {
        return Integer.toString(data);
    }

    return null;
}
...........................


 

如果控件TextView添加的数据写成 eg:mTextview.setText(1+"")时,也就是传入的是字符串,源码分析如下:
 
next1.
/**
 * Sets the text to be displayed. TextView <em>does not</em> accept
 * HTML-like formatting, which you can do with text strings in XML resource files.
 * To style your strings, attach android.text.style.* objects to a
 * {@link android.text.SpannableString}, or see the
 * <a href="{@docRoot}guide/topics/resources/available-resources.html#stringresources">
 * Available Resource Types</a> documentation for an example of setting
 * formatted text in the XML resource file.
 * <p/>
 * When required, TextView will use {@link android.text.Spannable.Factory} to create final or
 * intermediate {@link Spannable Spannables}. Likewise it will use
 * {@link android.text.Editable.Factory} to create final or intermediate
 * {@link Editable Editables}.
 *
 * @param text text to be displayed
 *
 * @attr ref android.R.styleable#TextView_text
 */
@android.view.RemotableViewMethod
public final void setText(CharSequence text) {
    setText(text, mBufferType);
}

 

next2.

设置要显示的文本

 

/**
 * Sets the text to be displayed and the {@link android.widget.TextView.BufferType}.
 * <p/>
 * When required, TextView will use {@link android.text.Spannable.Factory} to create final or
 * intermediate {@link Spannable Spannables}. Likewise it will use
 * {@link android.text.Editable.Factory} to create final or intermediate
 * {@link Editable Editables}.
 *
 * @param text text to be displayed
 * @param type a {@link android.widget.TextView.BufferType} which defines whether the text is
 *              stored as a static text, styleable/spannable text, or editable text
 *
 * @see #setText(CharSequence)
 * @see android.widget.TextView.BufferType
 * @see #setSpannableFactory(Spannable.Factory)
 * @see #setEditableFactory(Editable.Factory)
 *
 * @attr ref android.R.styleable#TextView_text
 * @attr ref android.R.styleable#TextView_bufferType
 */
public void setText(CharSequence text, BufferType type) {
    setText(text, type, true, 0);

    if (mCharWrapper != null) {
        mCharWrapper.mChars = null;
    }
}
next3.

 

 

private void setText(CharSequence text, BufferType type,
                     boolean notifyBefore, int oldlen) {
    mTextFromResource = false;
    if (text == null) {
        text = "";
    }

    // If suggestions are not enabled, remove the suggestion spans from the text
    if (!isSuggestionsEnabled()) {
        text = removeSuggestionSpans(text);
    }

    ............

    .................

    .........

 

 

notifyViewAccessibilityStateChangedIfNeeded(AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);

if (needEditableForNotification) {
    sendAfterTextChanged((Editable) text);
} else {
    // Always notify AutoFillManager - it will return right away if autofill is disabled.
    notifyAutoFillManagerAfterTextChangedIfNeeded();
}

// SelectionModifierCursorController depends on textCanBeSelected, which depends on text
if (mEditor != null) mEditor.prepareCursorControllers();