千家信息网

怎么浅析Activity的渲染过程

发表于:2025-01-21 作者:千家信息网编辑
千家信息网最后更新 2025年01月21日,今天就跟大家聊聊有关怎么浅析Activity的渲染过程,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。####以下代码基于API251.Acti
千家信息网最后更新 2025年01月21日怎么浅析Activity的渲染过程

今天就跟大家聊聊有关怎么浅析Activity的渲染过程,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

####以下代码基于API25

1.Activity是如何展现在我们眼前的?

我大概知道UI方面是和Window相关的,而activity的attach方法会传入一个window对象,所以就从activity的attach方法入手:

    • attach方法里会创建一个PhoneWindow

    • activity在执行onCreate方法的时候都会执行一个方法performCreateCommon

    • 点击setEnterActivityOptions方法:

    注意window.getDecorView()这个方法



    /**
    * Retrieve the top-level window decor view (containing the standard
    * window frame/decorations and the client's content inside of that), which
    * can be added as a window to the window manager.
    *
    *

    Note that calling this function for the first time "locks in"
    * various window characteristics as described in
    * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}.


    *
    * @return Returns the top-level window decor view.
    */
    public abstract View getDecorView();


    官方的解释是说这个方法是获取了顶层window的decorview。

    这里的window很明显是PhoneWindow,看它对getDecorView的实现。

mDecor其实在PhoneWindow的构造方法里就已经生成了

preservedWindow向上追溯其来源:

从onCreate里其实根本就找不到activity显示相关的源码,于是只能看看其他人怎么分析的。

https://www.aliyun.com/jiaocheng/6995.html (道中重点)

https://www.cnblogs.com/snow-flower/p/6111599.html

https://blog.csdn.net/monkey646812329/article/details/52885835

if (r.window == null && !a.mFinished && willBeVisible) {    r.window = r.activity.getWindow();    View decor = r.window.getDecorView();    decor.setVisibility(View.INVISIBLE);    ViewManager wm = a.getWindowManager();    WindowManager.LayoutParams l = r.window.getAttributes();    a.mDecor = decor;    l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;    l.softInputMode |= forwardBit;    if (r.mPreserveWindow) {        a.mWindowAdded = true;        r.mPreserveWindow = false;        // Normally the ViewRoot sets up callbacks with the Activity        // in addView->ViewRootImpl#setView. If we are instead reusing        // the decor view we have to notify the view root that the        // callbacks may have changed.        ViewRootImpl impl = decor.getViewRootImpl();        if (impl != null) {            impl.notifyChildRebuilt();        }    }    if (a.mVisibleFromClient && !a.mWindowAdded) {        a.mWindowAdded = true;        wm.addView(decor, l);    }// If the window has already been added, but during resume// we started another activity, then don't yet make the// window visible.} else if (!willBeVisible) {    if (localLOGV) Slog.v(        TAG, "Launch " + r + " mStartedActivity set");    r.hideForNow = true;}

看上面的源码

得到PhoneWindow对象

   r.window = r.activity.getWindow();      View decor = r.window.getDecorView();

从PhoneWindow获取DecorView并赋值给activity

    a.mDecor = decor;

通过WindowManager添加decor

wm.addView(decor, l);

这样activity就展示在我们眼前了。关于WindowManager更加底层的源码后探讨。

2.Activity的setContentView方法

/** * Set the activity content from a layout resource.  The resource will be * inflated, adding all top-level views to the activity. * * @param layoutResID Resource ID to be inflated. * * @see #setContentView(android.view.View) * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams) */public void setContentView(@LayoutRes int layoutResID) {    getWindow().setContentView(layoutResID);    initWindowDecorActionBar();}/** * Set the activity content to an explicit view.  This view is placed * directly into the activity's view hierarchy.  It can itself be a complex * view hierarchy.  When calling this method, the layout parameters of the * specified view are ignored.  Both the width and the height of the view are * set by default to {@link ViewGroup.LayoutParams#MATCH_PARENT}. To use * your own layout parameters, invoke * {@link #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)} * instead. * * @param view The desired content to display. * * @see #setContentView(int) * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams) */public void setContentView(View view) {    getWindow().setContentView(view);    initWindowDecorActionBar();}/** * Set the activity content to an explicit view.  This view is placed * directly into the activity's view hierarchy.  It can itself be a complex * view hierarchy. * * @param view The desired content to display. * @param params Layout parameters for the view. * * @see #setContentView(android.view.View) * @see #setContentView(int) */public void setContentView(View view, ViewGroup.LayoutParams params) {    getWindow().setContentView(view, params);    initWindowDecorActionBar();}/** * Add an additional content view to the activity.  Added after any existing * ones in the activity -- existing views are NOT removed. * * @param view The desired content to display. * @param params Layout parameters for the view. */public void addContentView(View view, ViewGroup.LayoutParams params) {    getWindow().addContentView(view, params);    initWindowDecorActionBar();}

关于填充view,activity有4种方法。下面先讨论最常用的方法

public void setContentView(@LayoutRes int layoutResID) {    getWindow().setContentView(layoutResID);    initWindowDecorActionBar();}

第一步调用PhoneWindow的对应的方法(注意观察源码会发现,activity的绝大多数的方法都是在操作PhoneWindow)

第二步初始化ActionBar

先看看PhoneWindow的源码

@Overridepublic 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.    if (mContentParent == null) {        installDecor();    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {        mContentParent.removeAllViews();    }    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,                getContext());        transitionTo(newScene);    } else {        mLayoutInflater.inflate(layoutResID, mContentParent);    }    mContentParent.requestApplyInsets();    final Callback cb = getCallback();    if (cb != null && !isDestroyed()) {        cb.onContentChanged();    }    mContentParentExplicitlySet = true;}

最开始程序应该是走了installDecor方法

这个方法比较长,它负责的业务主要有以下几项。

  • 如果decor为null,创建PhoneWindow的decor,已经存在decor则与PhoneWindow绑定。

  • 如果mContentParent为null,通过decor创建mContentParent。

  • 转场动画的处理

private void installDecor() {    mForceDecorInstall = false;    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) {        mContentParent = generateLayout(mDecor);        // Set up decor part of UI to ignore fitsSystemWindows if appropriate.        mDecor.makeOptionalFitsSystemWindows();        final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(                R.id.decor_content_parent);        if (decorContentParent != null) {            mDecorContentParent = decorContentParent;            mDecorContentParent.setWindowCallback(getCallback());            if (mDecorContentParent.getTitle() == null) {                mDecorContentParent.setWindowTitle(mTitle);            }            final int localFeatures = getLocalFeatures();            for (int i = 0; i < FEATURE_MAX; i++) {                if ((localFeatures & (1 << i)) != 0) {                    mDecorContentParent.initFeature(i);                }            }            mDecorContentParent.setUiOptions(mUiOptions);            if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 ||                    (mIconRes != 0 && !mDecorContentParent.hasIcon())) {                mDecorContentParent.setIcon(mIconRes);            } else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 &&                    mIconRes == 0 && !mDecorContentParent.hasIcon()) {                mDecorContentParent.setIcon(                        getContext().getPackageManager().getDefaultActivityIcon());                mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;            }            if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 ||                    (mLogoRes != 0 && !mDecorContentParent.hasLogo())) {                mDecorContentParent.setLogo(mLogoRes);            }            // Invalidate if the panel menu hasn't been created before this.            // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu            // being called in the middle of onCreate or similar.            // A pending invalidation will typically be resolved before the posted message            // would run normally in order to satisfy instance state restoration.            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);            if (!isDestroyed() && (st == null || st.menu == null) && !mIsStartingWindow) {                invalidatePanelMenu(FEATURE_ACTION_BAR);            }        } else {            mTitleView = (TextView) findViewById(R.id.title);            if (mTitleView != null) {                if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {                    final View titleContainer = findViewById(R.id.title_container);                    if (titleContainer != null) {                        titleContainer.setVisibility(View.GONE);                    } else {                        mTitleView.setVisibility(View.GONE);                    }                    mContentParent.setForeground(null);                } else {                    mTitleView.setText(mTitle);                }            }        }        if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {            mDecor.setBackgroundFallback(mBackgroundFallbackResource);        }        // Only inflate or create a new TransitionManager if the caller hasn't        // already set a custom one.        if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) {            if (mTransitionManager == null) {                final int transitionRes = getWindowStyle().getResourceId(                        R.styleable.Window_windowContentTransitionManager,                        0);                if (transitionRes != 0) {                    final TransitionInflater inflater = TransitionInflater.from(getContext());                    mTransitionManager = inflater.inflateTransitionManager(transitionRes,                            mContentParent);                } else {                    mTransitionManager = new TransitionManager();                }            }            mEnterTransition = getTransition(mEnterTransition, null,                    R.styleable.Window_windowEnterTransition);            mReturnTransition = getTransition(mReturnTransition, USE_DEFAULT_TRANSITION,                    R.styleable.Window_windowReturnTransition);            mExitTransition = getTransition(mExitTransition, null,                    R.styleable.Window_windowExitTransition);            mReenterTransition = getTransition(mReenterTransition, USE_DEFAULT_TRANSITION,                    R.styleable.Window_windowReenterTransition);            mSharedElementEnterTransition = getTransition(mSharedElementEnterTransition, null,                    R.styleable.Window_windowSharedElementEnterTransition);            mSharedElementReturnTransition = getTransition(mSharedElementReturnTransition,                    USE_DEFAULT_TRANSITION,                    R.styleable.Window_windowSharedElementReturnTransition);            mSharedElementExitTransition = getTransition(mSharedElementExitTransition, null,                    R.styleable.Window_windowSharedElementExitTransition);            mSharedElementReenterTransition = getTransition(mSharedElementReenterTransition,                    USE_DEFAULT_TRANSITION,                    R.styleable.Window_windowSharedElementReenterTransition);            if (mAllowEnterTransitionOverlap == null) {                mAllowEnterTransitionOverlap = getWindowStyle().getBoolean(                        R.styleable.Window_windowAllowEnterTransitionOverlap, true);            }            if (mAllowReturnTransitionOverlap == null) {                mAllowReturnTransitionOverlap = getWindowStyle().getBoolean(                        R.styleable.Window_windowAllowReturnTransitionOverlap, true);            }            if (mBackgroundFadeDurationMillis < 0) {                mBackgroundFadeDurationMillis = getWindowStyle().getInteger(                        R.styleable.Window_windowTransitionBackgroundFadeDuration,                        DEFAULT_BACKGROUND_FADE_DURATION_MS);            }            if (mSharedElementsUseOverlay == null) {                mSharedElementsUseOverlay = getWindowStyle().getBoolean(                        R.styleable.Window_windowSharedElementsUseOverlay, true);            }        }    }}

对上面的方法主要节点剖析

  •  mContentParent = generateLayout(mDecor);

推荐结合这篇博客Andriod中Style/Theme原理以及Activity界面文件选取过程浅析来看这个方法的源码

protected ViewGroup generateLayout(DecorView decor) {    // Apply data from current theme.    TypedArray a = getWindowStyle();    ...//根据window style设置window的属性    WindowManager.LayoutParams params = getAttributes();    // Non-floating windows on high end devices must put up decor beneath the system bars and    // therefore must know about visibility changes of those.    if (!mIsFloating && ActivityManager.isHighEndGfx()) {        if (!targetPreL && a.getBoolean(                R.styleable.Window_windowDrawsSystemBarBackgrounds,                false)) {            setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,                    FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags());        }        if (mDecor.mForceWindowDrawsStatusBarBackground) {            params.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;        }    }    if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {        decor.setSystemUiVisibility(                decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);    }    if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion            >= android.os.Build.VERSION_CODES.HONEYCOMB) {        if (a.getBoolean(                R.styleable.Window_windowCloseOnTouchOutside,                false)) {            setCloseOnTouchOutsideIfNotSet(true);        }    }    if (!hasSoftInputMode()) {        params.softInputMode = a.getInt(                R.styleable.Window_windowSoftInputMode,                params.softInputMode);    }    if (a.getBoolean(R.styleable.Window_backgroundDimEnabled,            mIsFloating)) {        /* All dialogs should have the window dimmed */        if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) {            params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;        }        if (!haveDimAmount()) {            params.dimAmount = a.getFloat(                    android.R.styleable.Window_backgroundDimAmount, 0.5f);        }    }    if (params.windowAnimations == 0) {        params.windowAnimations = a.getResourceId(                R.styleable.Window_windowAnimationStyle, 0);    }    // The rest are only done if this window is not embedded; otherwise,    // the values are inherited from our container.    if (getContainer() == null) {        if (mBackgroundDrawable == null) {            if (mBackgroundResource == 0) {                mBackgroundResource = a.getResourceId(                        R.styleable.Window_windowBackground, 0);            }            if (mFrameResource == 0) {                mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0);            }            mBackgroundFallbackResource = a.getResourceId(                    R.styleable.Window_windowBackgroundFallback, 0);            if (false) {                System.out.println("Background: "                        + Integer.toHexString(mBackgroundResource) + " Frame: "                        + Integer.toHexString(mFrameResource));            }        }        if (mLoadElevation) {            mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);        }        mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);        mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT);    }    // Inflate the window decor.    int layoutResource;    int features = getLocalFeatures();    // System.out.println("Features: 0x" + Integer.toHexString(features));    if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {        layoutResource = R.layout.screen_swipe_dismiss;    } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {        if (mIsFloating) {            TypedValue res = new TypedValue();            getContext().getTheme().resolveAttribute(                    R.attr.dialogTitleIconsDecorLayout, res, true);            layoutResource = res.resourceId;        } else {            layoutResource = R.layout.screen_title_icons;        }        // XXX Remove this once action bar supports these features.        removeFeature(FEATURE_ACTION_BAR);        // System.out.println("Title Icons!");    } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0            && (features & (1 << FEATURE_ACTION_BAR)) == 0) {        // Special case for a window with only a progress bar (and title).        // XXX Need to have a no-title version of embedded windows.        layoutResource = R.layout.screen_progress;        // System.out.println("Progress!");    } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {        // Special case for a window with a custom title.        // If the window is floating, we need a dialog layout        if (mIsFloating) {            TypedValue res = new TypedValue();            getContext().getTheme().resolveAttribute(                    R.attr.dialogCustomTitleDecorLayout, res, true);            layoutResource = res.resourceId;        } else {            layoutResource = R.layout.screen_custom_title;        }        // XXX Remove this once action bar supports these features.        removeFeature(FEATURE_ACTION_BAR);    } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {        // If no other features and not embedded, only need a title.        // If the window is floating, we need a dialog layout        if (mIsFloating) {            TypedValue res = new TypedValue();            getContext().getTheme().resolveAttribute(                    R.attr.dialogTitleDecorLayout, res, true);            layoutResource = res.resourceId;        } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {            layoutResource = a.getResourceId(                    R.styleable.Window_windowActionBarFullscreenDecorLayout,                    R.layout.screen_action_bar);        } else {            layoutResource = R.layout.screen_title;        }        // System.out.println("Title!");    } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {        layoutResource = R.layout.screen_simple_overlay_action_mode;    } else {        // Embedded, so no decoration is needed.        layoutResource = R.layout.screen_simple;        // System.out.println("Simple!");    }    mDecor.startChanging();    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);    if (contentParent == null) {        throw new RuntimeException("Window couldn't find content container view");    }    if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {        ProgressBar progress = getCircularProgressBar(false);        if (progress != null) {            progress.setIndeterminate(true);        }    }    if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {        registerSwipeCallbacks();    }    // Remaining setup -- of background and title -- that only applies    // to top-level windows.    if (getContainer() == null) {        final Drawable background;        if (mBackgroundResource != 0) {            background = getContext().getDrawable(mBackgroundResource);        } else {            background = mBackgroundDrawable;        }        mDecor.setWindowBackground(background);        final Drawable frame;        if (mFrameResource != 0) {            frame = getContext().getDrawable(mFrameResource);        } else {            frame = null;        }        mDecor.setWindowFrame(frame);        mDecor.setElevation(mElevation);        mDecor.setClipToOutline(mClipToOutline);        if (mTitle != null) {            setTitle(mTitle);        }        if (mTitleColor == 0) {            mTitleColor = mTextColor;        }        setTitleColor(mTitleColor);    }    mDecor.finishChanging();    return contentParent;}

下面看看PhoneWindow的setContentView中的installDecor之后的方法

//有转场动画,则做一个动画特效。if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {    final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,            getContext());    transitionTo(newScene);} else {    //没有动画直接将布局inflate到mConentParent容器里    mLayoutInflater.inflate(layoutResID, mContentParent);}mContentParent.requestApplyInsets();//这个CallBack其实是在Activity里实现的final Callback cb = getCallback();//将布局改变以的事件回调给activityif (cb != null && !isDestroyed()) {    cb.onContentChanged();}

所以由以上的源码分析我们可知,activity的setContentView会将布局inflate到PhoneWindow的decorView里,WindowManager再将decorview添加显示出来 。

看完上述内容,你们对怎么浅析Activity的渲染过程有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注行业资讯频道,感谢大家的支持。

0