知其然,更需知其所以然,让我们开启源码解析之旅.
文章目录
LayoutInflater的使用场景
LayoutInflater的由来
LayoutInflater.inflater()方法源码解析
总结
通常在日常开发中下面几个地方会使用到LayoutInflater
Fragment的onCreateView()方法中需要通过LayoutInflate.inflate方法填充布局,该LayoutInflater对象是Fragment方法中传递进来的
@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {return inflater.inflate(layoutId, container, false);}
在适配器Adapter的getView()方法中使用到LayoutInflater填充布局
View view = LayoutInflater.from(mContext).inflate(layoutId, parent, false);
还有一种情况是在Activity中需要获取到一个布局
View view = LayoutInflater.from(this).inflate(layoutId, null);
接下来我们看看LayoutInflater.form()这个静态方法的实现内幕
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;}
发现主要方法是通过上下文context调用获取系统服务getSystemService通过传入固定参数获取
通过源码注释可以知道,调用该方法可以根据传入的名称,获取到不同的系统级服务.具体包含的系统级服务有:
** <dl>* <dt> {@link #WINDOW_SERVICE} ("window")* <dd> The top-level window manager in which you can place custom* windows. The returned object is a {@link android.view.WindowManager}.* <dt> {@link #LAYOUT_INFLATER_SERVICE} ("layout_inflater")* <dd> A {@link android.view.LayoutInflater} for inflating layout resources* in this context.* <dt> {@link #ACTIVITY_SERVICE} ("activity")* <dd> A {@link android.app.ActivityManager} for interacting with the* global activity state of the system.* <dt> {@link #POWER_SERVICE} ("power")* <dd> A {@link android.os.PowerManager} for controlling power* management.* <dt> {@link #ALARM_SERVICE} ("alarm")* <dd> A {@link android.app.AlarmManager} for receiving intents at the* time of your choosing.* <dt> {@link #NOTIFICATION_SERVICE} ("notification")* <dd> A {@link android.app.NotificationManager} for informing the user* of background events.* <dt> {@link #KEYGUARD_SERVICE} ("keyguard")* <dd> A {@link android.app.KeyguardManager} for controlling keyguard.* <dt> {@link #LOCATION_SERVICE} ("location")* <dd> A {@link android.location.LocationManager} for controlling location* (e.g., GPS) updates.* <dt> {@link #SEARCH_SERVICE} ("search")* <dd> A {@link android.app.SearchManager} for handling search.* <dt> {@link #VIBRATOR_SERVICE} ("vibrator")* <dd> A {@link android.os.Vibrator} for interacting with the vibrator* hardware.* <dt> {@link #CONNECTIVITY_SERVICE} ("connection")* <dd> A {@link android.net.ConnectivityManager ConnectivityManager} for* handling management of network connections.* <dt> {@link #WIFI_SERVICE} ("wifi")* <dd> A {@link android.net.wifi.WifiManager WifiManager} for management of Wi-Fi* connectivity. On releases before NYC, it should only be obtained from an application* context, and not from any other derived context to avoid memory leaks within the calling* process.* <dt> {@link #WIFI_P2P_SERVICE} ("wifip2p")* <dd> A {@link android.net.wifi.p2p.WifiP2pManager WifiP2pManager} for management of* Wi-Fi Direct connectivity.* <dt> {@link #INPUT_METHOD_SERVICE} ("input_method")* <dd> An {@link android.view.inputmethod.InputMethodManager InputMethodManager}* for management of input methods.* <dt> {@link #UI_MODE_SERVICE} ("uimode")* <dd> An {@link android.app.UiModeManager} for controlling UI modes.* <dt> {@link #DOWNLOAD_SERVICE} ("download")* <dd> A {@link android.app.DownloadManager} for requesting HTTP downloads* <dt> {@link #BATTERY_SERVICE} ("batterymanager")* <dd> A {@link android.os.BatteryManager} for managing battery state* <dt> {@link #JOB_SCHEDULER_SERVICE} ("taskmanager")* <dd> A {@link android.app.job.JobScheduler} for managing scheduled tasks* <dt> {@link #NETWORK_STATS_SERVICE} ("netstats")* <dd> A {@link android.app.usage.NetworkStatsManager NetworkStatsManager} for querying network* usage statistics.* <dt> {@link #HARDWARE_PROPERTIES_SERVICE} ("hardware_properties")* <dd> A {@link android.os.HardwarePropertiesManager} for accessing hardware properties.* </dl>*
/*** Return the handle to a system-level service by name. The class of the* returned object varies by the requested name. Currently available names* are:*/public abstract Object getSystemService(@ServiceName @NonNull String name);
到Context这一步我们发现getSystemService()是抽象方法,那他的具体实现类有哪些呢?
==>我们可以通过Context类继承关系来查看,发现一个应用Context的总个数为Activity个数+Service个数+1
而一个Activity的入口是ActivityThread的main()方法,在该方法中会new一个ActivityThread实例,启动主线程的Looper消息轮询,创建新的Activity和Context对象,并将Context对象传递给Activity.
ActivityThread.main()方法
public static void main(String[] args) {//事件跟踪,使用Systrace工具通过打标记tag来收集后写入内部缓存,与后面的调用方法Trance.traceEnd()成对出现Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");//启动性能分析SamplingProfilerIntegration.start();// CloseGuard defaults to true and can be quite spammy. We// disable it here, but selectively enable it later (via// StrictMode) on debug builds, but using DropBox, not logs.CloseGuard.setEnabled(false);Environment.initForCurrentUser();// Set the reporter for event logging in libcoreEventLogger.setReporter(new EventLoggingReporter());// Make sure TrustedCertificateStore looks in the right place for CA certificatesfinal File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());TrustedCertificateStore.setDefaultUserDirectory(configDir);Process.setArgV0("<pre-initialized>");//主线程消息轮询Looper.prepareMainLooper();//创建ActivityThread对象ActivityThread thread = new ActivityThread();thread.attach(false);if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}if (false) {Looper.myLooper().setMessageLogging(newLogPrinter(Log.DEBUG, "ActivityThread"));}// End of event ActivityThreadMain.Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);//开启轮询Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");}
再看thread.attach(false)方法
private void attach(boolean system) {sCurrentActivityThread = this;mSystemThread = system;//不是系统应用的情况if (!system) {ViewRootImpl.addFirstDrawHandler(new Runnable() {@Overridepublic void run() {ensureJitEnabled();}});android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",UserHandle.myUserId());RuntimeInit.setApplicationObject(mAppThread.asBinder());//通过Binder机制与AMS通信,并与ApplicaitonThread关联final IActivityManager mgr = ActivityManagerNative.getDefault();try {mgr.attachApplication(mAppThread);} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}// ...代码省略}
最后通过Handle的消息处理机制调用handleLaunchActivity()方法->接着调用performLaunchActivity()方法
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {// ...Activity a = performLaunchActivity(r, customIntent);// ...}
performLauncherActivity()
Activity的主要步骤在代码中已标明,接着我们看第三步创建上下文的方法
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {//需要的属性信息可以通过封装类ActivityClientRecord获取// System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");ActivityInfo aInfo = r.activityInfo;if (r.packageInfo == null) {//package信息r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,Context.CONTEXT_INCLUDE_CODE);}//组件信息ComponentName component = r.intent.getComponent();if (component == null) {component = r.intent.resolveActivity(mInitialApplication.getPackageManager());r.intent.setComponent(component);}if (r.activityInfo.targetActivity != null) {component = new ComponentName(r.activityInfo.packageName,r.activityInfo.targetActivity);}Activity activity = null;try {//拿到类加载器java.lang.ClassLoader cl = r.packageInfo.getClassLoader();//1.创建activityactivity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);StrictMode.incrementExpectedActivityCount(activity.getClass());r.intent.setExtrasClassLoader(cl);r.intent.prepareToEnterProcess();if (r.state != null) {r.state.setClassLoader(cl);}} catch (Exception e) {if (!mInstrumentation.onException(activity, e)) {throw new RuntimeException("Unable to instantiate activity " + component+ ": " + e.toString(), e);}}try {//2.创建Application对象Application app = r.packageInfo.makeApplication(false, mInstrumentation);if (localLOGV) Slog.v(TAG, "Performing launch of " + r);if (localLOGV) Slog.v(TAG, r + ": app=" + app+ ", appName=" + app.getPackageName()+ ", pkg=" + r.packageInfo.getPackageName()+ ", comp=" + r.intent.getComponent().toShortString()+ ", dir=" + r.packageInfo.getAppDir());if (activity != null) {//3.创建上下文Context对象Context appContext = createBaseContextForActivity(r, activity);CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());Configuration config = new Configuration(mCompatConfiguration);if (r.overrideConfig != null) {config.updateFrom(r.overrideConfig);}if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "+ r.activityInfo.name + " with config " + config);Window window = null;if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {window = r.mPendingRemoveWindow;r.mPendingRemoveWindow = null;r.mPendingRemoveWindowManager = null;}//4.将上下文appContex等对象attach到activity中activity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstances, config,r.referrer, r.voiceInteractor, window);if (customIntent != null) {activity.mIntent = customIntent;}r.lastNonConfigurationInstances = null;activity.mStartedActivity = false;int theme = r.activityInfo.getThemeResource();if (theme != 0) {activity.setTheme(theme);}activity.mCalled = false;//根据Activity是否进行过持久化,调用不同的onCreate()方法if (r.isPersistable()) {mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);} else {mInstrumentation.callActivityOnCreate(activity, r.state);}//......return activity;}
createBaseContextForActivity()
private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {int displayId = Display.DEFAULT_DISPLAY;try {displayId = ActivityManagerNative.getDefault().getActivityDisplayId(r.token);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}//到这步我们确认我们使用的上下文的实现类是ContextImplContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token, displayId, r.overrideConfig);appContext.setOuterContext(activity);Context baseContext = appContext;//...return baseContext;}
找到了上下文的实现类ContextImpl,继续跟踪getSystemService()方法
发现真正的实现类是SystemServiceRegistry.getSystemService()
@Overridepublic Object getSystemService(String name) {return SystemServiceRegistry.getSystemService(this, name);}
接着跟踪SystemServiceRegistry的实现
/*** 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;}
第六行代码这里出现了一个属性SYSTEM_SERVICE_FETCHERS,我们看下这个属性的实现
//首先发现他是一个HashMap对象private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS = new HashMap<String, ServiceFetcher<?>>();
既然是HasnMap对象那就一定实现了put(),和get()方法
发现在registerService()方法中实现,再接着看该方法
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);}
registerService()-->发现该方法在静态代码块中调用,其中就包括我们我们需要找的LAYOUT_INFLATE_SERVICE,并且返回的是LayoutInfalter的子类PhoneLayoutInflater,这里面使用到HashMap用来保存各种系统级服务的对象,是为了保证这些服务对象实例的单一性,也是单例模的一种实现.
static {registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,new CachedServiceFetcher<AccessibilityManager>() {@Overridepublic AccessibilityManager createService(ContextImpl ctx) {return AccessibilityManager.getInstance(ctx);}});//..........registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,new CachedServiceFetcher<LayoutInflater>() {@Overridepublic LayoutInflater createService(ContextImpl ctx) {return new PhoneLayoutInflater(ctx.getOuterContext());}});//..........}
到这里我们把LayoutInflater的使用场景和LayoutInflater怎么来的问题搞清楚了,下面我们一起来看看inflate方法的实现,是怎么将xml文件控件加载到界面中
LayoutInflate.inflate():解析xml布局界面是如何展示出来的?
inflate有两个重载函数,分别是:
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) + ")");}//根据布局id,拿到xml资源解析器final XmlResourceParser parser = res.getLayout(resource);try {//实际调用infalte方法return inflate(parser, root, attachToRoot);} finally {//注意关闭资源parser.close();}}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {//内部调用重载函数,第三个参数根据是否传入根布局确定return inflate(resource, root, root != null);}
第一个方法内部调用第二个方法:其中的参数分别代表
resource:xml资源文件
root:代表传入的根布局
attachToRoot:意思是否将该xml布局绑定到根布局root中,如果是的话,会使用根布局的属性信息
接着跟踪核心方法infalte,通过解析xml文件的节点得到最终想要的View控件
该方法处理解析文件的主要业务逻辑:
/***参数解析: parser为xml文件解析器root为根布局attachToRoot标示是否attach到根布局root中*/public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {synchronized (mConstructorArgs) {//日志跟踪
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");//拿到构造函数中传入的上下文mContextfinal Context inflaterContext = mContext;//拿到属性信息final AttributeSet attrs = Xml.asAttributeSet(parser);Context lastContext = (Context) mConstructorArgs[0];mConstructorArgs[0] = inflaterContext;//默认返回的result为根布局View result = root;try {// 查找根节点int type;while ((type = parser.next()) != XmlPullParser.START_TAG &&type != XmlPullParser.END_DOCUMENT) {// Empty}if (type != XmlPullParser.START_TAG) {throw new InflateException(parser.getPositionDescription()+ ": No start tag found!");}//拿到根节点名称final String name = parser.getName();if (DEBUG) {System.out.println("**************************");System.out.println("Creating root view: "+ name);System.out.println("**************************");}if (TAG_MERGE.equals(name)) {//1.根结点为merge ==> 则根布局root必须不为空,且必须绑定到跟布局中,(attachToRoot为true)if (root == null || !attachToRoot) {throw new InflateException("<merge /> can be used only with a valid "+ "ViewGroup root and attachToRoot=true");}//解析merge内部包含的子控件rInflate(parser, root, inflaterContext, attrs, false);} else {// 2.从xml文件中解析到顶层控件temp->后面会单独详细讲解final View temp = createViewFromTag(root, name, inflaterContext, attrs);ViewGroup.LayoutParams params = null;if (root != null) {if (DEBUG) {System.out.println("Creating params from root: " +root);}//3.拿到root属性信息params = root.generateLayoutParams(attrs);if (!attachToRoot) {// attachToRoot为false时将从根布局获取的属性信息设置给temp,如果attachToRoot为true,则直接调用root.addView()temp.setLayoutParams(params);}}if (DEBUG) {System.out.println("-----> start inflating children");}//递归解析xml布局文件中temp包含的子控件rInflateChildren(parser, temp, attrs, true);if (DEBUG) {System.out.println("-----> done inflating children");}// 4.attachToRoot为true直接调用addView()添加,将temp绑定到root下,root作为temp的父控件if (root != null && attachToRoot) {root.addView(temp, params);}// 5.根布局root为null或者attachToRoot为false不绑定到root中,返回的temp(xml文件中解析得到的控件)//其他情况返回的是根布局root
if (root == null || !attachToRoot) {result = temp;}}} catch (XmlPullParserException e) {final InflateException ie = new InflateException(e.getMessage(), e);ie.setStackTrace(EMPTY_STACK_TRACE);throw ie;} catch (Exception e) {final InflateException ie = new InflateException(parser.getPositionDescription()+ ": " + e.getMessage(), e);ie.setStackTrace(EMPTY_STACK_TRACE);throw ie;} finally {// Don't retain static reference on context.mConstructorArgs[0] = lastContext;mConstructorArgs[1] = null;Trace.traceEnd(Trace.TRACE_TAG_VIEW);}return result;}}
1.先解析拿到xml布局文件中顶层控件(注意和根布局root区分)名称name
2.如果name为merge,则判断root一定不能为空,且attachToRoot必须为true,之后调用rInflate方法将merger标签包含的控件全部添加到根布局root中,并且最后返回的是根布局root.
3.接下来的情况就是name不是merge,通过createViewFromTag()方法获取到xml布局文件的顶层控件temp
a.接着拿到根布局root的属性信息params,如果attachToRoot为false,则将根布局的属性信息设置给顶层控件temp
b.接着递归调用rInflateChildren()方法解析拿到顶层控件temp下的子控件
c.接着判断如果root不为空,且attachToRoot为true,则直接调用根布局root的addView()方法,表示该xml控件的顶层控件temp将以根布局root为父控件,并且最后返回的是根布局root.
d.如果root为空,或者attachToRoot为false,则最后返回的是xml文件的顶层控件temp.
接下来查看createViewFromTag()方法:该方法会根据xml文件的节点名称真正来解析得到控件
createViewFromTag()主要是判断xml文件的控件是使用的是系统控件还是自定义控件,判断依据是控件名称是否包含点,因为系统控件都是在android.view.包名下,系统会为我们自动添加,后面会看到,而自定义控件需要添加全包名.
/*** 参数信息:parent为根布局,name为控件名称(例如TextView),attrs在创建view需要用到*View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,boolean ignoreThemeAttr) {if (name.equals("view")) {name = attrs.getAttributeValue(null, "class");}// Apply a theme wrapper, if allowed and one is specified.if (!ignoreThemeAttr) {final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);final int themeResId = ta.getResourceId(0, 0);if (themeResId != 0) {context = new ContextThemeWrapper(context, themeResId);}ta.recycle();}//直接返回BlinkLayoutif (name.equals(TAG_1995)) {// Let's party like it's 1995!return new BlinkLayout(context, attrs);}try {View view;//因为我们是以new PhoneLayoutInflate(Context ctx)方法创建LayoutInflate实例的,所以mFactory,mFactory2和mPrivateFactory皆为nullif (mFactory2 != null) {view = mFactory2.onCreateView(parent, name, context, attrs);} else if (mFactory != null) {view = mFactory.onCreateView(name, context, attrs);} else {view = null;}if (view == null && mPrivateFactory != null) {view = mPrivateFactory.onCreateView(parent, name, context, attrs);}//前面的判断逻辑都不会执行,最后会执行该判断逻辑if (view == null) {final Object lastContext = mConstructorArgs[0];mConstructorArgs[0] = context;try {if (-1 == name.indexOf('.')) {//1.进入该判断语句,说明控件名称不包含点".",使用的系统控件如TextView,Buttonview = onCreateView(parent, name, attrs);} else {//2.使用的是自定义控件,界面名称是类的全包名,包含点view = createView(name, null, attrs);}} finally {mConstructorArgs[0] = lastContext;}}return view;} catch (InflateException e) {throw e;} catch (ClassNotFoundException e) {final InflateException ie = new InflateException(attrs.getPositionDescription()+ ": Error inflating class " + name, e);ie.setStackTrace(EMPTY_STACK_TRACE);throw ie;} catch (Exception e) {final InflateException ie = new InflateException(attrs.getPositionDescription()+ ": Error inflating class " + name, e);ie.setStackTrace(EMPTY_STACK_TRACE);throw ie;}}
eg:xml布局文件例子
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.timmy.customeView.countdownTime.CountDownTimeActivity"> <include layout="@layout/common_toolbar"/> //自定义控件书写 <com.timmy.MyView android:layout_width="120dp" android:layout_margin="30dp" android:layout_height="120dp" /> //系统控件 <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" /></LinearLayout>
假如使用的系统控件,进入逻辑1调用 onCreateView(parent,name,attrs)
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, "android.view.", attrs);}
最后系统控件和自定义控件都会调用createView()方法
最终创建View控件的就是createView()方法,根据控件全包名使用反射方式得到控件对象view,并返回.
/*** name为控件名称prefix为控件报名的前缀*/public final View createView(String name, String prefix, AttributeSet attrs)throws ClassNotFoundException, InflateException {//使用集合进行缓存控件的构造函数Constructor<? extends View> constructor = sConstructorMap.get(name);if (constructor != null && !verifyClassLoader(constructor)) {constructor = null;sConstructorMap.remove(name);}Class<? extends View> clazz = null;try {Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);if (constructor == null) {//1. 缓存中没有,反射拿到控件的Class对象clazz = mContext.getClassLoader().loadClass(prefix != null ? (prefix + name) : name).asSubclass(View.class);if (mFilter != null && clazz != null) {boolean allowed = mFilter.onLoadClass(clazz);if (!allowed) {failNotAllowed(name, prefix, attrs);}}//2.拿到控件构造函数constructor = clazz.getConstructor(mConstructorSignature);constructor.setAccessible(true);sConstructorMap.put(name, constructor);} else {//缓存中有该控件的构造函数if (mFilter != null) {// Have we seen this name before?Boolean allowedState = mFilterMap.get(name);if (allowedState == null) {// New class -- remember whether it is allowedclazz = mContext.getClassLoader().loadClass(prefix != null ? (prefix + name) : name).asSubclass(View.class);boolean allowed = clazz != null && mFilter.onLoadClass(clazz);mFilterMap.put(name, allowed);if (!allowed) {failNotAllowed(name, prefix, attrs);}} else if (allowedState.equals(Boolean.FALSE)) {failNotAllowed(name, prefix, attrs);}}}Object[] args = mConstructorArgs;args[1] = attrs;//3.构造函数执行final View view = constructor.newInstance(args);if (view instanceof ViewStub) {// Use the same context when inflating ViewStub later.final ViewStub viewStub = (ViewStub) view;viewStub.setLayoutInflater(cloneInContext((Context) args[0]));}return view;} catch (NoSuchMethodException e) {final InflateException ie = new InflateException(attrs.getPositionDescription()+ ": Error inflating class " + (prefix != null ? (prefix + name) : name), e);ie.setStackTrace(EMPTY_STACK_TRACE);throw ie;} catch (ClassCastException e) {// If loaded class is not a View subclassfinal InflateException ie = new InflateException(attrs.getPositionDescription()+ ": Class is not a View " + (prefix != null ? (prefix + name) : name), e);ie.setStackTrace(EMPTY_STACK_TRACE);throw ie;} catch (ClassNotFoundException e) {// If loadClass fails, we should propagate the exception.throw e;} catch (Exception e) {final InflateException ie = new InflateException(attrs.getPositionDescription() + ": Error inflating class "+ (clazz == null ? "<unknown>" : clazz.getName()), e);ie.setStackTrace(EMPTY_STACK_TRACE);throw ie;} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}}
到这里就了解了我们在xml文件写的布局是如何解析得到我们想要的控件.
最后我们来看下在infalate方法中的rInflateChilder()方法时如何递归拿到所有的控件-->先调用rInflate()->内部再调用rInflateChildren()
void rInflate(XmlPullParser parser, View parent, Context context,AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {//拿到xml文件的层级final int depth = parser.getDepth();int type;//xml解析器不断进行节点解析while (((type = parser.next()) != XmlPullParser.END_TAG ||parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {if (type != XmlPullParser.START_TAG) {continue;}//拿到节点名称final String name = parser.getName();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 (parser.getDepth() == 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 {//拿到该节点的控件viewfinal View view = createViewFromTag(parent, name, context, attrs);final ViewGroup viewGroup = (ViewGroup) parent;final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);//以view为父控件递归rInfalateChildren()rInflateChildren(parser, view, attrs, true);//最后按照层级添加viewGroup.addView(view, params);}}if (finishInflate) {//当所有的子控件都添加完毕,就会调用我们熟悉的onFinishInfalte()方法,通过该方法我们可以拿到内部的子控件.parent.onFinishInflate();}}
总结:
奉上infalte方法调用时序图:

随时随地看视频