知其然,更需知其所以然,让我们开启源码解析之旅.
文章目录
LayoutInflater的使用场景
LayoutInflater的由来
LayoutInflater.inflater()方法源码解析
总结
通常在日常开发中下面几个地方会使用到LayoutInflater
Fragment的onCreateView()方法中需要通过LayoutInflate.inflate方法填充布局,该LayoutInflater对象是Fragment方法中传递进来的
@Override
public 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 libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final 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(new
LogPrinter(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() {
@Override
public 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.创建activity
activity = 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();
}
//到这步我们确认我们使用的上下文的实现类是ContextImpl
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, r.token, displayId, r.overrideConfig);
appContext.setOuterContext(activity);
Context baseContext = appContext;
//...
return baseContext;
}
找到了上下文的实现类ContextImpl,继续跟踪getSystemService()方法
发现真正的实现类是SystemServiceRegistry.getSystemService()
@Override
public 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>() {
@Override
public AccessibilityManager createService(ContextImpl ctx) {
return AccessibilityManager.getInstance(ctx);
}});
//..........
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher<LayoutInflater>() {
@Override
public 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");
//拿到构造函数中传入的上下文mContext
final 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();
}
//直接返回BlinkLayout
if (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皆为null
if (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,Button
view = 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 allowed
clazz = 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 subclass
final 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 {
//拿到该节点的控件view
final 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方法调用时序图: