Android 6.0 inflate过程分析
来源:互联网 发布:linux kill -9 编辑:程序博客网 时间:2024/06/05 04:22
综述
在aapt编译apk的过程中,aapt中的XMLNode类会将资源生成一个ResXMLTree对象,并将其序列化到apk文件中。
Android系统中,首先用C++实现了ResXMLParser类,用来解析存储在apk中的ResXMLTree。然后用Java封装了一个XmlBlock对象,通过JNI方法调用ResXMLParser。
XmlBlock.Parser类是一个XmlResourceParser接口的实现。XmlResourceParser接口继承自XmlPullParser接口和AttributeSet接口。其中XmlPullParser是xml pull方式解析xml的标准接口。AttributeSet是访问资源的封装接口。
LayoutInflater使用根据上下文获得的XmlBlock.Parser对象去获取layout的描述,并生成View对象,将子View附着到父View中。
预备知识
XmlPullParser解析xml的简要教程
XmlPullParser是一种基于流,根据事件来解析xml的解析器。
我们所要做的事情,主要就是解析文档开始结束,Tag开始结束这4个事件:
* XmlPullParser.START_DOCUMENT: 文档开始,该准备什么数据结构或者暂存逻辑,可以在此时把容器new出来。
* XmlPullParser.END_DOCUMENT:文档结束。可以真正处理暂存的结构了。
* XmlPullParser.START_TAG: Tag起始,一个新的Tag发现了。
* XmlPullParser.END_TAG: 这个Tag结束了,处理处理扔到容器里吧。
- 构建XmlPullParserFactory的实例. 放在try…catch中是因为有XmlPullParserException异常要处理。
try { XmlPullParserFactory pullParserFactory = XmlPullParserFactory .newInstance();
- 获取XmlPullParser的实例
XmlPullParser xmlPullParser = pullParserFactory.newPullParser();
- 设置输入流 xml文件
xmlPullParser.setInput( context.getResources().openRawResource(R.raw.xml文件名), "UTF-8");
- 开始解析
int eventType = xmlPullParser.getEventType();
- 主循环, 直至遇到文档结束事件XmlPullParser.END_DOCUMENT
try { while (eventType != XmlPullParser.END_DOCUMENT) {
- XmlPullParser.START_DOCUMENT事件时,处理文档开始。此处可以准备一个数据结构存储等。
String nodeName = xmlPullParser.getName(); switch (eventType) { case XmlPullParser.START_DOCUMENT: // 处理文档开始 break;
- 处理节点开始事件
case XmlPullParser.START_TAG: // 此处开始一个节点,可以读取下面的属性和值 break;
- 处理节点结束事件,比如可以将对象加入到容器中。
case XmlPullParser.END_TAG: // 结束节点 break; default: break; }
- 读取下一个事件
eventType = xmlPullParser.next(); } } catch (NumberFormatException e) { Log.e(TAG, e.getLocalizedMessage()); } catch (IOException e) { Log.e(TAG, e.getLocalizedMessage()); } } catch (XmlPullParserException e) { Log.e("Xml", e.getLocalizedMessage()); }
下面我们来看一下XmlPullParser的官方例子:
来自类的定义:http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/util/AttributeSet.java#58
import java.io.IOException;import java.io.StringReader;import org.xmlpull.v1.XmlPullParser;import org.xmlpull.v1.XmlPullParserException;import org.xmlpull.v1.XmlPullParserFactory;public class SimpleXmlPullApp{ public static void main (String args[]) throws XmlPullParserException, IOException { XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); factory.setNamespaceAware(true); XmlPullParser xpp = factory.newPullParser(); xpp.setInput( new StringReader ( "<foo>Hello World!</foo>" ) ); int eventType = xpp.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { if(eventType == XmlPullParser.START_DOCUMENT) { System.out.println("Start document"); } else if(eventType == XmlPullParser.START_TAG) { System.out.println("Start tag "+xpp.getName()); } else if(eventType == XmlPullParser.END_TAG) { System.out.println("End tag "+xpp.getName()); } else if(eventType == XmlPullParser.TEXT) { System.out.println("Text "+xpp.getText()</a>); } eventType = xpp.next(); } System.out.println("End document"); }}
XmlPullParser接口
141public interface XmlPullParser {
XmlPullParser其实只是一个接口。学习了如何使用之后,我们看一下XmlPullParser接口都要求实现些什么。
可选特性
可选的特性,这些特性默认都是关闭的
* FEATURE_PROCESS_NAMESPACES:解析器是否处理命名空间。必须在解析开始前就设好,中途不能再反悔了
* FEATURE_REPORT_NAMESPACE_ATTRIBUTES:命名空间的属性是否通过对属性的访问方式暴露出来
* FEATURE_PROCESS_DOCDECL:是否支持DTD
* FEATURE_VALIDATION: 是否支持在XML 1.0规范中定义的所有验证错误都将上报。
相关方法:
* void setFeature(String name, boolean state) throws XmlPullParserException; 设置feature
* boolean getFeature(String name); 获取feature值,未定义的值当然是false。
相关的代码如下:
345 // ----------------------------------------------------------------------------346 // namespace related features347348 /**349 * This feature determines whether the parser processes350 * namespaces. As for all features, the default value is false.351 * <p><strong>NOTE:</strong> The value can not be changed during352 * parsing an must be set before parsing.353 *354 * @see #getFeature355 * @see #setFeature356 */357 String FEATURE_PROCESS_NAMESPACES =358 "http://xmlpull.org/v1/doc/features.html#process-namespaces";359360 /**361 * This feature determines whether namespace attributes are362 * exposed via the attribute access methods. Like all features,363 * the default value is false. This feature cannot be changed364 * during parsing.365 *366 * @see #getFeature367 * @see #setFeature368 */369 String FEATURE_REPORT_NAMESPACE_ATTRIBUTES =370 "http://xmlpull.org/v1/doc/features.html#report-namespace-prefixes";371372 /**373 * This feature determines whether the document declaration374 * is processed. If set to false,375 * the DOCDECL event type is reported by nextToken()376 * and ignored by next().377 *378 * If this feature is activated, then the document declaration379 * must be processed by the parser.380 *381 * <p><strong>Please note:</strong> If the document type declaration382 * was ignored, entity references may cause exceptions383 * later in the parsing process.384 * The default value of this feature is false. It cannot be changed385 * during parsing.386 *387 * @see #getFeature388 * @see #setFeature389 */390 String FEATURE_PROCESS_DOCDECL =391 "http://xmlpull.org/v1/doc/features.html#process-docdecl";392393 /**394 * If this feature is activated, all validation errors as395 * defined in the XML 1.0 specification are reported.396 * This implies that FEATURE_PROCESS_DOCDECL is true and both, the397 * internal and external document type declaration will be processed.398 * <p><strong>Please Note:</strong> This feature can not be changed399 * during parsing. The default value is false.400 *401 * @see #getFeature402 * @see #setFeature403 */404 String FEATURE_VALIDATION =405 "http://xmlpull.org/v1/doc/features.html#validation";406407 /**408 * Use this call to change the general behaviour of the parser,409 * such as namespace processing or doctype declaration handling.410 * This method must be called before the first call to next or411 * nextToken. Otherwise, an exception is thrown.412 * <p>Example: call setFeature(FEATURE_PROCESS_NAMESPACES, true) in order413 * to switch on namespace processing. The initial settings correspond414 * to the properties requested from the XML Pull Parser factory.415 * If none were requested, all features are deactivated by default.416 *417 * @exception XmlPullParserException If the feature is not supported or can not be set418 * @exception IllegalArgumentException If string with the feature name is null419 */420 void setFeature(String name,421 boolean state) throws XmlPullParserException;422423 /**424 * Returns the current value of the given feature.425 * <p><strong>Please note:</strong> unknown features are426 * <strong>always</strong> returned as false.427 *428 * @param name The name of feature to be retrieved.429 * @return The value of the feature.430 * @exception IllegalArgumentException if string the feature name is null431 */432433 boolean getFeature(String name);
事件类型
XmlPullParser定义了两种API,高级API和低级API。高级API通过next();方法取下一个事件,取得的事件如前面的例子所述,主要有下面5种:
- 基本事件类型常量
- int START_DOCUMENT = 0; xml文档开始
- int END_DOCUMENT = 1; xml文档结束
- int START_TAG = 2; tag开始,可以通过getName();方法获取Tag名
- int END_TAG = 3; tag开始,可以通过getName();方法获取Tag名
- int TEXT = 4; 文本,可通过getText()方法获取文本
而下面的高级事件API可以通过nextToken()方法获取。
- 高级事件类型常量
- int CDSECT = 5; CDATA
- int ENTITY_REF = 6; entity reference
- int IGNORABLE_WHITESPACE = 7; 可被忽略的空白符
- int PROCESSING_INSTRUCTION = 8; XML处理指令
- int COMMENT = 9; 注释
- int DOCDECL = 10; DTD
START_DOCUMENT
第一次调用getEvent()时才会遇到。
149 /**150 * Signalize that parser is at the very beginning of the document151 * and nothing was read yet.152 * This event type can only be observed by calling getEvent()153 * before the first call to next(), nextToken, or nextTag()</a>).154 *155 * @see #next156 * @see #nextToken157 */158 int START_DOCUMENT = 0;
END_DOCUMENT
xml文档已经到结尾。可以通过getEventType(), next()和nextToken()遇到。
如果在此状态下继续调next()或者nextToken()将引发异常。
160 /**161 * Logical end of the xml document. Returned from getEventType, next()162 * and nextToken()163 * when the end of the input document has been reached.164 * <p><strong>NOTE:</strong> subsequent calls to165 * <a href="#next()">next()</a> or <a href="#nextToken()">nextToken()</a>166 * may result in exception being thrown.167 *168 * @see #next169 * @see #nextToken170 */171 int END_DOCUMENT = 1;
START_TAG
获取到一个新标签。可以通过getName()方法取得标签名。还可以通过getNamespace()和getPrefix()获取名字空间和前缀。
如果FEATURE_PROCESS_NAMESPACES支持的话,还可以通过getAttribute()方法获取属性。
173 /**174 * Returned from getEventType(),175 * <a href="#next()">next()</a>, <a href="#nextToken()">nextToken()</a> when176 * a start tag was read.177 * The name of start tag is available from getName(), its namespace and prefix are178 * available from getNamespace() and getPrefix()179 * if <a href='#FEATURE_PROCESS_NAMESPACES'>namespaces are enabled</a>.180 * See getAttribute* methods to retrieve element attributes.181 * See getNamespace* methods to retrieve newly declared namespaces.182 *183 * @see #next184 * @see #nextToken185 * @see #getName186 * @see #getPrefix187 * @see #getNamespace188 * @see #getAttributeCount189 * @see #getDepth190 * @see #getNamespaceCount191 * @see #getNamespace192 * @see #FEATURE_PROCESS_NAMESPACES193 */194 int START_TAG = 2;
END_TAG
标签结事,可以获得的信息与START_TAG基本一致。
196 /**197 * Returned from getEventType(), <a href="#next()">next()</a>, or198 * <a href="#nextToken()">nextToken()</a> when an end tag was read.199 * The name of start tag is available from getName(), its200 * namespace and prefix are201 * available from getNamespace() and getPrefix().202 *203 * @see #next204 * @see #nextToken205 * @see #getName206 * @see #getPrefix207 * @see #getNamespace208 * @see #FEATURE_PROCESS_NAMESPACES209 */210 int END_TAG = 3;
TEXT
可以通过getText()方法获取文本的内容。
213 /**214 * Character data was read and will is available by calling getText().215 * <p><strong>Please note:</strong> <a href="#next()">next()</a> will216 * accumulate multiple217 * events into one TEXT event, skipping IGNORABLE_WHITESPACE,218 * PROCESSING_INSTRUCTION and COMMENT events,219 * In contrast, <a href="#nextToken()">nextToken()</a> will stop reading220 * text when any other event is observed.221 * Also, when the state was reached by calling next(), the text value will222 * be normalized, whereas getText() will223 * return unnormalized content in the case of nextToken(). This allows224 * an exact roundtrip without changing line ends when examining low225 * level events, whereas for high level applications the text is226 * normalized appropriately.227 *228 * @see #next229 * @see #nextToken230 * @see #getText231 */232 int TEXT = 4;
AttributeSet接口
XmlPullParser上面介绍的API中,没有专门提及处理属性相关的API,是因为我们专门有一个AttributeSet接口,它的实现类会处理资源中的属性,比如读取资源字符串的值。
下面例程介绍如何生成AttributeSet接口的对象。
XmlPullParser parser = resources.getXml(myResource);AttributeSet attributes = Xml.asAttributeSet(parser);
XmlPullParser和AttributeSet两个接口的实现都高度依赖于aapt对于资源xml的预编译优化。
举例来说:getAttributeFloatValue读取的值,在预编译时就是按浮点数存储的,不存在从文本转化的过程。
这个接口中基本都是获取值的方法,仅仅是类型不同。我们只以两个为例看一下:
* abstract boolean getAttributeBooleanValue(String namespace, String attribute, boolean defaultValue):以boolean类型返回namespace空间的attribute属性的值
* abstract boolean getAttributeBooleanValue(int index, boolean defaultValue): 以boolean类型返回索引为index号属性的值。
XmlResourceParser
将前面介绍的XmlPullParser和AttributeSet两个接口整合在一起,既支持解析xml结构,又能适用于属性资源,这就是XmlResourceParser.
我们先看看这个XmlResourceParser的定义:
23/**24 * The XML parsing interface returned for an XML resource. This is a standard25 * XmlPullParser interface, as well as an extended AttributeSet interface and26 * an additional close() method on this interface for the client to indicate27 * when it is done reading the resource.28 */29public interface XmlResourceParser extends XmlPullParser, AttributeSet, AutoCloseable {30 /**31 * Close this interface to the resource. Calls on the interface are no32 * longer value after this call.33 */34 public void close();35}
XmlResourceParser只定义了一个close方法,另外,它继承自XmlPullParser, AttributeSet和AutoCloseable三个接口。
AutoCloseable是标准的Java 7的接口:
35public interface AutoCloseable {36 /**37 * Closes the object and release any system resources it holds.38 */39 void close() throws Exception;40}
LayoutInflater
类介绍
44/**45 * Instantiates a layout XML file into its corresponding {@link android.view.View}46 * objects. It is never used directly. Instead, use47 * {@link android.app.Activity#getLayoutInflater()} or48 * {@link Context#getSystemService} to retrieve a standard LayoutInflater instance49 * that is already hooked up to the current context and correctly configured50 * for the device you are running on. For example:51 *52 * <pre>LayoutInflater inflater = (LayoutInflater)context.getSystemService53 * (Context.LAYOUT_INFLATER_SERVICE);</pre>54 *55 * <p>56 * To create a new LayoutInflater with an additional {@link Factory} for your57 * own views, you can use {@link #cloneInContext} to clone an existing58 * ViewFactory, and then call {@link #setFactory} on it to include your59 * Factory.60 *61 * <p>62 * For performance reasons, view inflation relies heavily on pre-processing of63 * XML files that is done at build time. Therefore, it is not currently possible64 * to use LayoutInflater with an XmlPullParser over a plain XML file at runtime;65 * it only works with an XmlPullParser returned from a compiled resource66 * (R.<em>something</em> file.)67 *68 * @see Context#getSystemService69 */
LayoutInflater用于解析xml,根据xml所写的布局生成View对象。
这个类的用法是从来不直接调用。而是通过两种方法间接调用:
* android.app.Activity.getLayoutInflater()
* Context.getSystemService()
通过这两个方法可以获取跟上下文绑定的LayoutInflater对象。调用例:
LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE);
如果你的View需要用额外的工厂类创建一种新的LayoutInflater,可以通过cloneInContext()去复制一份ViewFactory,然后用setFactory方法将你的工厂类添加进去。
因为性能的原因,View的inflate过程重度依赖于在编译时对XML的预处理. 所以,LayoutInflater不支持在运行时解析xml源文件。
详细分析
首先,LayoutInflater是个抽象类,具体被调用到的实现类如前面所述,根据上下文不同会有变化。
70public abstract class LayoutInflater {7172 private static final String TAG = LayoutInflater.class.getSimpleName();73 private static final boolean DEBUG = false;7475 /**76 * This field should be made private, so it is hidden from the SDK.77 * {@hide}78 */79 protected final Context mContext;
下面是一些可选项,是可以定制的。
81 // these are optional, set by the caller82 private boolean mFactorySet;83 private Factory mFactory;84 private Factory2 mFactory2;85 private Factory2 mPrivateFactory;86 private Filter mFilter;
接口
LayoutInflater为子类定义了一些接口,通过实现这些接口,可以实现一些定制化的功能。
Filter接口 - 实现过滤功能
如果允许,则onLoadClass返回真值。
111 /**112 * Hook to allow clients of the LayoutInflater to restrict the set of Views that are allowed113 * to be inflated.114 *115 */116 public interface Filter {117 /**118 * Hook to allow clients of the LayoutInflater to restrict the set of Views119 * that are allowed to be inflated.120 *121 * @param clazz The class object for the View that is about to be inflated122 *123 * @return True if this class is allowed to be inflated, or false otherwise124 */125 @SuppressWarnings("unchecked")126 boolean onLoadClass(Class clazz);127 }
Factory接口 - 解析自定义Tag
129 public interface Factory {130 /**131 * Hook you can supply that is called when inflating from a LayoutInflater.132 * You can use this to customize the tag names available in your XML133 * layout files.134 *135 * <p>136 * Note that it is good practice to prefix these custom names with your137 * package (i.e., com.coolcompany.apps) to avoid conflicts with system138 * names.139 *140 * @param name Tag name to be inflated.141 * @param context The context the view is being created in.142 * @param attrs Inflation attributes as specified in XML file.143 *144 * @return View Newly created view. Return null for the default145 * behavior.146 */147 public View onCreateView(String name, Context context, AttributeSet attrs);148 }
Factory2 - 工厂类版本2 - 支持父View参数
150 public interface Factory2 extends Factory {151 /**152 * Version of {@link #onCreateView(String, Context, AttributeSet)}153 * that also supplies the parent that the view created view will be154 * placed in.155 *156 * @param parent The parent that the created view will be placed157 * in; <em>note that this may be null</em>.158 * @param name Tag name to be inflated.159 * @param context The context the view is being created in.160 * @param attrs Inflation attributes as specified in XML file.161 *162 * @return View Newly created view. Return null for the default163 * behavior.164 */165 public View onCreateView(View parent, String name, Context context, AttributeSet attrs);166 }
抽象方法
cloneInContext方法
为新的上下文环境下创建新的LayoutInflater
236 /**237 * Create a copy of the existing LayoutInflater object, with the copy238 * pointing to a different Context than the original. This is used by239 * {@link ContextThemeWrapper} to create a new LayoutInflater to go along240 * with the new Context theme.241 *242 * @param newContext The new Context to associate with the new LayoutInflater.243 * May be the same as the original Context if desired.244 *245 * @return Returns a brand spanking new LayoutInflater object associated with246 * the given Context.247 */248 public abstract LayoutInflater cloneInContext(Context newContext);
好了,准备知识告一段落,我们下面正式开始分析inflate的流程。
inflate流程解析
View.inflate
路径:/frameworks/base/core/java/android/view/View.java
通过xml来进行inflate的入口,在View类的inflate方法中。
首先通过LayoutInflater.from(context)得到一个LayoutInflater类的对象,然后调用LayoutInflater的inflate方法。
19778 /**19779 * Inflate a view from an XML resource. This convenience method wraps the {@link19780 * LayoutInflater} class, which provides a full range of options for view inflation.19781 *19782 * @param context The Context object for your activity or application.19783 * @param resource The resource ID to inflate19784 * @param root A view group that will be the parent. Used to properly inflate the19785 * layout_* parameters.19786 * @see LayoutInflater19787 */19788 public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {19789 LayoutInflater factory = LayoutInflater.from(context);19790 return factory.inflate(resource, root);19791 }
LayoutInflater.from
首先要获取一个LayoutInflater对象,通过LayoutInflater.from方法,通过系统服务获得服务对象。
路径:/frameworks/base/core/java/android/view/LayoutInflater.java
224 /**225 * Obtains the LayoutInflater from the given context.226 */227 public static LayoutInflater from(Context context) {228 LayoutInflater LayoutInflater =229 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);230 if (LayoutInflater == null) {231 throw new AssertionError("LayoutInflater not found.");232 }233 return LayoutInflater;234 }
LayoutInflater.inflate
inflate的标准用法是inflate一个资源ID,这个xml是经过预编译的,性能比解析文件上的原始xml的性能要更好一些。
我们先看这个入口的解析资源中预编译xml的inflate版本:
397 /**398 * Inflate a new view hierarchy from the specified xml resource. Throws399 * {@link InflateException} if there is an error.400 *401 * @param resource ID for an XML layout resource to load (e.g.,402 * <code>R.layout.main_page</code>)403 * @param root Optional view to be the parent of the generated hierarchy (if404 * <em>attachToRoot</em> is true), or else simply an object that405 * provides a set of LayoutParams values for root of the returned406 * hierarchy (if <em>attachToRoot</em> is false.)407 * @param attachToRoot Whether the inflated hierarchy should be attached to408 * the root parameter? If false, root is only used to create the409 * correct subclass of LayoutParams for the root view in the XML.410 * @return The root View of the inflated hierarchy. If root was supplied and411 * attachToRoot is true, this is root; otherwise it is the root of412 * the inflated XML file.413 */414 public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
首先,通过上下文的getResource()方法来获取Resource的对象。
415 final Resources res = getContext().getResources();416 if (DEBUG) {417 Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("418 + Integer.toHexString(resource) + ")");419 }420
下面会通过资源对象的getLayout方法获取相关的XmlResourceParser。
421 final XmlResourceParser parser = res.getLayout(resource);422 try {423 return inflate(parser, root, attachToRoot);424 } finally {425 parser.close();426 }427 }
然后,调用inflate方法
429 /**430 * Inflate a new view hierarchy from the specified XML node. Throws431 * {@link InflateException} if there is an error.432 * <p>433 * <em><strong>Important</strong></em> For performance434 * reasons, view inflation relies heavily on pre-processing of XML files435 * that is done at build time. Therefore, it is not currently possible to436 * use LayoutInflater with an XmlPullParser over a plain XML file at runtime.437 *438 * @param parser XML dom node containing the description of the view439 * hierarchy.440 * @param root Optional view to be the parent of the generated hierarchy (if441 * <em>attachToRoot</em> is true), or else simply an object that442 * provides a set of LayoutParams values for root of the returned443 * hierarchy (if <em>attachToRoot</em> is false.)444 * @param attachToRoot Whether the inflated hierarchy should be attached to445 * the root parameter? If false, root is only used to create the446 * correct subclass of LayoutParams for the root view in the XML.447 * @return The root View of the inflated hierarchy. If root was supplied and448 * attachToRoot is true, this is root; otherwise it is the root of449 * the inflated XML file.450 */451 public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {452 synchronized (mConstructorArgs) {453 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");454455 final Context inflaterContext = mContext;456 final AttributeSet attrs = Xml.asAttributeSet(parser);457 Context lastContext = (Context) mConstructorArgs[0];458 mConstructorArgs[0] = inflaterContext;459 View result = root;460
下面开始就是我们前面讨论过的XmlPullParser的经典过程了。下面是要寻找根节点,如果遇到的不是XmlPullParser.START_TAG,就继续往下找,直至遇到第一个Tag为止。
461 try {462 // Look for the root node.463 int type;464 while ((type = parser.next()) != XmlPullParser.START_TAG &&465 type != XmlPullParser.END_DOCUMENT) {466 // Empty467 }
如果一个Tag也没找到,就报错。
468469 if (type != XmlPullParser.START_TAG) {470 throw new InflateException(parser.getPositionDescription()471 + ": No start tag found!");472 }
找到根Tag了,先打几行log.
473474 final String name = parser.getName();475476 if (DEBUG) {477 System.out.println("**************************");478 System.out.println("Creating root view: "479 + name);480 System.out.println("**************************");481 }482
如果是Tag是”merge”的话,如果有root可以attach,则递归调用rInflate去将merge的View attach到root上去。rInflate在下面会分析。
483 if (TAG_MERGE.equals(name)) {484 if (root == null || !attachToRoot) {485 throw new InflateException("<merge /> can be used only with a valid "486 + "ViewGroup root and attachToRoot=true");487 }488489 rInflate(parser, root, inflaterContext, attrs, false);490 } else {
如果不是merge,那么说明要建立一个新的根节点,调用createViewFromTag去创建之。createViewFromTag下面分析。
491 // Temp is the root view that was found in the xml492 final View temp = createViewFromTag(root, name, inflaterContext, attrs);493494 ViewGroup.LayoutParams params = null;495496 if (root != null) {497 if (DEBUG) {498 System.out.println("Creating params from root: " +499 root);500 }501 // Create layout params that match root, if supplied502 params = root.generateLayoutParams(attrs);503 if (!attachToRoot) {504 // Set the layout params for temp if we are not505 // attaching. (If we are, we use addView, below)506 temp.setLayoutParams(params);507 }508 }509510 if (DEBUG) {511 System.out.println("-----> start inflating children");512 }513
根节点创建就绪,调用rInflateChildren去建立根节点下面的子节点树。
514 // Inflate all children under temp against its context.515 rInflateChildren(parser, temp, attrs, true);516517 if (DEBUG) {518 System.out.println("-----> done inflating children");519 }520
如果root节点非空,而且要求attachToRoot,则将我们新建立的根节点attach到root上。否则,我们直接将我们生成的temp根节点作为根节点返回。
521 // We are supposed to attach all the views we found (int temp)522 // to root. Do that now.523 if (root != null && attachToRoot) {524 root.addView(temp, params);525 }526527 // Decide whether to return the root that was passed in or the528 // top view found in xml.529 if (root == null || !attachToRoot) {530 result = temp;531 }532 }533534 } catch (XmlPullParserException e) {535 InflateException ex = new InflateException(e.getMessage());536 ex.initCause(e);537 throw ex;538 } catch (Exception e) {539 InflateException ex = new InflateException(540 parser.getPositionDescription()541 + ": " + e.getMessage());542 ex.initCause(e);543 throw ex;544 } finally {545 // Don't retain static reference on context.546 mConstructorArgs[0] = lastContext;547 mConstructorArgs[1] = null;548 }549550 Trace.traceEnd(Trace.TRACE_TAG_VIEW);551552 return result;553 }554 }
LayoutInflater.rInflateChildren
我们先挑个小的方法继续看,先看看上面非merge情况下建立子树的方法rInflateChildren。
rInflateChildren只是rInflate的简单封装。rInflate其实比rInflateChildren就多了一个Context参数,其它都透传。
789 /**790 * Recursive method used to inflate internal (non-root) children. This791 * method calls through to {@link #rInflate} using the parent context as792 * the inflation context.793 * <strong>Note:</strong> Default visibility so the BridgeInflater can794 * call it.795 */796 final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,797 boolean finishInflate) throws XmlPullParserException, IOException {798 rInflate(parser, parent, parent.getContext(), attrs, finishInflate);799 }
LayoutInflater.rInflate
这个才是递归搜索建立子树的正主。
801 /**802 * Recursive method used to descend down the xml hierarchy and instantiate803 * views, instantiate their children, and then call onFinishInflate().804 * <p>805 * <strong>Note:</strong> Default visibility so the BridgeInflater can806 * override it.807 */808 void rInflate(XmlPullParser parser, View parent, Context context,809 AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {810
与根相比,子树的方法加上了对层数的限制。
811 final int depth = parser.getDepth();812 int type;813814 while (((type = parser.next()) != XmlPullParser.END_TAG ||815 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {816817 if (type != XmlPullParser.START_TAG) {818 continue;819 }820821 final String name = parser.getName();822
下面要处理一些特殊的Tag,它们的定义如下:
100 private static final String TAG_MERGE = "merge";101 private static final String TAG_INCLUDE = "include";102 private static final String TAG_1995 = "blink";103 private static final String TAG_REQUEST_FOCUS = "requestFocus";104 private static final String TAG_TAG = "tag";
823 if (TAG_REQUEST_FOCUS.equals(name)) {824 parseRequestFocus(parser, parent);825 } else if (TAG_TAG.equals(name)) {826 parseViewTag(parser, parent, attrs);827 } else if (TAG_INCLUDE.equals(name)) {828 if (parser.getDepth() == 0) {829 throw new InflateException("<include /> cannot be the root element");830 }831 parseInclude(parser, context, parent, attrs);832 } else if (TAG_MERGE.equals(name)) {833 throw new InflateException("<merge /> must be the root element");834 } else {
还是调用createViewFromTag来生成本级的View对象,然后还是调用rInflateChildren去建子树,实现递归。
835 final View view = createViewFromTag(parent, name, context, attrs);836 final ViewGroup viewGroup = (ViewGroup) parent;837 final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);838 rInflateChildren(parser, view, attrs, true);
子树建好后,add到父viewGroup中去。
839 viewGroup.addView(view, params);840 }841 }842
递归结束的话,调用onFinishInflate().
843 if (finishInflate) {844 parent.onFinishInflate();845 }846 }
LayoutInflater.createViewFromTag
根据Tag创建View对象。
707 /**708 * Creates a view from a tag name using the supplied attribute set.709 * <p>710 * <strong>Note:</strong> Default visibility so the BridgeInflater can711 * override it.712 *713 * @param parent the parent view, used to inflate layout params714 * @param name the name of the XML tag used to define the view715 * @param context the inflation context for the view, typically the716 * {@code parent} or base layout inflater context717 * @param attrs the attribute set for the XML tag used to define the view718 * @param ignoreThemeAttr {@code true} to ignore the {@code android:theme}719 * attribute (if set) for the view being inflated,720 * {@code false} otherwise721 */722 View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,723 boolean ignoreThemeAttr) {
如果Tag的名字是view的话,就从属性中读取类名作为新的name.
724 if (name.equals("view")) {725 name = attrs.getAttributeValue(null, "class");726 }727
下面处理主题相关
728 // Apply a theme wrapper, if allowed and one is specified.729 if (!ignoreThemeAttr) {730 final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);731 final int themeResId = ta.getResourceId(0, 0);732 if (themeResId != 0) {733 context = new ContextThemeWrapper(context, themeResId);734 }735 ta.recycle();736 }
如前面所讲的,如果有定义自己的工厂类的话,则调用那些工厂类的onCreateView。
743 try {744 View view;745 if (mFactory2 != null) {746 view = mFactory2.onCreateView(parent, name, context, attrs);747 } else if (mFactory != null) {748 view = mFactory.onCreateView(name, context, attrs);749 } else {750 view = null;751 }752753 if (view == null && mPrivateFactory != null) {754 view = mPrivateFactory.onCreateView(parent, name, context, attrs);755 }756
如果没有自定义工厂类,则调用LayoutInflater中的onCreateView或者createView。其中onCreateView也只是简单封装一下,唯一做的一件事就是将省略掉的android.view包名给补上。这样,createViewFromTag的主要逻辑也就结束了。
757 if (view == null) {758 final Object lastContext = mConstructorArgs[0];759 mConstructorArgs[0] = context;760 try {761 if (-1 == name.indexOf('.')) {762 view = onCreateView(parent, name, attrs);763 } else {764 view = createView(name, null, attrs);765 }766 } finally {767 mConstructorArgs[0] = lastContext;768 }769 }770771 return view;772 } catch (InflateException e) {773 throw e;774775 } catch (ClassNotFoundException e) {776 final InflateException ie = new InflateException(attrs.getPositionDescription()777 + ": Error inflating class " + name);778 ie.initCause(e);779 throw ie;780781 } catch (Exception e) {782 final InflateException ie = new InflateException(attrs.getPositionDescription()783 + ": Error inflating class " + name);784 ie.initCause(e);785 throw ie;786 }787 }
LayoutInflater.onCreateView
这个方法啥情况,唯一的作用就是把parent参数给扔了,呵呵。
/** * Version of {@link #onCreateView(String, AttributeSet)} that also takes * the future parent of the view being constructed. The default * implementation simply calls {@link #onCreateView(String, AttributeSet)}. * * @param parent * The future parent of the returned view. <em>Note that * this may be null.</em> * @param name * The fully qualified class name of the View to be create. * @param attrs * An AttributeSet of attributes to apply to the View. * * @return View The View created. */ protected View onCreateView(View parent, String name, AttributeSet attrs) throws ClassNotFoundException { return onCreateView(name, attrs); }
两个参数的版本,加个前缀”android.view.”,最后还调回createView.
/** * This routine is responsible for creating the correct subclass of View * given the xml element name. Override it to handle custom view objects. If * you override this in your subclass be sure to call through to * super.onCreateView(name) for names you do not recognize. * * @param name * The fully qualified class name of the View to be create. * @param attrs * An AttributeSet of attributes to apply to the View. * * @return View The View created. */ protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException { return createView(name, "android.view.", attrs); }
LayoutInflater.createView
通过反射去生成View对象。
556 /**557 * Low-level function for instantiating a view by name. This attempts to558 * instantiate a view class of the given <var>name</var> found in this559 * LayoutInflater's ClassLoader.560 *561 * <p>562 * There are two things that can happen in an error case: either the563 * exception describing the error will be thrown, or a null will be564 * returned. You must deal with both possibilities -- the former will happen565 * the first time createView() is called for a class of a particular name,566 * the latter every time there-after for that class name.567 *568 * @param name The full name of the class to be instantiated.569 * @param attrs The XML attributes supplied for this instance.570 *571 * @return View The newly instantiated view, or null.572 */573 public final View createView(String name, String prefix, AttributeSet attrs)574 throws ClassNotFoundException, InflateException {
sConstructorMap是构造方法的缓存,如果有了就用现成的吧。
575 Constructor<? extends View> constructor = sConstructorMap.get(name);576 Class<? extends View> clazz = null;577578 try {579 Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);580581 if (constructor == null) {582 // Class not found in the cache, see if it's real, and try to add it583 clazz = mContext.getClassLoader().loadClass(584 prefix != null ? (prefix + name) : name).asSubclass(View.class);585
如前所述,如果定义了过滤的话,则调用mFilter的onLoadClass判断是否允许,不允许则调用failNotAllowed去抛Exception。
586 if (mFilter != null && clazz != null) {587 boolean allowed = mFilter.onLoadClass(clazz);588 if (!allowed) {589 failNotAllowed(name, prefix, attrs);590 }591 }592 constructor = clazz.getConstructor(mConstructorSignature);593 constructor.setAccessible(true);594 sConstructorMap.put(name, constructor);595 } else {
下面一个分支是能拿到可重用的构造器的情况
596 // If we have a filter, apply it to cached constructor597 if (mFilter != null) {598 // Have we seen this name before?599 Boolean allowedState = mFilterMap.get(name);600 if (allowedState == null) {601 // New class -- remember whether it is allowed602 clazz = mContext.getClassLoader().loadClass(603 prefix != null ? (prefix + name) : name).asSubclass(View.class);604605 boolean allowed = clazz != null && mFilter.onLoadClass(clazz);606 mFilterMap.put(name, allowed);607 if (!allowed) {608 failNotAllowed(name, prefix, attrs);609 }610 } else if (allowedState.equals(Boolean.FALSE)) {611 failNotAllowed(name, prefix, attrs);612 }613 }614 }615616 Object[] args = mConstructorArgs;617 args[1] = attrs;618619 final View view = constructor.newInstance(args);
如果是ViewStub的话,暂时不需要inflate了,但是需要clone一个inflater给它。
620 if (view instanceof ViewStub) {621 // Use the same context when inflating ViewStub later.622 final ViewStub viewStub = (ViewStub) view;623 viewStub.setLayoutInflater(cloneInContext((Context) args[0]));624 }625 return view;626627 } catch (NoSuchMethodException e) {628 InflateException ie = new InflateException(attrs.getPositionDescription()629 + ": Error inflating class "630 + (prefix != null ? (prefix + name) : name));631 ie.initCause(e);632 throw ie;633634 } catch (ClassCastException e) {635 // If loaded class is not a View subclass636 InflateException ie = new InflateException(attrs.getPositionDescription()637 + ": Class is not a View "638 + (prefix != null ? (prefix + name) : name));639 ie.initCause(e);640 throw ie;641 } catch (ClassNotFoundException e) {642 // If loadClass fails, we should propagate the exception.643 throw e;644 } catch (Exception e) {645 InflateException ie = new InflateException(attrs.getPositionDescription()646 + ": Error inflating class "647 + (clazz == null ? "<unknown>" : clazz.getName()));648 ie.initCause(e);649 throw ie;650 } finally {651 Trace.traceEnd(Trace.TRACE_TAG_VIEW);652 }653 }
failNotAllowed
就是个Exception拼字符串的方法。
/** * Throw an exception because the specified class is not allowed to be * inflated. */ private void failNotAllowed(String name, String prefix, AttributeSet attrs) { throw new InflateException(attrs.getPositionDescription() + ": Class not allowed to be inflated " + (prefix != null ? (prefix + name) : name)); }
XmlBlock.Parser
当梦想照进现实,我们看看XmlResourceParser接口的真正实现类XmlBlock.Parser。
定义
77 /*package*/ final class Parser implements XmlResourceParser {78 Parser(long parseState, XmlBlock block) {79 mParseState = parseState;80 mBlock = block;81 block.mOpenCount++;82 }
native函数唱主角
基本上主要的功能都靠native函数来实现
491 private static final native long nativeCreate(byte[] data,492 int offset,493 int size);494 private static final native long nativeGetStringBlock(long obj);495496 private static final native long nativeCreateParseState(long obj);497 /*package*/ static final native int nativeNext(long state);498 private static final native int nativeGetNamespace(long state);499 /*package*/ static final native int nativeGetName(long state);500 private static final native int nativeGetText(long state);501 private static final native int nativeGetLineNumber(long state);502 private static final native int nativeGetAttributeCount(long state);503 private static final native int nativeGetAttributeNamespace(long state, int idx);504 private static final native int nativeGetAttributeName(long state, int idx);505 private static final native int nativeGetAttributeResource(long state, int idx);506 private static final native int nativeGetAttributeDataType(long state, int idx);507 private static final native int nativeGetAttributeData(long state, int idx);508 private static final native int nativeGetAttributeStringValue(long state, int idx);509 private static final native int nativeGetIdAttribute(long state);510 private static final native int nativeGetClassAttribute(long state);511 private static final native int nativeGetStyleAttribute(long state);512 private static final native int nativeGetAttributeIndex(long state, String namespace, String name);513 private static final native void nativeDestroyParseState(long state);514515 private static final native void nativeDestroy(long obj);
这些方法和本地函数的对照表在/frameworks/base/core/jni/android_util_XmlBlock.cpp中,
364/*365 * JNI registration.366 */367static JNINativeMethod gXmlBlockMethods[] = {368 /* name, signature, funcPtr */369 { "nativeCreate", "([BII)J",370 (void*) android_content_XmlBlock_nativeCreate },371 { "nativeGetStringBlock", "(J)J",372 (void*) android_content_XmlBlock_nativeGetStringBlock },373 { "nativeCreateParseState", "(J)J",374 (void*) android_content_XmlBlock_nativeCreateParseState },375 { "nativeNext", "(J)I",376 (void*) android_content_XmlBlock_nativeNext },377 { "nativeGetNamespace", "(J)I",378 (void*) android_content_XmlBlock_nativeGetNamespace },379 { "nativeGetName", "(J)I",380 (void*) android_content_XmlBlock_nativeGetName },381 { "nativeGetText", "(J)I",382 (void*) android_content_XmlBlock_nativeGetText },383 { "nativeGetLineNumber", "(J)I",384 (void*) android_content_XmlBlock_nativeGetLineNumber },385 { "nativeGetAttributeCount", "(J)I",386 (void*) android_content_XmlBlock_nativeGetAttributeCount },387 { "nativeGetAttributeNamespace","(JI)I",388 (void*) android_content_XmlBlock_nativeGetAttributeNamespace },389 { "nativeGetAttributeName", "(JI)I",390 (void*) android_content_XmlBlock_nativeGetAttributeName },391 { "nativeGetAttributeResource", "(JI)I",392 (void*) android_content_XmlBlock_nativeGetAttributeResource },393 { "nativeGetAttributeDataType", "(JI)I",394 (void*) android_content_XmlBlock_nativeGetAttributeDataType },395 { "nativeGetAttributeData", "(JI)I",396 (void*) android_content_XmlBlock_nativeGetAttributeData },397 { "nativeGetAttributeStringValue", "(JI)I",398 (void*) android_content_XmlBlock_nativeGetAttributeStringValue },399 { "nativeGetAttributeIndex", "(JLjava/lang/String;Ljava/lang/String;)I",400 (void*) android_content_XmlBlock_nativeGetAttributeIndex },401 { "nativeGetIdAttribute", "(J)I",402 (void*) android_content_XmlBlock_nativeGetIdAttribute },403 { "nativeGetClassAttribute", "(J)I",404 (void*) android_content_XmlBlock_nativeGetClassAttribute },405 { "nativeGetStyleAttribute", "(J)I",406 (void*) android_content_XmlBlock_nativeGetStyleAttribute },407 { "nativeDestroyParseState", "(J)V",408 (void*) android_content_XmlBlock_nativeDestroyParseState },409 { "nativeDestroy", "(J)V",410 (void*) android_content_XmlBlock_nativeDestroy },411};
我们看几个例子:
getText
getText是涉及到访问资源的,我们先看看这个。
资源ID查找的过程是在nativeGetText本地方法中实现的,如下所示:
141 public String getText() {142 int id = nativeGetText(mParseState);143 return id >= 0 ? mStrings.get(id).toString() : null;144 }
查上面的表,找到对应的函数:
150static jint android_content_XmlBlock_nativeGetText(JNIEnv* env, jobject clazz,151 jlong token)152{153 ResXMLParser* st = reinterpret_cast<ResXMLParser*>(token);154 if (st == NULL) {155 return -1;156 }157158 return static_cast<jint>(st->getTextID());159}
核心功能指向一个C++的类ResXMLParser,我们下面再看这个类的详细定义,先看看getTextID().
1062int32_t ResXMLParser::getTextID() const1063{1064 if (mEventCode == TEXT) {1065 return dtohl(((const ResXMLTree_cdataExt*)mCurExt)->data.index);1066 }1067 return -1;1068}
next
我们再看一个next的实现。
236 public int next() throws XmlPullParserException,IOException {237 if (!mStarted) {238 mStarted = true;239 return START_DOCUMENT;240 }241 if (mParseState == 0) {242 return END_DOCUMENT;243 }244 int ev = nativeNext(mParseState);245 if (mDecNextDepth) {246 mDepth--;247 mDecNextDepth = false;248 }249 switch (ev) {250 case START_TAG:251 mDepth++;252 break;253 case END_TAG:254 mDecNextDepth = true;255 break;256 }257 mEventType = ev;258 if (ev == END_DOCUMENT) {259 // Automatically close the parse when we reach the end of260 // a document, since the standard XmlPullParser interface261 // doesn't have such an API so most clients will leave us262 // dangling.263 close();264 }265 return ev;266 }
基本上处理一下深度等,主要逻辑全靠JNI函数。
94static jint android_content_XmlBlock_nativeNext(JNIEnv* env, jobject clazz,95 jlong token)96{97 ResXMLParser* st = reinterpret_cast<ResXMLParser*>(token);98 if (st == NULL) {99 return ResXMLParser::END_DOCUMENT;100 }101102 do {103 ResXMLParser::event_code_t code = st->next();104 switch (code) {105 case ResXMLParser::START_TAG:106 return 2;107 case ResXMLParser::END_TAG:108 return 3;109 case ResXMLParser::TEXT:110 return 4;111 case ResXMLParser::START_DOCUMENT:112 return 0;113 case ResXMLParser::END_DOCUMENT:114 return 1;115 case ResXMLParser::BAD_DOCUMENT:116 goto bad;117 default:118 break;119 }120 } while (true);121122bad:123 jniThrowException(env, "org/xmlpull/v1/XmlPullParserException",124 "Corrupt XML binary file");125 return ResXMLParser::BAD_DOCUMENT;126}
最终的实现还是靠ResXMLParser:
1034ResXMLParser::event_code_t ResXMLParser::next()1035{1036 if (mEventCode == START_DOCUMENT) {1037 mCurNode = mTree.mRootNode;1038 mCurExt = mTree.mRootExt;1039 return (mEventCode=mTree.mRootCode);1040 } else if (mEventCode >= FIRST_CHUNK_CODE) {1041 return nextNode();1042 }1043 return mEventCode;1044}
ResXMLParser
这个类的定义在/frameworks/base/include/androidfw/ResourceTypes.h中,
680class ResXMLParser681{682public:683 ResXMLParser(const ResXMLTree& tree);684685 enum event_code_t {686 BAD_DOCUMENT = -1,687 START_DOCUMENT = 0,688 END_DOCUMENT = 1,689690 FIRST_CHUNK_CODE = RES_XML_FIRST_CHUNK_TYPE,691692 START_NAMESPACE = RES_XML_START_NAMESPACE_TYPE,693 END_NAMESPACE = RES_XML_END_NAMESPACE_TYPE,694 START_TAG = RES_XML_START_ELEMENT_TYPE,695 END_TAG = RES_XML_END_ELEMENT_TYPE,696 TEXT = RES_XML_CDATA_TYPE697 };698699 struct ResXMLPosition700 {701 event_code_t eventCode;702 const ResXMLTree_node* curNode;703 const void* curExt;704 };705706 void restart();707708 const ResStringPool& getStrings() const;709710 event_code_t getEventType() const;711 // Note, unlike XmlPullParser, the first call to next() will return712 // START_TAG of the first element.713 event_code_t next();714715 // These are available for all nodes:716 int32_t getCommentID() const;717 const char16_t* getComment(size_t* outLen) const;718 uint32_t getLineNumber() const;719720 // This is available for TEXT:721 int32_t getTextID() const;722 const char16_t* getText(size_t* outLen) const;723 ssize_t getTextValue(Res_value* outValue) const;724725 // These are available for START_NAMESPACE and END_NAMESPACE:726 int32_t getNamespacePrefixID() const;727 const char16_t* getNamespacePrefix(size_t* outLen) const;728 int32_t getNamespaceUriID() const;729 const char16_t* getNamespaceUri(size_t* outLen) const;730731 // These are available for START_TAG and END_TAG:732 int32_t getElementNamespaceID() const;733 const char16_t* getElementNamespace(size_t* outLen) const;734 int32_t getElementNameID() const;735 const char16_t* getElementName(size_t* outLen) const;736737 // Remaining methods are for retrieving information about attributes738 // associated with a START_TAG:739740 size_t getAttributeCount() const;741742 // Returns -1 if no namespace, -2 if idx out of range.743 int32_t getAttributeNamespaceID(size_t idx) const;744 const char16_t* getAttributeNamespace(size_t idx, size_t* outLen) const;745746 int32_t getAttributeNameID(size_t idx) const;747 const char16_t* getAttributeName(size_t idx, size_t* outLen) const;748 uint32_t getAttributeNameResID(size_t idx) const;749750 // These will work only if the underlying string pool is UTF-8.751 const char* getAttributeNamespace8(size_t idx, size_t* outLen) const;752 const char* getAttributeName8(size_t idx, size_t* outLen) const;753754 int32_t getAttributeValueStringID(size_t idx) const;755 const char16_t* getAttributeStringValue(size_t idx, size_t* outLen) const;756757 int32_t getAttributeDataType(size_t idx) const;758 int32_t getAttributeData(size_t idx) const;759 ssize_t getAttributeValue(size_t idx, Res_value* outValue) const;760761 ssize_t indexOfAttribute(const char* ns, const char* attr) const;762 ssize_t indexOfAttribute(const char16_t* ns, size_t nsLen,763 const char16_t* attr, size_t attrLen) const;764765 ssize_t indexOfID() const;766 ssize_t indexOfClass() const;767 ssize_t indexOfStyle() const;768769 void getPosition(ResXMLPosition* pos) const;770 void setPosition(const ResXMLPosition& pos);771772private:773 friend class ResXMLTree;774775 event_code_t nextNode();776777 const ResXMLTree& mTree;778 event_code_t mEventCode;779 const ResXMLTree_node* mCurNode;780 const void* mCurExt;781};
不支持的功能
不支持的feature
XmlBlock.Parser只支持两个feature:
* FEATURE_PROCESS_NAMESPACES
* FEATURE_REPORT_NAMESPACE_ATTRIBUTES
DTD是不支持的,也不要提validation了
84 public void setFeature(String name, boolean state) throws XmlPullParserException {85 if (FEATURE_PROCESS_NAMESPACES.equals(name) && state) {86 return;87 }88 if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name) && state) {89 return;90 }91 throw new XmlPullParserException("Unsupported feature: " + name);92 }93 public boolean getFeature(String name) {94 if (FEATURE_PROCESS_NAMESPACES.equals(name)) {95 return true;96 }97 if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name)) {98 return true;99 }100 return false;101 }
属性不支持
setProperty不支持啦~
102 public void setProperty(String name, Object value) throws XmlPullParserException {103 throw new XmlPullParserException("setProperty() not supported");104 }105 public Object getProperty(String name) {106 return null;107 }
不支持定义input
setInput不支持
108 public void setInput(Reader in) throws XmlPullParserException {109 throw new XmlPullParserException("setInput() not supported");110 }111 public void setInput(InputStream inputStream, String inputEncoding) throws XmlPullParserException {112 throw new XmlPullParserException("setInput() not supported");113 }
Entity Replacement Text不支持
114 public void defineEntityReplacementText(String entityName, String replacementText) throws XmlPullParserException {115 throw new XmlPullParserException("defineEntityReplacementText() not supported");116 }
命名空间不支持
117 public String getNamespacePrefix(int pos) throws XmlPullParserException {118 throw new XmlPullParserException("getNamespacePrefix() not supported");119 }120 public String getInputEncoding() {121 return null;122 }123 public String getNamespace(String prefix) {124 throw new RuntimeException("getNamespace() not supported");125 }126 public int getNamespaceCount(int depth) throws XmlPullParserException {127 throw new XmlPullParserException("getNamespaceCount() not supported");128 }
XMLBlock的编译生成 - aapt中的XMLNode
这个过程的实现在/frameworks/base/tools/aapt/XMLNode.cpp中.
例如,下面的函数就是将AaptFile生成前面我们所看到的ResXMLTree对象。
554status_t parseXMLResource(const sp<AaptFile>& file, ResXMLTree* outTree,555 bool stripAll, bool keepComments,556 const char** cDataTags)557{558 sp<XMLNode> root = XMLNode::parse(file);559 if (root == NULL) {560 return UNKNOWN_ERROR;561 }562 root->removeWhitespace(stripAll, cDataTags);563564 if (kIsDebug) {565 printf("Input XML from %s:\n", (const char*)file->getPrintableSource());566 root->print();567 }568 sp<AaptFile> rsc = new AaptFile(String8(), AaptGroupEntry(), String8());569 status_t err = root->flatten(rsc, !keepComments, false);570 if (err != NO_ERROR) {571 return err;572 }573 err = outTree->setTo(rsc->getData(), rsc->getSize(), true);574 if (err != NO_ERROR) {575 return err;576 }577578 if (kIsDebug) {579 printf("Output XML:\n");580 printXMLBlock(outTree);581 }582583 return NO_ERROR;584}
- Android 6.0 inflate过程分析
- Android InflateLayout.inflate()原理分析
- Android Layout inflate分析(2) - ViewGroup
- Android:inflate.inflate()方法
- Android Inflate
- Android inflate
- Android获取到inflate服务的方式及inflate的解析过程
- android中LayoutInflater.from(context).inflate的分析
- android LayoutInflate.inflate源码分析及使用区分
- android中LayoutInflater.from(context).inflate的分析
- android中LayoutInflater.from(context).inflate的分析
- Android Layout Inflate分析(3) - 深入Layout XML属性
- android中LayoutInflater.from(context).inflate的分析
- [读书笔记]Android LayoutInflater.inflate方法参数详解原理分析
- Android LayoutInflater.inflate的使用及源码分析
- 【android Inflate】 Android中inflate简介
- Inflate()---Android之Inflate()方法用途
- 【Android】【Inflate】inflate方法的三个参数
- android 实现漫天飞舞雪花以及下雨天的效果
- Exception:org.eclipse.m2e.wtp.MarkedException: Unable to configure OHBC
- Android中Relativelayout各个属性
- 297. Serialize and Deserialize Binary Tree
- python爬虫获取google镜像
- Android 6.0 inflate过程分析
- Coroutine,你究竟干了什么?(小续)
- NYOJ 1003 MAX SUM
- 20160128.CCPP体系详解(0007天)
- JavaSE019_反射应用之动态代理
- struts2动态方法调用(DMI)
- Android中自定义一个事件监听器
- 【NYOJ】[62]笨小熊
- ITOO4.1之Memcached实践篇