前言:
Android布局文件Xml,通过setContentView(@LayoutResint layoutResID)大概LayoutInflater.from(context).inflate(int ResID)转换为Java对象,开辟工具Android Studio 提供的预览功能,开辟过程中界面和业务可以并行开辟,进步了开辟服从。以下分析过程是基于 Android API 25 Platform 源码,并以setContentView()方法为入口。
Xml 转成 Java 对象方式
1、Activity中setContentView(@LayoutResint layoutResID)方法;该方法都会被每个继承 android.app.Activity 的子类重载;
2、LayoutInflater.from(Context context).inflate(@LayoutResint resource, ...)。
一样平常利用的 Activity 大概是
1). android.support.v7.app.AppCompatActivity2). android.support.v4.app.FragmentActivity3). android.app.Activity4). 其他 Activity从Activity中setContentView()方法开始
public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar();}跟踪下getWindow()源码:
public Window getWindow() { return mWindow; }mWindow在Activity.java中attach()方法里初始化
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); ... }以是Window.java的实现类是PhoneWindow.java类,@hide代表 PhoneWindow 的源码在 sdk 内里是隐蔽的,查察 PhoneWindow.setContentView(layoutResID)如下:
@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)) { mContentParent.removeAllViews(); } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext()); transitionTo(newScene); } else { mLayoutInflater.inflate(layoutResID, mContentParent); } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } mContentParentExplicitlySet = true; }从上面代码可以发现假如没有转场动画时,实验的是
mLayoutInflater.inflate(layoutResID, mContentParent);在PhoneWindow构造函数里发现mLayoutInflater对象赋值代码如下:
public PhoneWindow(Context context) { super(context); mLayoutInflater = LayoutInflater.from(context); }以是可以得出一个结论 Activity.setContentView(resId) 终极还是利用LayoutInflater.from(context).inflate(resId, ……)。
在看下其他activity android.support.v7.app.AppCompatActivity和android.support.v4.app.FragmentActivity 发现 android.support.v4.app.FragmentActivity 没有重载 android.app.Activity.setContentView(resId) 但是 android.support.v7.app.AppCompatActivity 重载了
@Override public void setContentView(@LayoutRes int layoutResID) { getDelegate().setContentView(layoutResID); }getDelegate()源代码终极会调用到 android.support.v7.app.AppCompatDelegateImplV9.setContentView(resId)
@Override public void setContentView(int resId) { ensureSubDecor(); ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content); contentParent.removeAllViews(); LayoutInflater.from(mContext).inflate(resId, contentParent); mOriginalWindowCallback.onContentChanged(); }因此xml 转成 Java 对象是通过LayoutInflater的inflate()方法来完成的
关键字abstract,LayoutInflater是一个抽象类,不能实例化,LayoutInflater 对象获取的方式有:
1). 在 Activity 中通过 getLayoutInflater() 获取2). LayoutInflater里静态方法from(context) 获取3). context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) 获取如 Activity 的 getLayoutInflater()
/** * Convenience for calling * {@link android.view.Window#getLayoutInflater}. */ @NonNull public LayoutInflater getLayoutInflater() { return getWindow().getLayoutInflater(); }可以看出 Activity 通过 getLayoutInflater() 获取的是 PhoneWindow 的 mLayoutInflater。
LayoutInflater.from(context)
/** * Obtains the LayoutInflater from the given context. */ public static LayoutInflater from(Context context) { LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); if (LayoutInflater == null) { throw new AssertionError("LayoutInflater not found."); } return LayoutInflater; }以是LayoutInflater对象都是通过服务获取 LayoutInflater 实例对象
跟踪下源码context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Context的实现类是ContextImpl.java,如:
@Override public Object getSystemService(String name) { return SystemServiceRegistry.getSystemService(this, name); }继承跟踪 SystemServiceRegistry.java
/** * 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 ? fetcher.getService(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); }在 SystemServiceRegistry 类,这里只注册各种体系服务的处,通过 Context.LAYOUT_INFLATER_SERVICE找到注册代码地方,如下:
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class, new CachedServiceFetcher<LayoutInflater>() { @Override public LayoutInflater createService(ContextImpl ctx) { return new PhoneLayoutInflater(ctx.getOuterContext()); }});通过以上代码发现 LayoutInflater 的实现类是 PhoneLayoutInflater
LayoutInflater 读取 Xml 文件并创建 View 对象,继承跟踪LayoutInflater.inflate()方法
1).View inflate(@LayoutRes int resource, @Nullable ViewGroup root)2).View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) 重点看第二个方法,代码如下:
/** * 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 (e.g., * <code>R.layout.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) { Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" (" + Integer.toHexString(resource) + ")"); } final XmlResourceParser parser = res.getLayout(resource); try { return inflate(parser, root, attachToRoot); } finally { parser.close(); } }根据以上代码逻辑,起首通过 resource 对象把 resId 指向的 xml 文件转换为XmlResourceParser,然后实验inflate(parser, root, attachToRoot)方法,焦点代码如下:
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate"); final Context inflaterContext = mContext; final AttributeSet attrs = Xml.asAttributeSet(parser); Context lastContext = (Context) mConstructorArgs[0]; mConstructorArgs[0] = inflaterContext; View result = root; try { // Look for the root node. ... final String name = parser.getName(); //分析1 if (TAG_MERGE.equals(name)) { if (root == null || !attachToRoot) { throw new InflateException("<merge /> can be used only with a valid " + "ViewGroup root and attachToRoot=true"); } //分析2 rInflate(parser, root, inflaterContext, attrs, false); } else { //分析3 // Temp is the root view that was found in the xml final View temp = createViewFromTag(root, name, inflaterContext, attrs); ViewGroup.LayoutParams params = null; if (root != null) { // Create layout params that match root, if supplied params = root.generateLayoutParams(attrs); if (!attachToRoot) { // Set the layout params for temp if we are not // attaching. (If we are, we use addView, below) temp.setLayoutParams(params); } } //分析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 && attachToRoot) { root.addView(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; } } //非常处理处罚部分 return result; } }分析1: