在 Android 开发中,我们经常使用 LayoutInflater,俗称布局填充器,它可以将 XML 布局文件转换为 View 对象。通常情况下,我们会采用以下方式之一来实现:

1. 调用其静态 `from` 方法,获取 `LayoutInflater` 对象,然后调用其 `inflate` 方法获取一个 `View` 对象:

```java

public static LayoutInflater from(Context context) {

return (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

}

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {

Context ctx = root == null ? null : root.getContext();

LayoutInflater factory = from(ctx);

return factory.inflate(resource, root, attachToRoot);

}

```

或:

```java

public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {

LayoutInflater factory = from(context);

return factory.inflate(resource, root);

}

```

当然,今天我们要讨论的不仅仅是如何加载布局的问题,还有一些其他问题,例如:

* 每次调用 `LayoutInflater` 的 `from` 方法都会创建一个新的 `LayoutInflater` 对象吗?

* 如果传递不同的 `Context` 对象给 `LayoutInflater` 的 `from` 方法,会有什么不同?

* 为什么在 XML 布局文件中使用 `View`,就必须要有两个参数的构造方法呢?

下面我们将通过实践和源码分析的方式回答上述问题。

### `LayoutInflater.from(Context context)` 方法

先给出结论:`LayoutInflater` 的静态 `from` 方法用于获取一个 `LayoutInflater` 实例。当传入不同的 `Context` 对象时,获取到的 `LayoutInflater` 实例也不同。每个 `Activity` 都会持有一个 `LayoutInflater` 实例。如果每次传递的 `Context` 都是同一个 `Activity` 实例,那么只会创建一个 `LayoutInflater` 实例。

验证:

```java

private void testCreateInflater() {

LayoutInflater inflater1 = ContextThemeWrapper.wrap(this).getSystemService(Context.LAYOUT_INFLATER_SERVICE);

assertEquals(inflater1, ContextThemeWrapper.wrap(this).getSystemService(Context.LAYOUT_INFLATER_SERVICE));

LayoutInflater inflater2 = new MyActivity().getLayoutInflater(); // 注意:这里不是 ContextThemeWrapper.wrap()!

assertEquals(inflater1, inflater2);

}

```

在Android系统中,LayoutInflater是一个非常重要的类,它负责将XML布局文件转换为View对象。在这个例子中,我们可以看到有三个不同的LayoutInflater实例:fromActivity1、fromActivity2和fromApplication。这三个实例分别通过不同的方式创建,但它们都继承自PhoneLayoutInflater类。

首先,我们来看fromActivity1的创建过程:

```java

LayoutInflater fromActivity1 = LayoutInflater.from(this);

```

这里,我们通过调用当前Activity的LayoutInflater的from方法来创建一个新的LayoutInflater实例。这个实例是Activity级别的,因此在同一个Activity中多次调用只会创建一个实例。

接下来,我们看fromActivity2的创建过程:

```java

LayoutInflater fromActivity2 = LayoutInflater.from(this);

```

这里,我们再次通过调用当前Activity的LayoutInflater的from方法来创建一个新的LayoutInflater实例。由于fromActivity1已经存在,所以这次调用实际上是在现有实例上进行操作,而不是创建一个新的实例。因此,fromActivity2与fromActivity1共享相同的实例。

最后,我们看fromApplication的创建过程:

```java

LayoutInflater fromApplication = LayoutInflater.from(getApplication());

```

这里,我们通过调用应用程序级别的getApplication()方法来获取应用程序的Context对象,然后再调用该Context对象的LayoutInflater的from方法来创建一个新的LayoutInflater实例。这个实例是应用程序级别的,因此在整个应用程序中多次调用都会创建新的实例。

总结一下,当我们通过Activity或应用程序级别的Context对象调用LayoutInflater的from方法时,会根据情况创建不同级别的实例。在同一个Activity中,多次调用只会创建一个实例;而在整个应用程序中,每次调用都会创建新的实例。

在Android中,`LayoutInflater` 类是一个用于将布局XML文件转换为Java对象的类。要使用它,我们需要先从 `Context` 获取一个 `LayoutInflater` 实例。在这个过程中,我们实际上是调用了 `Context` 的 `getSystemService` 方法。接下来,我将分析这段代码并展示其工作原理。

首先,我们来看 `android.view.LayoutInflater` 类中的静态方法 `from(Context context)`:

```java

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` 参数,并通过调用 `context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)` 来获取一个 `LayoutInflater` 实例。如果没有找到 `LayoutInflater`,则抛出一个异常。最后,返回这个 `LayoutInflater` 实例。

接下来,我们看一下在 `Activity` 中的 `getSystemService(String name)` 方法:

```java

@Override

public Object getSystemService(@NonNull String name) {

if (getBaseContext() == null) {

throw new IllegalStateException("System services not available to Activities before onCreate()");

}

if (WINDOW_SERVICE.equals(name)) {

return mWindowManager;

} else if (SEARCH_SERVICE.equals(name)) {

ensureSearchManager();

return mSearchManager;

}

return super.getSystemService(name);

}

```

当我们在 `Activity` 中调用 `getSystemService()` 方法时,它会首先检查是否已经创建了基本的 `Context`(即非空的 `getBaseContext()`)。如果没有创建基本的 `Context`,则抛出异常提示系统服务在 onCreate() 方法之前不可用。然后,根据传入的 `name` 参数,分别返回对应的系统服务。例如,如果传入的名称是 `WINDOW_SERVICE`,则返回 `mWindowManager`;如果传入的名称是 `SEARCH_SERVICE`,则先确保搜索管理器已经初始化,然后返回 `mSearchManager`。如果传入的名称不在已知的系统服务列表中,则调用父类的 `getSystemService()` 方法并返回结果。

在Activity中,getSystemService()方法主要针对WINDOW_SERVICE和SEARCH_SERVICE进行了特殊处理,而对于LAYOUT_INFLATER_SERVICE并没有进行处理。然而,Activity实际上是继承自ContextThemeWrapper的,因此我们可以进一步了解ContextThemeWrapper中的getSystemService()方法。

```java

#android.view.ContextThemeWrapper

@Override

public Object getSystemService(String name) {

if (LAYOUT_INFLATER_SERVICE.equals(name)) {

if (mInflater == null) {

mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this); //1

}

return mInflater;

}

return getBaseContext().getSystemService(name);

}

```

从上述代码可以看出,当我们需要获取LayoutInflater对象时,如果当前的mInflater变量为空,就会调用1处的`LayoutInflater.from(getBaseContext()).cloneInContext(this)`方法创建一个LayoutInflater对象。这样,下次再调用时就可以直接返回这个对象,而不会重复创建。

在上面的代码中,getBaseContext()方法获取的就是该Activity绑定的mBase对象。需要注意的是,mBase是一个Context对象,而Context本身是一个抽象类。那么,它实际上是什么对象呢?这个问题我们稍后会进行解释。

现在,让我们来看一下如果我们传递的是Application对象会发生什么情况。我们需要知道一点:Application是继承自ContextWrapper的。因此,我们可以查看ContextWrapper的getSystemService()方法来了解其行为。

在Android系统中,无论是Activity、Application还是Service对象,在创建完毕后,它们的attach()方法都会被调用。在这个过程中,会调用attachBaseContext()方法,该方法会为mBase赋值,实际上是一个ContextImpl对象。这个ContextImpl对象在之前的几篇文章中也有涉及到相关内容,如果想了解更多,可以参考Activity启动过程分析和Android四大组件——Service的工作过程分析。

通过上面的分析,我们可以发现,无论是传递Activity还是Application,实际上都会先调用ContextImpl的getSystemService()方法。下面我们来看一下android.app.ContextImpl中的getSystemService()方法:

```java

# android.app.ContextImpl

@Override

public Object getSystemService(String name) {

return SystemServiceRegistry.getSystemService(this, name);

}

```

需要注意的是,ContextImpl的getSystemService()方法在不同的版本中可能会有所不同。我们不需要过于纠结API的不同,而是要关注流程。现在,我们想要了解的是LayoutInflater对象是如何创建的。接下来,让我们继续查看SystemServiceRegistry的getSystemService()方法。

在 SystemServiceRegistry 类的 getSystemService() 方法中,首先根据传入的 name 参数从 SYSTEM_SERVICE_FETCHERS(一个 HashMap)中获取对应的 ServiceFetcher 对象。如果找到了对应的 ServiceFetcher,就调用其 getService() 方法并传入 ContextImpl 对象(即 ctx),返回一个 Object 类型的对象;如果没有找到对应的 ServiceFetcher,则直接返回 null。

SYSTEM_SERVICE_FETCHERS 实际上是一个 HashMap,用于存储不同服务名称与相应的 ServiceFetcher 对象之间的映射关系。当我们需要获取某个服务时,只需通过这个映射关系找到对应的 ServiceFetcher,然后调用其 getService() 方法即可。

在这个例子中,我们想要获取的 LayoutInflater 服务应该已经被注册到了 SystemServiceRegistry 中。因此,getSystemService() 方法会直接从 SYSTEM_SERVICE_FETCHERS 中获取到我们所需的 ServiceFetcher 对象。接下来,ServiceFetcher 的 getService() 方法会先检查当前是否已经缓存了对应的 LayoutInflater 对象,如果已经缓存了,则直接返回该对象;如果没有缓存,则调用 createService() 方法创建一个新的 LayoutInflater 对象并返回。

以下是重构后的内容:

```

final class SystemServiceRegistry {

private static final String TAG = "SystemServiceRegistry";

// Service registry information.

// This information is never changed once static initialization has completed.

private static final HashMap, String> SYSTEM_SERVICE_NAMES = new HashMap, String>();

private static final HashMap> SYSTEM_SERVICE_FETCHERS = new HashMap>();

private static int sServiceCacheSize;

// Not instantiable.

private SystemServiceRegistry() {}

static {

registerService(Context.ACTIVITY_SERVICE, ActivityManager.class, new CachedServiceFetcher() {{ @Override public ActivityManager createService(ContextImpl ctx) { return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler()); }});

...

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

...

}

}

```

通过上面的分析,我们可以发现,无论是在LayoutInflater的from方法中的Context(传递Activity或Application),实际上都会先调用ContextImpl的getSystemService()方法来获取一个单例的LayoutInflater对象。这个对象在整个应用程序中是唯一的,只要是从ContextImpl这获取的,就只有一个。而对于Activity,还会调用cloneInContext()方法来获取一个LayoutInflater对象,但需要注意的是,这个cloneInContext()方法是一个抽象方法,需要由PhoneLayoutInflater实现。

```java

// com.android.internal.policy.PhoneLayoutInflater

public PhoneLayoutInflater(Context context) {

super(context);

}

public LayoutInflater cloneInContext(Context newContext) {

return new PhoneLayoutInflater(this, newContext);

}

protected PhoneLayoutInflater(LayoutInflater original, Context newContext) {

super(original, newContext);

}

#android.view.LayoutInflater

protected LayoutInflater(LayoutInflater original, Context newContext) {

mContext = newContext;

mFactory = original.mFactory;

mFactory2 = original.mFactory2;

mPrivateFactory = original.mPrivateFactory;

setFilter(original.mFilter);

}

```

从上述代码中可以看出,调用`LayoutInflater`的`cloneInContext()`方法实际上是创建了一个新的`LayoutInflater`对象,并将原来对象上的一些属性复制过来,如`mFactory`、`mFactory2`、`mPrivateFactory`和`mFilter`。这些factory和filter用于在从XML加载布局以创建View对象时。

需要注意的是,`PhoneLayoutInflater`的一个构造方法中的`Context`是由`ContextImpl`的`getOuterContext()`方法获取到的。那么这个`mOuterContext`是什么呢?`ContextImpl`对象与Application、Activity、Service对象是一一绑定的。在`ContextImpl`中,`mOuterContext`对象表示当前的Activity或Service。赋值时机是在`ContextImpl`对象创建后。通常情况下,`ContextImpl`的创建时机是在Activity线程中每次启动Activity、Service或者一个新的Application的时候。

接下来,我们来看一下`LayoutInflater`的创建时机。在上述讲解中,我们提到了当在Activity中调用`setContentView(layoutRes)`方法时,会调用到`PhoneWindow`的`setContentView(layoutRes)`方法。在这个过程中,是通过`LayoutInflater`对象来创建View树的。实际上,`LayoutInflater`的创建时机就是在Activity对象被创建出来之后。当Activity创建完成后,其`attach()`方法会被调用。在这个过程中,与Activity相关的对象或属性会被绑定,例如在这个时候就会创建出`PhoneWindow`。

```java

/** * Attaches this activity to the window and initializes it.

*

* @param context The application context that should be associated with this activity.

* @param aThread The thread where the attach() method is being called.

* @param instr The instrumentation that should be used for interacting with system services.

* @param token The IBinder token that represents this activity's connection to the window manager.

* @param ident The launcher UID.

* @param application The application that created this activity instance.

* @param intent The intent that was last delivered to this activity via startActivity(). Can be null if there is no previous intent.

* @param info A ActivityInfo object containing configuration information about how the screen should be initialized.

* @param title A CharSequence representing the title of this activity. If null, only the class name will be shown.

* @param parent A reference to the parent of this activity. Can be null if there is no parent or if this is not the root activity of its task.

* @param id A String representing the ID of this activity. This can be used to find activities later using their ID.

* @param lastNonConfigInstances A NonConfigurationInstances object holding state that was preserved across a configuration change but can be reset if needed; may be null if there was no such instance before the last call to onCreate().

* @param config A Configuration containing any configuration information specific to this activity.

* @param referrer The URI referrer, or null if there is no referrer.

* @param voiceInteractor An interface to handle voice interaction with the user. Can be null.

* @param window The Window instance associated with this activity or null if it has not been created yet.

* @param activityConfigCallback An interface that can be used to retrieve custom lifecycle callbacks for activities that are created from a configuration source other than a declarative configuration file or an instance of ContextThemeWrapper.

*/

public 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, ActivityConfigCallback activityConfigCallback) {

// ... (省略了具体实现细节)

}

```

在PhoneWindow类中,有两个构造函数,一个接受3个参数,另一个接受1个参数。当调用有3个参数的构造函数时,首先会调用有1个参数的构造函数。在这个过程中,会调用LayoutInflater的from方法来创建一个LayoutInflater对象。

```java

public PhoneWindow(Context context) {

super(context);

mLayoutInflater = LayoutInflater.from(context);

}

```

注意:这个context对象就是Activity。

LayoutInflater的inflate()方法通常使用2个或3个参数。第一个参数表示要加载的布局文件,第二个参数是ViewGroup,需要与第三个参数配合使用。如果attachToRoot为true,表示将当前布局添加到ViewGroup中,作为其子View。如果为false,则表示仅采用ViewGroup的LayoutParams作为测量依据。如果ViewGroup为null,也可以得到一个View,但其尺寸可能不是我们想要的。因此,尽量不要使第二个参数为null。如果您只想从布局中加载View,而不想将其添加到ViewGroup中,可以使用3个参数的方法,并将attachToRoot设置为false。

下面这两个方法用于从资源中找到对应的布局xml文件,然后创建一个XmlResourceParser对象来解析该布局。解析方式采用的是pull解析。

以下是根据提供的内容重构的代码,并保持了段落结构:

```java

// android.view.LayoutInflater

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {

return inflate(resource, root, root != null);

}

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) + "\" (0x" + Integer.toHexString(resource) + ")");

}

final XmlResourceParser parser = res.getLayout(resource);

try {

return inflate(parser, root, attachToRoot);

} finally {

parser.close();

}

}

```

除了上面提到的两个开发者经常使用的方法外,还有一个接受三个参数的`inflate`方法。第一个参数是`XmlPullParser`对象,我们可以将其视为一个解析标签的对象。在这个`inflate()`方法中,会处理所有的视图标签(包括自定义视图)、include、merge以及ViewStub等。

在该方法中,有一个名为mConstructorArgs的数组,它包含两个元素。这个数组在调用View构造函数时用于提供参数。具体来说,mConstructorArgs的第一个元素就是inflaterContext,实际上它代表了创建LayoutInflater对象时的上下文(mContext)。对于在一个Activity中创建的LayoutInflater,mContext所指向的就是该Activity。

以下是重构后的内容:

在该方法中,我们有一个名为`mConstructorArgs`的数组,它包含两个元素。这个数组在调用View构造函数时被用来传递参数。具体地,`mConstructorArgs`的第一个元素是`inflaterContext`,它实际上代表了创建`LayoutInflater`对象时的上下文(`mContext`)。在一个Activity中创建的`LayoutInflater`,`mContext`将指向该Activity。

```java

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.

int type;

while ((type = parser.next()) != XmlPullParser.START_TAG &&

type != XmlPullParser.END_DOCUMENT) {

// Empty

}

final String name = parser.getName();

if (TAG_MERGE.equals(name)) {

if (root == null || !attachToRoot) {

throw new InflateException(" can be used only with a valid " +

"ViewGroup root and attachToRoot=true");

}

rInflate(parser, root, inflaterContext, attrs, false);

} else {

// 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) {

if (DEBUG) {

System.out.println("Creating params from root: " + root);

}

// 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);

}

}

// 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;

}

}

} catch (InflateException e) {

throw e;

} catch (Exception e) {

Log.e(TAG, "Error inflating XML", e);

} finally {

Trace.traceEnd(Trace.TRACE_TAG_VIEW);

}

return result;

}

}

```

在这段代码中,createViewFromTag()方法被调用,该方法会调用其重载版本。在createViewFromTag的重载版本中,会依次判断mFactory2、mFactory和mPrivateFactory是否为null。如果它们中的任何一个不为null,那么就会根据优先级调用这些工厂的相关方法来创建View。只有在前一个工厂返回的View为null的情况下,才会使用后一个工厂来创建。

如果这几个工厂创建的View都为null,那么将会调用LayoutInflater自身的方法来创建View。这些工厂(mFactory2、mFactory和mPrivateFactory)都具有相应的set方法用于设置。通过这个实现方式,我们可以理解之前讲解的在克隆一个原始的LayoutInflater时的作用,即可以复用它的mFactory2、mFactory和mPrivateFactory,而无需重新设置。

重构后的代码如下:

```java

private View createViewFromTagPrivate(View parent, String name, Context context, AttributeSet attrs) {

if (name.equals("view")) {

name = attrs.getAttributeValue(null, "class");

}

// 如果允许应用主题并指定了主题资源ID,则应用主题包装器。

if (!ignoreThemeAttr) {

TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);

int themeResId = ta.getResourceId(0, 0);

if (themeResId != 0) {

context = new ContextThemeWrapper(context, themeResId);

}

ta.recycle();

}

// 通过工厂方法创建View

View view;

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('.')) {

view = onCreateView(parent, name, attrs);

} else {

view = createView(name, null, attrs);

}

} finally {

mConstructorArgs[0] = lastContext;

}

}

return view;

}

```

我们来分析一下Factory、Factory2以及LayoutInflater的createView方法。首先,Factory和Factory2都继承自Factory接口,它们的接口方法都是用来创建View对象的。

```java

public interface Factory {

public View onCreateView(String name, Context context, AttributeSet attrs);

}

public interface Factory2 extends Factory {

public View onCreateView(View parent, String name, Context context, AttributeSet attrs);

}

```

接下来,我们看一下LayoutInflater的createView方法。首先,它通过View的名称来获取它的构造函数Constructor。如果Constructor为null的话就会采用ClassLoader去加载对应的class。需要注意的是,我们在xml填写的View的名称比如TextView,实际上是有全路径名的,即为:android.widget.TextView。类加载器加载必须要使用全路径名,因此对于TextView这样的Android系统自带的空间,需要加上全路径。因此,在注释1处可以看到使用了prefix。

当Class加载成功的时候,就会通过mConstructorSignature创建一个两个参数的构造器,对应的参数是Context.class和AttributeSet.class。然后,我们可以看到利用反射的方式创建View对象。

```java

# android.view.LayoutInflater public final View createView(String name, String prefix, AttributeSet attrs) throws ClassNotFoundException, InflateException {

Constructor constructor = sConstructorMap.get(name);

if (constructor != null && !verifyClassLoader(constructor)) {

constructor = null;

sConstructorMap.remove(name);

}

Class clazz = null;

try {

Trace.traceBegin(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 = 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);

}

}

constructor = clazz.getConstructor(mConstructorSignature);

constructor.setAccessible(true);

sConstructorMap.put(name, constructor);

} else {

// If we have a filter, apply it to cached constructor

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 lastContext = mConstructorArgs[0];

if (mConstructorArgs[0] == null) {

// Fill in the context if not already within inflation.

mConstructorArgs[0] = mContext;

}

Object[] args = mConstructorArgs;

args[1] = attrs;

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]));

}

mConstructorArgs[0] = lastContext;

return view;

} catch (InstantiationException e) {

throw new RuntimeException("Failed to instantiate view class " + name, e);

} catch (IllegalAccessException e) {

throw new RuntimeException("Failed to access view constructor for " + name, e);

} catch (InvocationTargetException e) {

Throwable cause = e.getCause();

if (cause instanceof ClassNotFoundException) {

throw (ClassNotFoundException) cause;

} else if (cause instanceof IllegalArgumentException) {

throw (IllegalArgumentException) cause;

} else if (cause instanceof InflateException) {

throw (InflateException) cause;

} else if (cause instanceof Error) {

throw (Error) cause;

} else if (cause instanceof RuntimeException) {

throw (RuntimeException) cause;

} else if (cause instanceof Exception) {

throw new RuntimeException("Failed to create view for " + name, cause);

} else {

throw new RuntimeException("Unexpected exception thrown while creating view for " + name, e);

}

} finally {

Trace.traceEnd(Trace.TRACE_TAG_VIEW, name);

}

}

```

在Android中,View是一个非常重要的组件,它是所有UI元素的基础。在创建View对象时,我们需要传递两个参数:Context和AttributeSet。通过这两个参数的构造函数,我们可以创建一个View对象。其中,第一个参数Context实际上就是所在的Activity对象。对于Fragment中的View也是同样的道理。

在Fragment的onCreateView()方法中,我们需要传递一个LayoutInflater作为参数。这个LayoutInflater对象是从哪里来的呢?实际上,它是由Activity中的LayoutInflaterclone而来的。虽然Fragment中的LayoutInflater与Activity中的LayoutInflater不是同一个对象,但是它们之间是浅拷贝关系。这意味着,Fragment中的LayoutInflater会将Activity中的LayoutInflater中的mFactory、mFactory2、mPrivateFactory、mFilter等变量赋值给自己的相应成员变量。

为了更好地理解这个过程,我们可以通过分析源码来了解。在Android中,Fragment是由FragmentManager来管理的。当Fragment在创建阶段的生命周期方法被回调时,这些方法是在FragmentManager的moveToState()方法中回调的。在这个方法中,我们可以看到调用了Fragment的performCreateView()方法,并在其中传递了LayoutInflater参数。而这个参数是通过调用Fragment的performGetLayoutInflater()方法获得的。

总之,通过以上的解析,我们可以得出结论:在Android中,View对象的Context实际上就是其所在的Activity对象;而对于Fragment中的View对象,也是同样的道理。同时,我们还了解到了Fragment中的LayoutInflater是如何从Activity中的LayoutInflater进行clone操作的,以及这个过程中涉及到的一些关键信息。

```java

@SuppressWarnings("ReferenceEquality")

void moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive) {

if (DEBUG && false) Log.v(TAG, "moveToState: " + f + " oldState==" + f.mState + " newState==" + newState + " mRemoving==" + f.mRemoving + " Callers==" + Debug.getCallers(5));

// Fragments that are not currently added will sit in the onCreate() state.

if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) {

newState = Fragment.CREATED;

}

if (f.mRemoving && newState > f.mState) {

if (f.mState == Fragment.INITIALIZING && f.isInBackStack()) {

// Allow the fragment to be created so that it can be saved later.

newState = Fragment.CREATED;

} else {

// While removing a fragment, we can't change it to a higher state.

newState = f.mState;

}

}

// Defer start if requested; don't allow it to move to STARTED or higher

// if it's not already started.

if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) {

newState = Fragment.STOPPED;

}

if (f.mState <= newState) {

switch (f.mState) {

case Fragment.INITIALIZING:

dispatchOnFragmentPreAttached(f, mHost.getContext(), false);

f.mCalled = false;

f.onAttach(mHost.getContext());

...

case Fragment.CREATED:

ensureInflatedFragmentView(f);

if (newState > Fragment.CREATED) {

if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);

if (!f.mFromLayout) {

ViewGroup container = null;

...

container = mContainer.onFindViewById(f.mContainerId);

if (container == null && !f.mRestored) {

String resName;

try {

resName = f.getResources().getResourceName(f.mContainerId);

} catch (Resources.NotFoundException e) {

e.printStackTrace();

return;

} finally {

try {

resName = f.getResources().getResourceEntryName(f.getClass(), resName);

} catch (Resources.NotFoundException e1) {

e1.printStackTrace();

return;

} finally {

resName = resName == null || resName.length() == 0 ? null : resName;

}

}

f.mContainer = container;

f.mView = f.performCreateView(f.performGetLayoutInflater(), container, f.mSavedFragmentState);

if (f.mView != null) {

f.mView.setSaveFromParentEnabled(false);

if (container != null) {

container.addView(f.mView);

}

}

}

}

} else if (f.isHidden()) { // Only set visibility here since this is the last check before calling onDestroyView(). This ensures the correct view hierarchy is updated even when using nested fragments with different states and hiding/showing them together (#3462). #3462 is now fixed by moving the visibility check into the onDestroyView() method for non-nested fragments and updating the visible child views there as well as here before calling onDestroyView(). Previously, this was done after the call to onDestroyView() which would cause an exception when switching back from one state to another during the same transaction #3462 #3465 #3468 #3471 #3475 #3479 #3483 #3487 #3491 #3495 #3499 #3503 #3506 #3510 #3515 #3520 #3527 #3528 #3529 #3531 #3532 #3535 #3536 #3537 #3540 #3541 #3542 #3547 #3548 #3549 #3550 #3551 #3561 #3562 #3568 #3570 #3627). If you want to avoid this issue completely for your application, consider using a compatibility library like ViewPager2 instead of the support library which has this limitation (#11889). Additionally, you should use the PFLAG_ACTIVITY_NO_HISTORY flag in your activity configuration (#2796). See also https://developer.android.com/guide/components/fragments-pc#creating-and-removing-fragments and http://stackoverflow.com/q/9188089/1068768 and http://stackoverflow.com/a/10969707/1068768 for more details on handling fragment states and lifecycle callbacks in Android N and earlier versions of Android as well as some alternatives to support library fragments such as the support library version of androidx fragments (https://github.com/android/support-libraries/tree/master/fragment).

Fragment的performGetLayoutInflater()方法首先调用了onGetLayoutInflater()方法。在这段代码中,我们可以观察到以下内容:

```java

# android.support.v4.app.Fragment

public LayoutInflater performGetLayoutInflater(Bundle savedInstanceState) {

LayoutInflater layoutInflater = onGetLayoutInflater(savedInstanceState);

mLayoutInflater = layoutInflater;

return mLayoutInflater;

}

public LayoutInflater getLayoutInflater(Bundle savedFragmentState) {

if (mHost == null) {

throw new IllegalStateException("onGetLayoutInflater() cannot be executed until the " + "Fragment is attached to the FragmentManager.");

}

LayoutInflater result = mHost.onGetLayoutInflater();

getChildFragmentManager(); // Init if needed; use raw implementation below.

LayoutInflaterCompat.setFactory2(result, mChildFragmentManager.getLayoutInflaterFactory());

return result;

}

```

在performGetLayoutInflater()方法中,首先通过调用onGetLayoutInflater(savedInstanceState)方法获取一个LayoutInflater对象,然后将其赋值给mLayoutInflater成员变量,并返回该对象。而在getLayoutInflater(Bundle savedFragmentState)方法中,首先检查Fragment是否已经附加到FragmentManager,如果没有则抛出异常。接下来,通过调用mHost.onGetLayoutInflater()方法获取一个LayoutInflater对象,并根据需要初始化ChildFragmentManager。最后,使用LayoutInflaterCompat.setFactory2()方法设置结果对象的工厂并返回。

在Fragment的getLayoutInflater()方法中,通过调用mHost.onGetLayoutInflater()获取了一个LayoutInflater对象。mHost就是FragmentHostCallback对象,来看一下它的onGetLayoutInflater()方法:

```java

# android.support.v4.app.FragmentHostCallback

public LayoutInflater onGetLayoutInflater() {

return (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

}

```

FragmentHostCallback的onGetLayoutInflater()方法实际上就是调用了mContext的getSystemService方法,这个就跟我们前面分析的通过LayoutInflater的from方法是一个意思,mContext实际上就是Fragment所在的Activity。再加上之前的分析我们可以得出一个结论,Fragment中的LayoutInflater是由Activity中的LayoutInflater clone而来,它们不是同一个对象,不过Fragment中的LayoutInflater把Activity的LayoutInflater设置的一些factory copy过来,相当于它们使用的是同样的工厂。

接下来看一下LayoutInflaterCompat.setFactory2(result, mChildFragmentManager.getLayoutInflaterFactory());这个方法:

```java

// 设置工厂

public static void setFactory2(LayoutInflater result, LayoutInflaterFactory factory) {

if (factory != null) {

result.setFactory2(factory);

} else {

// 如果factory为null,则将mFactory、mFactory2均赋值为当前设置的factory

result.setFactory2((LayoutInflaterFactory) result.getClass().getField("mFactory").get(result));

result.setFactory2((LayoutInflaterFactory) result.getClass().getField("mFactory2").get(result));

}

}

```

LayoutInflater的setFactory2()方法很有意思,如果原来LayoutInflater上面的mFactory为null,就是把实际上mFactory、mFactory2均赋值为当前设置的factory。如果不为null创建了一个FactoryMerger对象赋值给mFactory、mFactory2。

在Activity中设置了mFactory2后,当我们在Fragment中的LayoutInflater调用setFactory2()方法时,我们会发现mFactory、mFactory2都不为空。这是因为如果mFactory和mFactory2都非空,我们会创建一个新的FactoryMerger对象。这个FactoryMerger实际上实现了Factory2接口。

以下是重构后的代码:

```java

public class MyLayoutInflater extends LayoutInflater {

private boolean mFactorySet = false;

private Factory2 mFactory = null;

private Factory2 mFactory2 = null;

@Override

public void setFactory2(Factory2 factory) {

if (mFactorySet) {

throw new IllegalStateException("A factory has already been set on this LayoutInflater");

}

if (factory == null) {

throw new NullPointerException("Given factory can not be null");

}

mFactorySet = true;

if (mFactory == null) {

mFactory = mFactory2 = factory;

} else {

mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);

}

}

}

class FactoryMerger implements Factory2 {

private Factory2 factory1;

private Factory2 factory2;

private Factory2 mFactory;

private Factory2 mFactory2;

FactoryMerger(Factory2 factory1, Factory2 factory2, Factory2 mFactory, Factory2 mFactory2) {

this.factory1 = factory1;

this.factory2 = factory2;

this.mFactory = mFactory;

this.mFactory2 = mFactory2;

}

}

```

在这个重构的版本中,我假设MyLayoutInflater类继承自android.view.LayoutInflater,并添加了一个私有变量`mFactorySet`来跟踪是否已经设置过工厂。然后在`setFactory2()`方法中,首先检查`mFactorySet`是否为true,如果是,则抛出一个异常。然后检查传入的工厂参数是否为null,如果是,则抛出一个异常。接下来,将`mFactorySet`设为true。然后检查`mFactory`和`mFactory2`是否都为null,如果是,那么就直接将传入的工厂赋值给它们。否则,使用传入的工厂以及现有的`mFactory`和`mFactory2`来创建一个新的`FactoryMerger`对象。

private static class FactoryMerger implements Factory2 {

private final Factory mF1, mF2;

private final Factory2 mF12, mF22;

FactoryMerger(Factory f1, Factory2 f12, Factory f2, Factory2 f22) {

mF1 = f1;

mF2 = f2;

mF12 = f12;

mF22 = f22;

}

public View onCreateView(String name, Context context, AttributeSet attrs) {

View v = mF1.onCreateView(name, context, attrs);

if (v != null) return v;

return mF2.onCreateView(name, context, attrs);

}

public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {

View v = mF12 != null ? mF12.onCreateView(parent, name, context, attrs) : mF1.onCreateView(name, context, attrs);

if (v != null) return v;

return mF22 != null ? mF22.onCreateView(parent, name, context, attrs) : mF2.onCreateView(name, context, attrs);

}

android.support.v4.app.FragmentManager$FragmentManagerImpl @Override public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {

if (!"fragment".equals(name)) {

return null;

}

// ...

return fragment.mView;

}

其实想想,这样的设计真的挺好,Fragment会复用了Activity的Factory对象,只是在解析fragment标签的时候,采用用FragmentManagerImpl来解析。

总结:相信看完上面的内容,你对LayoutInflater应该有一个比较全面的了解了,具体怎么灵活运用就要看你的需求了。比如你想完成一个换肤框架,那么你首先肯定要获得所有需要换肤的控件,此时LayoutInflater的Factory2就可以派上用场了。通过给LayoutInflater设置Factory2,可以自己处理View的创建逻辑,获取相关的View,当你需要换肤的时候,给这些View设置新的属性即可。我们再来回顾一些问题:现在你能够自己解答了吗?每次调用LayoutInflater的from方法都会创建一个新的LayoutInflater对象吗?LayoutInflater的from方法中如果传递不同的Context对象会有什么不同?调用View的getContext()方法会获取一个Context,那么这个Context对象具体是谁呢?为什么想要在xml中使用View,就必须要有两个参数的构造方法呢?Fragment中有一个onCreateView方法,有一个参数是LayoutInflater,这么LayoutInflater对象是从哪来的呢?LayoutInflater对象的真正创建时机是什么时候?