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结束了,处理处理扔到容器里吧。

  1. 构建XmlPullParserFactory的实例. 放在try…catch中是因为有XmlPullParserException异常要处理。
        try {            XmlPullParserFactory pullParserFactory = XmlPullParserFactory                    .newInstance();
  1. 获取XmlPullParser的实例
            XmlPullParser xmlPullParser = pullParserFactory.newPullParser();
  1. 设置输入流 xml文件
            xmlPullParser.setInput(                    context.getResources().openRawResource(R.raw.xml文件名),                    "UTF-8");
  1. 开始解析
            int eventType = xmlPullParser.getEventType();
  1. 主循环, 直至遇到文档结束事件XmlPullParser.END_DOCUMENT
            try {                while (eventType != XmlPullParser.END_DOCUMENT) {
  1. XmlPullParser.START_DOCUMENT事件时,处理文档开始。此处可以准备一个数据结构存储等。
                    String nodeName = xmlPullParser.getName();                    switch (eventType) {                    case XmlPullParser.START_DOCUMENT:                        // 处理文档开始                        break;
  1. 处理节点开始事件
                    case XmlPullParser.START_TAG:                        // 此处开始一个节点,可以读取下面的属性和值                        break;
  1. 处理节点结束事件,比如可以将对象加入到容器中。
                    case XmlPullParser.END_TAG:                        // 结束节点                        break;                    default:                        break;                    }
  1. 读取下一个事件
                    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>&nbsp;&nbsp;&nbsp;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}
0 0