Android中Spannable转义首引号致Html.fromHtml输出异常问题
Hey there! Let's dig into this issue you're facing as an Android newbie—this is actually a quirk (or edge-case bug) in Android's older Html.fromHtml() parser, and I'll break down what's going on:
1. 问题的核心原因
Your strings.xml uses an escaped quote (\") inside the CDATA block for the first <a> tag's href attribute, but here's the key point: CDATA sections in XML are meant to treat content as raw text, so you don't need to escape quotes inside them.
The older Html.fromHtml() method (the one you're using, without a mode parameter, which maps to pre-Android N behavior) can't handle this invalid escaped quote correctly. It misinterprets the \" as part of the attribute value, which breaks the entire <a> tag structure. This cascades into parsing errors:
- The opening
<strong>tag in the first paragraph never gets properly closed, so all subsequent text ends up bolded. - The broken
<a>tag causes the first line's content ("Timeline...") to be discarded entirely.
2. 为什么移除转义符就正常?
When you remove the backslash (\) to make it <a href="https://www.reddit.com/...">, you're using standard valid HTML syntax. The Html.fromHtml() parser can correctly parse the tag structure, so the Spannable is generated as expected—no missing lines, no unintended bolding.
3. 后续建议
- Stick to standard HTML in CDATA: Since CDATA protects your content from XML parsing, you don't need to escape quotes, ampersands, etc., inside it. Just write normal HTML like you would in a web page.
- Use modern parsing modes (if possible): If your app targets Android N (API 24) or higher, switch to
Html.fromHtml(htmlstring, Html.FROM_HTML_MODE_COMPACT)(or other supported modes). The newer parser has better error handling for edge cases like this, though it's still best to use valid HTML to avoid unexpected behavior.
对比示例
错误的strings.xml(导致问题)
<string name="nice_html"> <![CDATA[ <div><p><strong><a href=\"https://www.reddit.com/r/nosleep/comments/3ijnt6/im_a_search_and_rescue_officer_for_the_us_forest/\">Timeline</a> of my other stories separated by company.</strong></p><p><a href=\"https://redd.it/7jmvxo\">This</a> story about smashing modems from <a href=\"/u/devdevo1919\">u/devdevo1919</a> reminded of this little gem when I was a customer service representative at $SecurityCompany. Now before you say “but customer service isn’t IT”, let me explain the position. They <em>called</em> us CSRs, but what we really did was mostly technical support for the residential and small business security systems as well as some customer service functions. We had a separate team for the large businesses.</p></div> ]]> </string>
修正后的strings.xml(恢复正常)
<string name="nice_html"> <![CDATA[ <div><p><strong><a href="https://www.reddit.com/r/nosleep/comments/3ijnt6/im_a_search_and_rescue_officer_for_the_us_forest/">Timeline</a> of my other stories separated by company.</strong></p><p><a href="https://redd.it/7jmvxo">This</a> story about smashing modems from <a href="/u/devdevo1919">u/devdevo1919</a> reminded of this little gem when I was a customer service representative at $SecurityCompany. Now before you say “but customer service isn’t IT”, let me explain the position. They <em>called</em> us CSRs, but what we really did was mostly technical support for the residential and small business security systems as well as some customer service functions. We had a separate team for the large businesses.</p></div> ]]> </string>
你的Activity代码(无需修改,只要strings.xml正确即可)
TextView mylabel = (TextView) findViewById(R.id.mylabel); String htmlstring = getString(R.string.nice_html); // For API 24+, use Html.fromHtml(htmlstring, Html.FROM_HTML_MODE_COMPACT) Spannable spannable = (Spannable) Html.fromHtml(htmlstring); mylabel.setText(spannable);
内容的提问来源于stack exchange,提问作者sudoExclamationExclamation

