前言
有人说读源码很麻烦我们为什么要读?主要是为了以后的需求做准备。比如我们了解了View是怎么展示出来就可以做一些其他操作。比如换肤。读源码方式:耐心 看不懂的跳过。这也是自己学习的技巧。共同学习吧~
setContentView(R.layout.activity_main)加载过程
Activity:
/**
* getWindow()返回的是PhoneWindow,PhoneWindow是Window的唯一实现
* 点getWindow-->mWindow--> Ctrl+F 查找 "mWindow ="
*/
public void setContentView(@LayoutRes int layoutResID) {
//我们只关注xml文件怎么显示在界面的
getWindow().setContentView(layoutResID);
//初始化ActionBar
initWindowDecorActionBar();
}
PhoneWindow:
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
//第一次调用则installDecor
if (mContentParent == null) {
installDecor();
//是否设置FEATURE_CONTENT_TRANSITIONS(Window属性默认false)
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
//没有就移除所有子View
mContentParent.removeAllViews();
}
//hasFeature(FEATURE_CONTENT_TRANSITIONS)默认是false
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
//将资源文件通过LayoutInflater对象解析并且添加到mContentParent--》执行到方法1
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
private void installDecor() {
mForceDecorInstall = false;
//如果mDecor对象为空,则创建一个DecorView对象,这是一个FrameLayout的子类
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
//根据窗口的风格修饰,选择对应的修饰布局文件。
//并且将id为com.android.internal.R.id.content的FrameLayout赋值给mContentParent
mContentParent = generateLayout(mDecor);
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeOptionalFitsSystemWindows();
....
//初始化一些属性值
}
}
把控件写在xml文件中然后通过LayoutInflater初始化一个view
LayoutInflater:
/**
* 方法1
*/
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
//执行方法2
return inflate(resource, root, root != null);
}
/**
* 方法2
*/
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
.....
//XmlResourceParser是一个接口,继承于XmlPullParser,所以是通过Pull方式解析的xml
//具体查看Part2
final XmlResourceParser parser = res.getLayout(resource);
try {
//执行到方法3(重点)
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
/**
* 方法3(重点)
*/
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
final Context inflaterContext = mContext;
//获取一个AttributeSet实例,XmlPullParser(parser)是继承个AttributeSet的,所以可以直接作为个AttributeSet对象
final AttributeSet attrs = Xml.asAttributeSet(parser);
//构造函数的参数,第一个值是此view运行所在的对象context
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;
try {
....
//获取根节点标签的名字
final String name = parser.getName();
....
//如果根节点是一个merge,那么必须手动设置此父节点的
//根节点是merge的必须被其他layout包含
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(parser, root, inflaterContext, attrs, false);
} else {
//inflate的xml文件的root view
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
...
// 创建layout的参数
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// 设置参数
temp.setLayoutParams(params);
}
}
...
//加载temp下所有子view
// 调用方法4
rInflateChildren(parser, temp, attrs, true);
if (DEBUG) {
System.out.println("-----> done inflating children");
}
// 如果给了root,那么就把此view加到root中去
if (root != null && attachToRoot) {
root.addView(temp, params);
}
// 决定是否返回xml中的根视图
if (root == null || !attachToRoot) {
result = temp;
}
}
}
.....
finally {
// Don't retain static reference on context.
// 不要在静态文上下保留context引用
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
return result;
}
}
/**
* 方法4
*/
final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
//调用方法5
rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
}
/**
* 方法5(重要)
* 从上至下初始化所欲子View和子View的子View
*/
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) {
//判断是否是开始节点
if (type != XmlPullParser.START_TAG) {
continue;
}
final String name = parser.getName();
if (TAG_REQUEST_FOCUS.equals(name)) {
pendingRequestFocus = true;
consumeChildElements(parser);
} 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,会执行到方法6
final View view = createViewFromTag(parent, name, context, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
//又调用了方法4然后执行方法5,递归调用
rInflateChildren(parser, view, attrs, true);
viewGroup.addView(view, params);
}
}
if (pendingRequestFocus) {
parent.restoreDefaultFocus();
}
if (finishInflate) {
//完成所有加载
parent.onFinishInflate();
}
}
/**
* 方法6(重要)
*/
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
}
....
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 {
//这里只是为了判断xml文件中tag的属性是否加了包名,比如TextView就会返回-1,自定义的会执行else那个方法
if (-1 == name.indexOf('.')) {
//最终会执行createView(name, "android.view.", attrs)
//这就是我们常见的View比如android.view.TextView
view = onCreateView(parent, name, attrs);
} else {
//和上面的方法最终执行到一个方法里面,就是方法7
view = createView(name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
return view;
}
...
}
/**
* 方法7(重要)
* 真正创建一个view的方法,此方法时利用反射获取构造器来实例对象而不是直接new出来。
* 同一个类名的不同对象,可以直接得到缓存的构造器直接获取一个构造器对象实例。而不需要
* 重复进行new操作。
*/
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
//做了缓存,从HashMap中拿数据
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) {
// 缓存中没有这个view,那就添加
//通过类名加载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);
}
}
//通过参数类型活的一个构造器,参数列表为context,attrs
constructor = clazz.getConstructor(mConstructorSignature);
//禁用访问控制权限,使private失效
constructor.setAccessible(true);
//添加到缓存中
sConstructorMap.put(name, constructor);
} else {
// If we have a filter, apply it to cached constructor
.....
}
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,args[0]就是context前面初始化好了
args[1] = attrs;
//通过反射new一个对象,大功告成
final View view = constructor.newInstance(args);
//ViewStub是一个轻量级的View,是一个看不见,不占布局位置的控件。具体了解相关资料
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;
}
....
}
Xml解析过程
Resources:
public XmlResourceParser getLayout(@LayoutRes int id) throws NotFoundException {
//执行方法1
return loadXmlResourceParser(id, "layout");
}
/**
* 方法1
*/
@NonNull
XmlResourceParser loadXmlResourceParser(@AnyRes int id, @NonNull String type)
throws NotFoundException {
//保存一些有关resource的数据,
final TypedValue value = obtainTempTypedValue();
try {
final ResourcesImpl impl = mResourcesImpl;
//执行方法2
impl.getValue(id, value, true);
if (value.type == TypedValue.TYPE_STRING) {
//执行方法3
return impl.loadXmlResourceParser(value.string.toString(), id,
value.assetCookie, type);
}
throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+ " type #0x" + Integer.toHexString(value.type) + " is not valid");
} finally {
//释放临时TypedValue
releaseTempTypedValue(value);
}
}
/**
* 方法2
* id表示要查找的控件id,
* outValue是用来保存一些属性相关信息
* resolveRefs,如果是true则表明这个不是资源文件,仅仅是一个引用,如果为false则表明是资源文件本身
*/
void getValue(@AnyRes int id, TypedValue outValue, boolean resolveRefs)
throws NotFoundException {
//具体看AssetManager类的getResourceValue,会调用native方法。(可以看延伸里面的内容)
boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs);
if (found) {
//通过id属性找到了xml文件中的标签
return;
}
throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id));
}
/**
* 方法3
*/
@NonNull
XmlResourceParser loadXmlResourceParser(@NonNull String file, @AnyRes int id, int assetCookie,
@NonNull String type)
throws NotFoundException {
if (id != 0) {
try {
synchronized (mCachedXmlBlocks) {
//获取缓存
final int[] cachedXmlBlockCookies = mCachedXmlBlockCookies;
final String[] cachedXmlBlockFiles = mCachedXmlBlockFiles;
final XmlBlock[] cachedXmlBlocks = mCachedXmlBlocks;
// First see if this block is in our cache.
final int num = cachedXmlBlockFiles.length;
for (int i = 0; i < num; i++) {
if (cachedXmlBlockCookies[i] == assetCookie && cachedXmlBlockFiles[i] != null
&& cachedXmlBlockFiles[i].equals(file)) {
return cachedXmlBlocks[i].newParser();
}
}
//第一次加载,没有缓存,会打开这个文件获取一个xml数据块对象(可以看延伸里面的内容),执行方法3
final XmlBlock block = mAssets.openXmlBlockAsset(assetCookie, file);
//缓存id和block,以后同样的id直接从缓存中获取block
if (block != null) {
final int pos = (mLastCachedXmlBlockIndex + 1) % num;
mLastCachedXmlBlockIndex = pos;
final XmlBlock oldBlock = cachedXmlBlocks[pos];
if (oldBlock != null) {
oldBlock.close();
}
cachedXmlBlockCookies[pos] = assetCookie;
cachedXmlBlockFiles[pos] = file;
cachedXmlBlocks[pos] = block;
return block.newParser();
}
}
} catch (Exception e) {
final NotFoundException rnf = new NotFoundException("File " + file
+ " from xml type " + type + " resource ID #0x" + Integer.toHexString(id));
rnf.initCause(e);
throw rnf;
}
}
throw new NotFoundException("File " + file + " from xml type " + type + " resource ID #0x"
+ Integer.toHexString(id));
}
findViewById(resId)过程
View:
@Nullable
public final <T extends View> T findViewById(@IdRes int id) {
if (id == NO_ID) {
return null;
}
//调用方法2
return findViewTraversal(id);
}
/**
* 方法2(遍历的查找view)
*/
protected <T extends View> T findViewTraversal(@IdRes int id) {
//mID = a.getResourceId(attr, NO_ID),这是做了一下判断如果相同就返回
if (id == mID) {
//到这里我们就没有思路了那我们就去setId找一下mID是怎么设置的
return (T) this;
}
return null;
}
public void setId(@IdRes int id) {
mID = id;
if (mID == View.NO_ID && mLabelForId != View.NO_ID) {
//方法3
mID = generateViewId();
}
}
/**
* 方法3
*/
public static int generateViewId() {
for (;;) {
final int result = sNextGeneratedId.get();
// aapt-generated IDs have the high byte nonzero; clamp to the range under that.
int newValue = result + 1;
if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
if (sNextGeneratedId.compareAndSet(result, newValue)) {
return result;
}
}
}
.....等等 我们这样分析下去好像并没有结果,我们知道一般就是ViewGroup会findViewById,所以看一下ViewGroup。
ViewGroup:
/**
* 果然重写了。很开心
*/
@Override
protected <T extends View> T findViewTraversal(@IdRes int id) {
if (id == mID) {
return (T) this;
}
final View[] where = mChildren;
final int len = mChildrenCount;
for (int i = 0; i < len; i++) {
View v = where[i];
//若果ViewGroup的id等于要找的id就返回ViewGroup,不等于就遍历ViewGroup
if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
v = v.findViewById(id);
if (v != null) {
return (T) v;
}
}
}
return null;
}
延伸,大致了解下就行不必深究。
AssetManager:
final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
boolean resolveRefs) {
synchronized (this) {
//执行native方法1
final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs);
if (block < 0) {
return false;
}
// Convert the changing configurations flags populated by native code.
outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
outValue.changingConfigurations);
if (outValue.type == TypedValue.TYPE_STRING) {
outValue.string = mStringBlocks[block].get(outValue.data);
}
return true;
}
}
/** native方法1,对应...\frameworks\base\core\jni\android_util_AssetManager.cpp */
private native final int loadResourceValue(int ident, short density, TypedValue outValue,
boolean resolve);
/** native方法2,对应...\frameworks\base\core\jni\android_util_AssetManager.cpp */
private native final long openXmlAssetNative(int cookie, String fileName);
/*package*/ final XmlBlock openXmlBlockAsset(int cookie, String fileName)
throws IOException {
synchronized (this) {
if (!mOpen) {
throw new RuntimeException("Assetmanager has been closed");
}
//执行native方法2
long xmlBlock = openXmlAssetNative(cookie, fileName);
if (xmlBlock != 0) {
XmlBlock res = new XmlBlock(this, xmlBlock);
incRefsLocked(res.hashCode());
return res;
}
}
throw new FileNotFoundException("Asset XML file: " + fileName);
}
android_util_AssetManager.cpp:
//对应native方法1
static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz,
jint ident,
jshort density,
jobject outValue,
jboolean resolve)
{
if (outValue == NULL) {
jniThrowNullPointerException(env, "outValue");
return 0;
}
AssetManager* am = assetManagerForJavaObject(env, clazz);
if (am == NULL) {
return 0;
}
const ResTable& res(am->getResources());
Res_value value;
ResTable_config config;
uint32_t typeSpecFlags;
//加载二进制layout文件
ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config);
if (kThrowOnBadId) {
if (block == BAD_INDEX) {
jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
return 0;
}
}
uint32_t ref = ident;
//根据传过来的resolve继续进一步加载,true表示是引用,false表示就是文件本身不需要进行下面的操作。
if (resolve) {
block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config);
if (kThrowOnBadId) {
if (block == BAD_INDEX) {
jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
return 0;
}
}
}
//根据block决定是否将二进制layout拷贝到value中
if (block >= 0) {
return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config);
}
return static_cast<jint>(block);
}
//对应native方法2
static jlong android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz,
jint cookie,
jstring fileName)
{
AssetManager* am = assetManagerForJavaObject(env, clazz);
if (am == NULL) {
return 0;
}
ALOGV("openXmlAsset in %p (Java object %p)\n", am, clazz);
ScopedUtfChars fileName8(env, fileName);
if (fileName8.c_str() == NULL) {
return 0;
}
int32_t assetCookie = static_cast<int32_t>(cookie);
Asset* a = assetCookie
? am->openNonAsset(assetCookie, fileName8.c_str(), Asset::ACCESS_BUFFER)
: am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER, &assetCookie);
if (a == NULL) {
jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
return 0;
}
const DynamicRefTable* dynamicRefTable =
am->getResources().getDynamicRefTableForCookie(assetCookie);
ResXMLTree* block = new ResXMLTree(dynamicRefTable);
status_t err = block->setTo(a->getBuffer(true), a->getLength(), true);
a->close();
delete a;
if (err != NO_ERROR) {
jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
return 0;
}
return reinterpret_cast<jlong>(block);
}
网友评论