View加载流程

2017-12-21  本文已影响0人  Noblel

前言

有人说读源码很麻烦我们为什么要读?主要是为了以后的需求做准备。比如我们了解了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);
}
上一篇 下一篇

猜你喜欢

热点阅读