轉載自:http://blog.csdn.net/u013356254/article/details/55052363
android交流:364595326
<h3>源碼分析之LayoutInflater</h3>
<h4>簡介</h4>
<ul>
<li>
基於5.0的framework源碼進行分析,通過這篇文章我們能瞭解:
<ul>
<li>LayoutInflater的系統級服務的註冊過程</li>
<li>inflate填充的過程</li>
<li>ViewStub,merge,include的加載過程</li>
</ul>
</li>
</ul>
<h4>LayoutInflater系統服務的註冊過程</h4>
<ul>
<li>
<p>我們經常調用</p>
<pre><code>context.getSystemService(Context.LAYOUT_INFLATE_SERVICE)
</code></pre>
<p>獲得<strong>LayoutInflater</strong>對象。那麼這個對象是什麼時候註冊到Context中的呢?這個對象的具體實現類是誰?</p>
<ul>
<li>LayoutInflater這個服務,是在創建Activity的時候,作爲<strong>baseContext</strong>傳遞給Activity的。接下來我們看源碼過程。</li>
<li>
<p>我們知道<strong>Activity</strong>的創建過程是在<strong>ApplicationThread<strong>的</strong>performLaunchActivity</strong>方法中。那麼接下來我們分析這個方法</p>
<pre><code> private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
/*/
Activity activity = null;
try{
// 通過Instrumentation類創建Activity
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
}catch(Exeception e){}
/*/
// 創建Context過程,也就是baseContext
Context appContext = createBaseContextForActivity(r, activity);
// 關聯activity和baseContext
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);
}
</code></pre>
</li>
<li>
<p>那麼接下來我們只要分析</p>
<pre><code> Context appContext = createBaseContextForActivity(r, activity);
</code></pre>
<p>這個方法即可,源碼繼續</p>
<pre><code> private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
// 通過調用ContextImpl的靜態方法創建baseContext對象
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, r.token, displayId, r.overrideConfig);
appContext.setOuterContext(activity);
return baseContext;
}
</code></pre>
</li>
<li>
<p>接下來分析</p>
<pre><code>ContextImpl.createActivityContext(
this, r.packageInfo, r.token, displayId, r.overrideConfig);
appContext.setOuterContext(activity);
</code></pre>
<p>接下來我們分析下ContextImpl這個類,發現其有一個成員變量</p>
<pre><code> // 在這裏註冊系統級別的服務
// The system service cache for the system services that are cached per-ContextImpl.
final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();
</code></pre>
</li>
</ul>
<p>SystemServiceRegistry類有個靜態代碼塊,完成了常用服務的註冊,代碼如下 </p>
<pre><code>static{
// 註冊LayoutLAYOUT_INFLATER_SERVICE系統服務,具體實現類是PhoneLayoutInflater
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
// 註冊AM
registerService(Context.ACTIVITY_SERVICE, ActivityManager.class,
new CachedServiceFetcher<ActivityManager>() {
@Override
public ActivityManager createService(ContextImpl ctx) {
return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
}});
// 註冊WM
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher<WindowManager>() {
@Override
public WindowManager createService(ContextImpl ctx) {
return new WindowManagerImpl(ctx);
}});
// 等等
}
</code></pre>
</li>
<li>
<p>接下來我們看inflate過程,下面是整個inflate過程</p>
<pre><code>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 {
// 循環找到第一個view節點,
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty
}
// 這裏判斷是否是第一個view節點
if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
}
final String name = parser.getName();
// 解析merge標籤
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");
}
// 通過rInflate方法將merge標籤下的孩子直接合併到root上,這樣減少一層佈局,達到減少viewTree的目的
rInflate(parser, root, inflaterContext, attrs, false);
} else {
// 調用反射創建view對象
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) {
// 如果view的父容器不爲null,並且attachToRoot未true得話,這裏只是讓剛剛通過反射創建的view使用root(父容器的佈局參數)
temp.setLayoutParams(params);
}
}
// 通過深度遍歷temp下的節點,之後將節點依次添加到剛剛通過反射創建的temp對象上,因爲採用的是深度優先遍歷算法,因此viewTree的層級很深的話,會影響遍歷的性能
rInflateChildren(parser, temp, attrs, true);
// 判斷剛剛創建的temp對象是否添加到父節點上.
// 滿足兩個條件1 父節點(root)不爲null,2 attachToRoot=true
if (root != null && attachToRoot) {
root.addView(temp, params);
}
// 設置result
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) {
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
// 返回
return result;
}
</code></pre>
<p>}</p>
<ul>
<li>通過上面分析,我們對inflate的整體過程有了一個瞭解,也見到了merge標籤(經常作爲佈局文件根節點,來達到減少viewTree的層次)</li>
<li>接下來,我們分析4個方法</li>
<li>rInflate(parser, root, inflaterContext, attrs, false);,其實不管是根節點爲merge還是普通的view(最終都會用這個方法),深度遍歷添加view</li>
<li>
<p>下面是代碼</p>
<pre><code> // 深度遍歷添加孩子
void rInflate(XmlPullParser parser, View parent, Context context,
AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
int type;
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)) {
// 如果我們調用了View.setTag(),將會執行下面代碼
parseViewTag(parser, parent, attrs);
// include不能作爲根節點
} else if (TAG_INCLUDE.equals(name)) {
if (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");
}
// 這裏解析include標籤代碼
parseInclude(parser, context, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
// merge一定是根節點
throw new InflateException("<merge /> must be the root element");
} else {
final View view = createViewFromTag(parent, name, context, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
// 遞歸,因爲rInflateChildren最終還會調用rInflate(parser, parent, parent.getContext(), attrs, finishInflate);方法
rInflateChildren(parser, view, attrs, true);
viewGroup.addView(view, params);
}
}
if (finishInflate) {
// viewTree填充完畢,回調自定義view經常使用的onFinishInflate方法
parent.onFinishInflate();
}
}
</code></pre>
</li>
<li>
<p>rInflateChildren(parser, view, attrs, true);方法</p>
<pre><code> // 直接調用rInflate()實現ViewTree
final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
</code></pre>
<p>}</p>
</li>
<li>
<p>createViewFromTag(root, name, inflaterContext, attrs);方法,這個方法其實處理了自定義view和系統view的創建。最終調用了下面方法</p>
<pre><code> View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
}
// 設置view默認樣式
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();
}
try {
View view;
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {//創建系統view的方法,因爲系統view的標籤不是完整類名,需要會在 onCreateView中完成拼接(拼接出系統view的完整類名)
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);
} else {
//自定義view的創建
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;
}
}
</code></pre>
</li>
<li>
<p>接下來我們分析 createView(String name, String prefix, AttributeSet attrs)方法,系統view的創建,最終也會調用createView方法。只不過在前面拼接上了系統view的包名。</p>
<pre><code> public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
// 獲取view的構造方法
Constructor<? extends View> constructor = sConstructorMap.get(name);
// 驗證
if (constructor != null && !verifyClassLoader(constructor)) {
constructor = null;
sConstructorMap.remove(name);
}
Class<? extends View> clazz = null;
try {
if (constructor == null) {
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);
// 將view的構造方法緩存起來
sConstructorMap.put(name, constructor);
} else {
/*/
}
Object[] args = mConstructorArgs;
args[1] = attrs;
// 反射創建view對象
final View view = constructor.newInstance(args);
// 對viewStub進行處理
if (view instanceof ViewStub) {
// 給ViewStub設置LayoutInfalter.什麼時候inflate,什麼時候viewStub的內容才顯示,(比GONE性能好)
final ViewStub viewStub = (ViewStub) view;
viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
}
return view;
} catch (NoSuchMethodException e) {
} catch (ClassCastException e) {
} catch (ClassNotFoundException e) {
} catch (Exception e) {
} finally {
}
}
</code></pre>
</li>
</ul>
</li>
</ul>
<h4>總結</h4>
<ul>
<li>系統服務的填充過程,是在ContextImpl中完成註冊的</li>
<li>LayoutInflater的實現類是PhoneLayoutInflater</li>
<li>如果僅僅使用父容器的佈局參數,可以使用inflater.inflate(layoutId,parent,false);</li>
<li>onFinishInflate()方法是在viewTree遍歷完成之後,調用的</li>
<li>merge標籤只能是根節點,include標籤不能是根節點。</li>
<li>
佈局優化
<ul>
<li>view的inflate的過程是深度遍歷,因此應該儘量減少viewTree的層次,可以考慮使用merge標籤</li>
<li>如果我們不知道view什麼時候填充的時候,可以使用ViewStub標籤,什麼時候用什麼時候填充</li>
<li>include是提升複用的</li></ul></li></ul>