SoFunction
Updated on 2025-04-10

Detailed explanation of the process of converting Android Xml to View

Android layout file Xml

By converting setContentView(@LayoutResint layoutResID) or (context).inflate(int ResID) into Java objects, the preview function provided by the development tool Android Studio can be developed in parallel during the development process, improving development efficiency. The following analysis process is based on the Android API 25 Platform source code and uses the setContentView() method as the entry.

How to convert Xml to Java object

1. The setContentView(@LayoutResint layoutResID) method in the Activity;

This method will be overloaded by each inherited subclass;

2、(Context context).inflate(@LayoutResint resource, ...)。

Generally use Activity

  • 1). .
  • 2). .
  • 3).
  • 4). Other Activity

Start with the setContentView() method in Activity

public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
}

Tracking getWindow() source code

public Window getWindow() {
        return mWindow;
    }

mWindow is initialized in the attach() method

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window) {
        ...
        mWindow = new PhoneWindow(this, window);
        ...
    }

So the implementation class is a class,@hiderepresentPhoneWindowThe source code is hidden in sdk, check(layoutResID)as follows:

@Override
    public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            ();
        }
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = (mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            (layoutResID, mContentParent);
        }
        ();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            ();
        }
        mContentParentExplicitlySet = true;
    }

From the above code, we can find that if there is no transition animation, the execution is

(layoutResID, mContentParent);

In the PhoneWindow constructor, I found the mLayoutInflater object assignment code as follows:

public PhoneWindow(Context context) {
        super(context);
        mLayoutInflater = (context);
    }

So we can draw a conclusion (resId) and finally use (context).inflate(resId, ...).

Check out other activities.and.Discover.No reload(resId)but.Reloaded

@Override
    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
    }

The getDelegate() source code will eventually be called..(resId)

@Override
    public void setContentView(int resId) {
        ensureSubDecor();
        ViewGroup contentParent = (ViewGroup) ();
        ();
        (mContext).inflate(resId, contentParent);
        ();
    }

Therefore, converting xml into Java objects is throughLayoutInflaterofinflate()Method to complete

LayoutInflater object acquisition method

KeywordsabstractLayoutInflateris an abstract class that cannot be instantiated.LayoutInflaterThe methods for obtaining objects are:

1). Get it in Activity via getLayoutInflater()

2). Get the static method from(context) in LayoutInflater

3). (Context.LAYOUT_INFLATER_SERVICE) Get

As in ActivitygetLayoutInflater()

 /**
     * Convenience for calling
     * {@link #getLayoutInflater}.
     */
    @NonNull
    public LayoutInflater getLayoutInflater() {
        return getWindow().getLayoutInflater();
    }

It can be seen that the Activity gets the mLayoutInflater of PhoneWindow through getLayoutInflater().

(context)

   /**
     * Obtains the LayoutInflater from the given context.
     */
    public static LayoutInflater from(Context context) {
        LayoutInflater LayoutInflater =
                (LayoutInflater) (Context.LAYOUT_INFLATER_SERVICE);
        if (LayoutInflater == null) {
            throw new AssertionError("LayoutInflater not found.");
        }
        return LayoutInflater;
    }

Therefore, the LayoutInflater object is obtained through the service.

Track the source code (Context.LAYOUT_INFLATER_SERVICE);
ContextThe implementation class is,like:

   @Override
    public Object getSystemService(String name) {
        return (this, name);
    }

track

  /**
     * Gets a system service from a given context.
     */
    public static Object getSystemService(ContextImpl ctx, String name) {
        ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
        return fetcher != null ? (ctx) : null;
    }
   /**
     * Statically registers a system service with the context.
     * This method must be called during static initialization only.
     */
    private static <T> void registerService(String serviceName, Class<T> serviceClass,
            ServiceFetcher<T> serviceFetcher) {
        SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
        SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
    }

In the SystemServiceRegistry class, only various system services are registered here. The registration code place is found through Context.LAYOUT_INFLATER_SERVICE, as follows:

 registerService(Context.LAYOUT_INFLATER_SERVICE, ,
                new CachedServiceFetcher<LayoutInflater>() {
            @Override
            public LayoutInflater createService(ContextImpl ctx) {
                return new PhoneLayoutInflater(());
 }});

Through the above code, I foundLayoutInflaterThe implementation class isPhoneLayoutInflater

LayoutInflater Reads the Xml file and creates a View object, and continues to track the() method

1).View inflate(@LayoutRes int resource, @Nullable ViewGroup root)

2).View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) 

Focus on the second method, the code is as follows:

   /**
     * Inflate a new view hierarchy from the specified xml resource. Throws
     * {@link InflateException} if there is an error.
     * 
     * @param resource ID for an XML layout resource to load (.,
     *        <code>.main_page</code>)
     * @param root Optional view to be the parent of the generated hierarchy (if
     *        <em>attachToRoot</em> is true), or else simply an object that
     *        provides a set of LayoutParams values for root of the returned
     *        hierarchy (if <em>attachToRoot</em> is false.)
     * @param attachToRoot Whether the inflated hierarchy should be attached to
     *        the root parameter? If false, root is only used to create the
     *        correct subclass of LayoutParams for the root view in the XML.
     * @return The root View of the inflated hierarchy. If root was supplied and
     *         attachToRoot is true, this is root; otherwise it is the root of
     *         the inflated XML file.
     */
    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        if (DEBUG) {
            (TAG, "INFLATING from resource: \"" + (resource) + "\" ("
                    + (resource) + ")");
        }
        final XmlResourceParser parser = (resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            ();
        }
    }

According to the above code logic, first passresourceThe object converts the xml file pointed to by resId intoXmlResourceParser, and then executeinflate(parser, root, attachToRoot)The core code is as follows:

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            (Trace.TRACE_TAG_VIEW, "inflate");
            final Context inflaterContext = mContext;
            final AttributeSet attrs = (parser);
            Context lastContext = (Context) mConstructorArgs[0];
            mConstructorArgs[0] = inflaterContext;
            View result = root;
            try {
                // Look for the root node.
                ...
                final String name = ();
                //Analysis 1                if (TAG_MERGE.equals(name)) {
                    if (root == null || !attachToRoot) {
                        throw new InflateException("&lt;merge /&gt; can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }
                    //Analysis 2                    rInflate(parser, root, inflaterContext, attrs, false);
                } else {
                    //Analysis 3                    // Temp is the root view that was found in the xml
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);
                     params = null;
                    if (root != null) {                   
                        // Create layout params that match root, if supplied
                        params = (attrs);
                        if (!attachToRoot) {
                            // Set the layout params for temp if we are not
                            // attaching. (If we are, we use addView, below)
                            (params);
                        }
                    }
                    //Analysis 4                    // Inflate all children under temp against its context.
                    rInflateChildren(parser, temp, attrs, true);
                    // We are supposed to attach all the views we found (int temp)
                    // to root. Do that now.
                    if (root != null &amp;&amp; attachToRoot) {
                        (temp, params);
                    }
                    // Decide whether to return the root that was passed in or the
                    // top view found in xml.
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }
            //Exception handling part            return result;
        }
    }

analyze

Analysis 1:

If the Xml root tag isTAG_MERGE (i.e. merge), then root cannot be empty, attachToRoot is true, and it is executedrInflate(parser, root, inflaterContext, attrs, false)

Analysis 2 rInflate() method

void rInflate(XmlPullParser parser, View parent, Context context,
            AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
        final int depth = ();
        int type;
        while (((type = ()) != XmlPullParser.END_TAG ||
                () > depth) && type != XmlPullParser.END_DOCUMENT) {
            if (type != XmlPullParser.START_TAG) {
                continue;
            }
            final String name = ();
            if (TAG_REQUEST_FOCUS.equals(name)) {
                parseRequestFocus(parser, parent);
            } else if (TAG_TAG.equals(name)) {
                parseViewTag(parser, parent, attrs);
            } else if (TAG_INCLUDE.equals(name)) {
                if (() == 0) {
                    throw new InflateException("<include /> cannot be the root element");
                }
                parseInclude(parser, context, parent, attrs);
            } else if (TAG_MERGE.equals(name)) {
                throw new InflateException("<merge /> must be the root element");
            } else {
                final View view = createViewFromTag(parent, name, context, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final  params = (attrs);
                rInflateChildren(parser, view, attrs, true);
                (view, params);
            }
        }
        if (finishInflate) {
            ();
        }
    }

rInflate(parser, root, inflaterContext, attrs, false) is summarized as follows

1). While traversing the child nodes of the node

2). The child nodes include "requestFocus", "tag", "", "include"

3). The child node cannot be "merge"

4). Other situations of child nodes are the tags of various Views

5). View tags and "include" tags will create View objects

6). Execute after the traversal is completed ()

If the child node is include, the source code of parseInclude() is similar to inflate(parser, root, attachToRoot), both read the file corresponding to xml, convert it into XmlResourceParser and then traverse the tags in it.

createViewFromTag(parent, name, context, attrs)Responsible for creating View objects

Analysis 3, 4

1). Only if root is not null, the params attributes of xml and layout will be read;

2). attachToRoot is True, and the return is a root object. Otherwise, the view specified by the root tag created by xml is returned.

3). The createViewFromTag(root, name, inflaterContext, attrs) method is called to create View

4). rInflateChildren()->rInflate(); is the same as Analysis 2

In summary,()Create a View object, the source code is as follows:

View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
        if (("view")) {
            name = (null, "class");
        }
        // Apply a theme wrapper, if allowed and one is specified.
        if (!ignoreThemeAttr) {
            final TypedArray ta = (attrs, ATTRS_THEME);
            final int themeResId = (0, 0);
            if (themeResId != 0) {
                context = new ContextThemeWrapper(context, themeResId);
            }
            ();
        }
        if ((TAG_1995)) {
            // Let's party like it's 1995!
            return new BlinkLayout(context, attrs);
        }
        try {
            View view;
            if (mFactory2 != null) {
                view = (parent, name, context, attrs);
            } else if (mFactory != null) {
                view = (name, context, attrs);
            } else {
                view = null;
            }
            if (view == null &amp;&amp; mPrivateFactory != null) {
                view = (parent, name, context, attrs);
            }
            if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    if (-1 == ('.')) {
                        view = onCreateView(parent, name, attrs);
                    } else {
                        view = createView(name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }
            return view;
       //Exception handling       ...
    }

mFactory2mFactorymPrivateFactoryIt seems that all three objects can be created. For these three objects are null or empty implementations, just look at the following code to create a View object:

               final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    if (-1 == ('.')) {
                        view = onCreateView(parent, name, attrs);
                    } else {
                        //Customize                        view = createView(name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }

Note: IfnameThe attribute contains.It means that this is a custom View, and the system comes with a View. We can omit the path to the class, while the custom View cannot be omitted.

Custom View creation, the core code is as follows:

public final View createView(String name, String prefix, AttributeSet attrs)
            throws ClassNotFoundException, InflateException {
        Constructor&lt;? extends View&gt; constructor = (name);
        if (constructor != null &amp;&amp; !verifyClassLoader(constructor)) {
            constructor = null;
            (name);
        }
        Class&lt;? extends View&gt; clazz = null;
        try {
            (Trace.TRACE_TAG_VIEW, name);
            if (constructor == null) {
                // Class not found in the cache, see if it's real, and try to add it
                clazz = ().loadClass(
                        prefix != null ? (prefix + name) : name).asSubclass();
                if (mFilter != null &amp;&amp; clazz != null) {
                    boolean allowed = (clazz);
                    if (!allowed) {
                        failNotAllowed(name, prefix, attrs);
                    }
                }
                constructor = (mConstructorSignature);
                (true);
                (name, constructor);
            } else {
                // If we have a filter, apply it to cached constructor
                if (mFilter != null) {
                    // Have we seen this name before?
                    Boolean allowedState = (name);
                    if (allowedState == null) {
                        // New class -- remember whether it is allowed
                        clazz = ().loadClass(
                                prefix != null ? (prefix + name) : name).asSubclass();
                        boolean allowed = clazz != null &amp;&amp; (clazz);
                        (name, allowed);
                        if (!allowed) {
                            failNotAllowed(name, prefix, attrs);
                        }
                    } else if (()) {
                        failNotAllowed(name, prefix, attrs);
                    }
                }
            }
            Object[] args = mConstructorArgs;
            args[1] = attrs;
            final View view = (args);
            if (view instanceof ViewStub) {
                // Use the same context when inflating ViewStub later.
                final ViewStub viewStub = (ViewStub) view;
                (cloneInContext((Context) args[0]));
            }
            return view;
        //Exception handling        ......
    }

You can see that the above code(args), create View objects through reflection

For the various built-in Views in LayoutInflater implementation classes for AndroidPhoneLayoutInflaterReloadedonCreateView()method

private static final String[] sClassPrefixList = {
        ".",
        ".",
        "."
    };
@Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
        for (String prefix : sClassPrefixList) {
            try {
                View view = createView(name, prefix, attrs);
                if (view != null) {
                    return view;
                }
            } catch (ClassNotFoundException e) {
                // In this case we want to let the base class take a crack
                // at it.
            }
        }
        return (name, attrs);
    }

The code in LayoutInflater is as follows:

protected View onCreateView(View parent, String name, AttributeSet attrs)
            throws ClassNotFoundException {
        return onCreateView(name, attrs);
    }
protected View onCreateView(String name, AttributeSet attrs)
            throws ClassNotFoundException {
        return createView(name, ".", attrs);
    }

For the built-in Views of the system, the View will be added in front of the labels in turn....Then create a View through reflection.

The above is the detailed explanation of the process of converting Android Xml to View. For more information about converting Android Xml to View, please follow my other related articles!