有个现成的函数,里面用到了 XmlResourceParser,之前是把它当成黑盒子的,但非常零星然而又持续了很久地,后台的崩溃日志里有记录关联到了这个函数内部。今天抽空整理了一下其中的逻辑,力图把问题消除掉。
其实这个玩意儿实现的路子是随 SAX 的方式来的,就是流水席,顺着文档文本内容的从前到后的顺序把标签和属性源源不断地端上来。当要打开资源中的 XML 进行解析时,调用是这样的:XmlResourceParser xml = resources.getXml(xmlResourceId),如果要解析 AndroidManifest.xml,则是这样,XmlResourceParser parser = resources.getAssets().openXmlResourceParser(“AndroidManifest.xml”)。
梳理过程中发现,原先的实现有考虑不周的情况,对某个节点的解析,需要它是在特定的父节点下,而相应的判断没有跟上。另外,在访问一个标签的属性时,发现用名称访问是无法成功的,而用索引访问则可以。事后发现系统还提供了一个 Android.Util.Xml 的工具类,可以把前文中的 parser 转换为一个属性组的对象,如这样 AttributeSet attributeSet = Xml.asAttributeSet(parser)。
网上有一些人会有如何把 XmlResourceParser 用起来更像传统 XML 解析器的想法,大约就是如何转为 DOM 模型的意思。在 SO 上有个讨论,见这里,https://stackoverflow.com/questions/36176740/get-org-w3c-dom-document-from-xmlresourceparser。其中提到要历经两次封装,第一次就是要把 XmlResourceParser 先裹一层,最小化增加必要的改造以使其可以适配 org.xmlpull.v1.sax2.Driver 的二次封装。
虽然目前用不到,但是作为一个信息收集者,还是找了一份对 XmlResourceParser 浅封装的代码,链接是 https://www.oomake.com/question/4511818,代码如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
public class XmlPullParserWrapper implements XmlPullParser { private XmlPullParser mParser; public XmlPullParserWrapper(XmlPullParser parser) { mParser = parser; } @Override public void setFeature(String name, boolean state) throws XmlPullParserException { mParser.setFeature(name, state); } @Override public boolean getFeature(String name) { return mParser.getFeature(name); } @Override public void setProperty(String name, Object value) throws XmlPullParserException { mParser.setProperty(name, value); } @Override public Object getProperty(String name) { return mParser.getProperty(name); } @Override public void setInput(Reader in) throws XmlPullParserException { mParser.setInput(in); } @Override public void setInput(InputStream inputStream, String inputEncoding) throws XmlPullParserException { mParser.setInput(inputStream, inputEncoding); } @Override public String getInputEncoding() { return mParser.getInputEncoding(); } @Override public void defineEntityReplacementText(String entityName, String replacementText) throws XmlPullParserException { mParser.defineEntityReplacementText(entityName, replacementText); } @Override public int getNamespaceCount(int depth) throws XmlPullParserException { return mParser.getNamespaceCount(depth); } @Override public String getNamespacePrefix(int pos) throws XmlPullParserException { return mParser.getNamespacePrefix(pos); } @Override public String getNamespaceUri(int pos) throws XmlPullParserException { return mParser.getNamespaceUri(pos); } @Override public String getNamespace(String prefix) { return mParser.getNamespace(prefix); } @Override public int getDepth() { return mParser.getDepth(); } @Override public String getPositionDescription() { return mParser.getPositionDescription(); } @Override public int getLineNumber() { return mParser.getLineNumber(); } @Override public int getColumnNumber() { return mParser.getColumnNumber(); } @Override public boolean isWhitespace() throws XmlPullParserException { return mParser.isWhitespace(); } @Override public String getText() { return mParser.getText(); } @Override public char[] getTextCharacters(int[] holderForStartAndLength) { return mParser.getTextCharacters(holderForStartAndLength); } @Override public String getNamespace() { return mParser.getNamespace(); } @Override public String getName() { return mParser.getName(); } @Override public String getPrefix() { return ""; } @Override public boolean isEmptyElementTag() throws XmlPullParserException { return mParser.isEmptyElementTag(); } @Override public int getAttributeCount() { return mParser.getAttributeCount(); } @Override public String getAttributeNamespace(int index) { return mParser.getAttributeNamespace(index); } @Override public String getAttributeName(int index) { return mParser.getAttributeName(index); } @Override public String getAttributePrefix(int index) { return ""; } @Override public String getAttributeType(int index) { return mParser.getAttributeType(index); } @Override public boolean isAttributeDefault(int index) { return mParser.isAttributeDefault(index); } @Override public String getAttributeValue(int index) { return mParser.getAttributeValue(index); } @Override public String getAttributeValue(String namespace, String name) { return mParser.getAttributeValue(namespace, name); } @Override public int getEventType() throws XmlPullParserException { return mParser.getEventType(); } @Override public int next() throws XmlPullParserException, IOException { return mParser.next(); } @Override public int nextToken() throws XmlPullParserException, IOException { return mParser.nextToken(); } @Override public void require(int type, String namespace, String name) throws XmlPullParserException, IOException { mParser.require(type, namespace, name); } @Override public String nextText() throws XmlPullParserException, IOException { return mParser.nextText(); } @Override public int nextTag() throws XmlPullParserException, IOException { return mParser.nextTag(); } } |
在该文中,对此封装类的使用像这样:
1 2 3 |
Persister serializer = new Persister(); serializer.read(AFoo.class, new NodeReader(new PullReader(new XmlPullParserWrapper(r.getXml(resId)))).readRoot()) |
这篇文章虽然是中文的,但看上去说不定就是内容农场直接用工具翻译的 SO 上的另外哪一篇,时间关系,就没再找原始出处,毕竟防丢的主要部分(也就是代码)已经在上面存档了。