Android LayoutInflater 探索

日期 2016-12-25
Android LayoutInflater 探索

LayoutInflater.from(context) 是我们非常常用的方法,在Activity也可以调用getLayoutInflater()获取,可能这其中也有一些需要注意的地方。

先运行下面一段代码,猜猜运行结果,这三个方法运行后得到的是同一个对象吗?

public class MainActivity extends Activity {

private static final String TAG = "MainActivity";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Log.e(TAG, "onCreate -->LayoutInflater.from(this): "+LayoutInflater.from(this));
Log.e(TAG, "onCreate -->getLayoutInflater(): "+getLayoutInflater());
Log.e(TAG, "onCreate -->LayoutInflater.from(getApplicationContext()): "+LayoutInflater.from(getApplicationContext()));
}

}

下面看看结果:
运行结果

12-25 16:20:24.716 26432-26432/? E/MainActivity: onCreate -->LayoutInflater.from(this): com.android.internal.policy.PhoneLayoutInflater@f8ec208
12-25 16:20:24.716 26432-26432/? E/MainActivity: onCreate -->getLayoutInflater(): com.android.internal.policy.PhoneLayoutInflater@f8ec208
12-25 16:20:24.716 26432-26432/? E/MainActivity: onCreate -->LayoutInflater.from(getApplicationContext()): com.android.internal.policy.PhoneLayoutInflater@9173da1

也就是LayoutInflater.from(Activity.this)Activity.getLayoutInflater()得到的是同一个对象,
LayoutInflater.from(getApplicationContext()) 并不相同,这就要注意在使用时有什么区别了。

我们先看看LayoutInflater.from(context) 是怎么处理的
LayoutInflater.java

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

其中context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) 又有多种实现,先来看看Activity 的:

Activity.java

@Override
public Object getSystemService(@ServiceName @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);
}

继续追踪super.getSystemService(name);
ContextThemeWrapper.java

@Override
public Object getSystemService(String name) {
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
if (mInflater == null) {
mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
}
return mInflater;
}
return getBaseContext().getSystemService(name);
}

看到了我们熟悉的LAYOUT_INFLATER_SERVICE,并且根据代码可以看到每个Activity 都会有一个自己独立的mInflater 属性。

我们再来跟踪一下ApplicationContext 是怎么处理的
可以在SystemServiceRegistry.java 中看到服务的注册方式
SystemServiceRegistry.java

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

说白了是在整个app生命周期中会有一个原始的LayoutInflater对象,但是通过Activity创建的LayoutInflater对象中会持有Activity对象,这个context在创建View 的构造方法中传入,所以,在使用LayoutInflater.from(context).inflate() 创建出是如果要使用view.getContext()LayoutInflater.from(context) 的context相同。