在自定義View中咱們常常經過getContext()方法來使用Context獲取資源、設置樣式等。那麼你們有沒有想過View中getContext()獲取的Context是來自哪裏的?接下來就分析View中Context的生成過程。app
咱們都知道在View中有個getContext()方法供開發者使用。佈局
/** * Returns the context the view is running in, through which it can * access the current theme, resources, etc. * * @return The view's Context. */
@ViewDebug.CapturedViewProperty public final Context getContext() {
return mContext;
}
複製代碼
在上面的註釋中能夠看出,經過View中的Context是View正在運行的上下文環境中,而且能夠經過Context獲取資源和主題等。根據這些信息咱們能夠判斷View中的Context是一個ContextThemeWrapper類型的Context。post
在獲取到上面的信息後,咱們能夠知道View的Context是一個ContextThemeWrapper類型的Context,而且在View的構造方法中也能夠看到Context被賦值。根據這些信息咱們能夠判斷View在初始化的時候Context被賦值,因此接下來從Activity的setContentView()開始分析。this
在Activity的setContentView()方法中View會被建立以及繪製。spa
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
複製代碼
能夠看到在Activity的setContentView()方法中調用了Window的setContentView()方法。咱們知道這個Window就是PhoneWindow,接下來直接在PhoneWindow中分析setContentView()方法。code
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor(); //首先初始化DecorView
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
//.........
}
複製代碼
能夠看到再說上面的代碼中首先初始化了DecorView也就是頂級View。接下來看下DecorView是如何初始化的。遞歸
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1); //建立DecorView
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor); //生成佈局
}
//........
}
protected DecorView generateDecor(int featureId) {
Context context;
if (mUseDecorContext) {
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, getContext().getResources());
if (mTheme != -1) {
context.setTheme(mTheme);
}
}
} else {
context = getContext();
}
return new DecorView(context, featureId, this, getAttributes());
}
複製代碼
從上面的代碼中能夠看到,在建立DecorView的過程當中,首先會建立一個DecorContext。這個類型的Context就是DecorView中要使用的Context,能夠看到這個Context是能夠操做主題的。若是Context是空,就使用Window的Context,而Window的Context來自於Activity。ci
在DecorView建立完成後,接下來就是生成佈局。這裏就開始了View的初始化過程。element
protected ViewGroup generateLayout(DecorView decor) {
//......
if (mIsFloating) {
setLayout(WRAP_CONTENT, WRAP_CONTENT);
setFlags(0, flagsToUpdate);
} else {
setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
}
//......
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); //生成佈局
//......
mDecor.finishChanging();
return contentParent;
}
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
mDecorCaptionView = createDecorCaptionView(inflater);
final View root = inflater.inflate(layoutResource, null); //初始化View
if (mDecorCaptionView != null) {
if (mDecorCaptionView.getParent() == null) {
addView(mDecorCaptionView,
new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mDecorCaptionView.addView(root,
new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
} else {
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
initializeElevation();
}
複製代碼
在上面的代碼中能夠看出,佈局是從LayoutInflater的inflate開始的。資源
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
//......
// Inflate all children under temp against its context.
rInflateChildren(parser, temp, attrs, true);
//......
return result;
}
}
void rInflate(XmlPullParser parser, View parent, Context context, AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
int type;
boolean pendingRequestFocus = false;
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
//......
final String name = parser.getName();
if (TAG_REQUEST_FOCUS.equals(name)) {
pendingRequestFocus = true;
consumeChildElements(parser);
} else if (TAG_TAG.equals(name)) {
//經過tag建立
parseViewTag(parser, parent, attrs);
} 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)) {
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);
rInflateChildren(parser, view, attrs, true);
viewGroup.addView(view, params);
}
}
}
複製代碼
一路跟代碼下去,咱們能夠看到在rInflate()方法中根據佈局XML文件開始遞歸的建立View樹。在這個過程當中每一個View都會被建立。能夠看到在createViewFromTag(parent, name, context, attrs)方法中使用了Context的參數,接下來繼續分析。
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) {
//這裏爲View建立了一個ContextThemeWrapper類型的Context
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();
}
if (name.equals(TAG_1995)) {
// Let's party like it's 1995!
return new BlinkLayout(context, attrs);
}
try {
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;
}
}
複製代碼
從上面的代碼中咱們就找到了答案,View的Context在這裏被建立。
在上面的分析中,咱們能夠知道View中的Context是ContextThemeWrapper類型的。在View被繪製以前的初始化過程當中被建立。固然根據Android的特性View的Context也得是一個ContextThemeWrapper類型的,由於View中也涉及到了Theme的操做。