iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >Android Activity 启动流程 二:setContentView
  • 633
分享到

Android Activity 启动流程 二:setContentView

android启动流程java面试setcontentview 2023-09-03 07:09:18 633人浏览 泡泡鱼
摘要

关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、商业变现、人工智能等,希望大家多多支持。 目录 一、概

关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。
专注于分享各领域原创系列文章 ,擅长java后端移动开发、商业变现、人工智能等,希望大家多多支持。

目录

在这里插入图片描述

- > 上 篇,Activity创建后,还只是调用了onCreate方法,页面并没有展示出来,还需要调用setContentView方法,加载页面布局,并进行渲染,最后展示。

一、概览

源码基于Android 12
看代码前,我们先上一张Activity,Window, DecorView三者之间的关系图
在这里插入图片描述

DecorView是整个ViewTree的最顶层View,它是一个FrameLayout布局,代表了整个应用的界面。
在该布局下面,有标题view和内容view两个子元素。

Activity setContentView 核心就是PhoneWindow的setContentView方法,其主要干了两件事:
1.完成DecorView的创建与加载,这个DecorView会在后面onresume后添加到window中
2.将MainActivity的布局加载到DecorView内的一个ViewGroup中

创建DecorView,即installDecor方法,其内部用到了两个核心的方法:
1.generateDecor方法创建出DecorView对象
2.generateLayout方法完成这个DecorView对象的布局加载,并完成了MainActivity的父容器的赋值(即contentParent变量)

先上一张流程图
在这里插入图片描述

二、setContentView()

我们跟踪一下源码,看看这个方法是怎么做的

    public void setContentView(View view) {        getWindow().setContentView(view);        initWindowDecorActionBar();    }

这里window即为 PhoneWindow,
window的初始化是在 Acticity 创建的时候初始化, 在Acticity对象创建后,会调用attach方法

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)    final void attach(Context context, ActivityThread aThread, ...) {        attacHBaseContext(context);        mFragments.attachHost(null );        mWindow = new PhoneWindow(this, window, activityConfiGCallback);        mWindow.setWindowControllerCallback(mWindowControllerCallback);        mWindow.setCallback(this);        mWindow.setWindowManager(                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),                mToken, mComponent.flattenToString(),                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);    }

PhoneWindow.java

    @Override    public void setContentView(int layoutResID) {        根view 为空,则初始 mDecor view        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 {                        // 将布局文件添加到 mContentParent            mLayoutInflater.inflate(layoutResID, mContentParent);        }        mContentParent.requestApplyInsets();        final Callback cb = getCallback();        if (cb != null && !isDestroyed()) {            cb.onContentChanged();        }        mContentParentExplicitlySet = true;    }    调用installDecor()进行DecorView的初始化    private void installDecor() {        mForceDecorInstall = false;        if (mDecor == null) {            // 创建出一个DecorView并返回            mDecor = generateDecor(-1);        } else {            mDecor.setWindow(this);        }        if (mContentParent == null) {            //对mContentParent进行赋值,作为Activity布局的父容器,            mContentParent = generateLayout(mDecor);        }    }

首先判断了mContentParent是否为null,如果为空则执行installDecor()方法,同时初始化一个mContentParent,这个就是Activity布局的父容器

三、inflate

LayoutInflater.java

    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {        final Resources res = getContext().getResources();        View view = tryInflatePrecompiled(resource, res, root, attachToRoot);        if (view != null) {            return view;        }        XmlResourceParser parser = res.getLayout(resource);        try {            return inflate(parser, root, attachToRoot);        } finally {        }    }
    private @Nullable    View tryInflatePrecompiled(@LayoutRes int resource, Resources res, @Nullable ViewGroup root, boolean attachToRoot) {        try {            Class clazz = Class.forName("" + pkg + ".CompiledView", false, mPrecompiledClassLoader);            Method inflater = clazz.getMethod(layout, Context.class, int.class);            View view = (View) inflater.invoke(null, mContext, resource);            if (view != null && root != null) {                XmlResourceParser parser = res.getLayout(resource);                try {                    AttributeSet attrs = Xml.asAttributeSet(parser);                    advanceToRootnode(parser);                    ViewGroup.LayoutParams params = root.generateLayoutParams(attrs);                    if (attachToRoot) {                        root.addView(view, params);                    } else {                        view.setLayoutParams(params);                    }                } finally {                    parser.close();                }            }            return view;        } catch (Throwable e) {        } finally {        }        return null;    }

布局就是这么添加进mContentParent中的。
在这里插入图片描述

但是,view还是没有显示出来的,此时代码所做的事情仅仅只是加载了布局,并没有开始view的测量、布局、绘制工作。
对应方法是onMeasure, onLayout, onDraw,这些操作在后面

四、view的绘制展示

每一个Activity组件都有一个关联的Window对象,用来描述一个应用程序窗口。每一个应用程序窗口内部又包含一个View对象,用来描述应用程序窗口的视图。
我们再看下图:
在这里插入图片描述

Activity#onResume()之后才是布局由不可见变为可见的,我们看源码

4.1 Activity.onResume

ActivityThread.java
下面这个方法是在Activity onCreate创建后调用的,handleResumeActivity,不清楚的可以看前面app启动文章.

    @Override    public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,        boolean isForward, String reason) {        // 这个方法会调用 activity的 onResume 方法        if (!perfORMResumeActivity(r, finalStateRequest, reason)) {            return;        }                final Activity a = r.activity;        // window 未被添加进 windowmanager        if (r.window == null && !a.mFinished && willBeVisible) {            // window            r.window = r.activity.getWindow();                // decorView            View decor = r.window.getDecorView();            decor.setVisibility(View.INVISIBLE);                        ViewManager wm = a.getWindowManager();                        WindowManager.LayoutParams l = r.window.getAttributes();            a.mDecor = decor;        } else if (!willBeVisible){        }        if (a.mVisibleFromClient) {            if (!a.mWindowAdded) {    // DecorView 添加到 window                wm.addView(decor, l);            } else {                a.onWindowAttributesChanged(l);            }        }                if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {            使布局可见            if (r.activity.mVisibleFromClient) {                r.activity.makeVisible();            }        }        r.nextIdle = mNewActivities;    }        

在上面的代码中,会先调用Activity的onResume, 然后再是view的绘制,最后将DecorView 设置 可见;

4.2 WindowManager addView

WindowManagerImpl.java

    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();    ...    @Override    public void addView(View view, ViewGroup.LayoutParams params) {        mGlobal.addView(view, params, mDisplay, mParentWindow);    }

这里也是一个空壳代码,调用WindowManagerGlobal

WindowManagerGlobal.java

    public void addView(View view, ViewGroup.LayoutParams params,            Display display, Window parentWindow, int userId) {        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;        ViewRootImpl root;        View panelParentView = null;// 加        synchronized (mLock) {//实例化ViewRootImpl类            root = new ViewRootImpl(view.getContext(), display);            view.setLayoutParams(wparams);            mViews.add(view);            mRoots.add(root);            mParams.add(wparams);            // do this last because it fires off messages to start doing things            try {// //调用ViewRootImpl.setView方法,把DecorView作为参数传递进去                root.setView(view, wparams, panelParentView, userId);            } catch (RuntimeException e) {                // BadTokenException or InvalidDisplayException, clean up.                if (index >= 0) {                    removeViewLocked(index, true);                }                throw e;            }        }    }

在方法内部,会通过跨进程方式向WMS(WindowManagerService)发起一个调用,从而将DecorView最终加到Window上,在这个过程中,ViewRootImpl、DecorView和WMS会彼此关联。
最后,WMS调用ViewRootImpl.performTraversals 方法开始View的测量、布局、绘制。

4.3 ViewRootImpl

一个 Window 对应着一个 ViewRootImpl 和 一个 VIew。这个 View 就是被 ViewRootImpl 操作的.

从上面代码,我们可以看到,ViewRootImpl的初始化是在WindowManagerGlobal的addView中

ViewRootImpl.java

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) {        synchronized (this) {            if (mView == null) {                mView = view;                        mAdded = true;                int res;                         // 刷新布局的操作,触发view的measure -> layout -> draw 操作                requestLayout();    try {                    //将 View 添加到 WMS 中                    res = mwindowsession.addToDisplayAsUser(mWindow, mWindowAttributes, ...);        } catch (RemoteException e) {                        } finally {                        }                // Set up the input pipeline. 设置了一系列的输入通道                CharSequence counterSuffix = attrs.getTitle();                mSyntheticInputStage = new SyntheticInputStage();                InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);                }        }    }

首先会调用requestLayout方法来刷新布局,然后将 View 添加到 WMS 中,最后是view事件的处理;
view事件的处理,最后还是会回到了 PhoneWindow 中的 DecorView 来处理,剩下的就是从 DecorView 开始将事件层层传递给内部的子 View 中了
这里就不展开

ViewGroup.java    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        }
    @Override    public void requestLayout() {        if (!mHandlingLayoutInLayoutRequest) {            checkThread();            mLayoutRequested = true;            scheduleTraversals();        }    }    final class TraversalRunnable implements Runnable {        @Override        public void run() {            doTraversal();        }    }    void doTraversal() {        if (mTraversalScheduled) {            performTraversals();        }    }

requestLayout()最终会调用到performTraversals,在这个方法中会调用 View 的 measure() ,layout() ,draw() 方法。
我们看下面源码

private void performTraversals() {final View host = mView;if (mFirst || windowShouldResize || viewVisibilityChanged || params != null|| mForceNextWindowRelayout) {try {if (!mPendingMergedConfiguration.equals(mLastReportedMergedConfiguration)) {performConfigurationChange(new MergedConfiguration(mPendingMergedConfiguration),!mFirst, INVALID_DISPLAY );}} catch (RemoteException e) {}if (!mStopped || wasReportNextDraw) {                //View 的测量performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);if (measureAgain) {                    //View 的测量performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);}layoutRequested = true;}}} else {}if (didLayout) {        // View 的布局performLayout(lp, mWidth, mHeight);}if (!cancelDraw) {        // View 的绘制performDraw();} else {}mIsInTraversal = false;}

4.4 addWindow & makeVisible

com.android.server.wm.Session.java

    @Override    public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,            int viewVisibility, int displayId, InsetsVisibilities requestedVisibilities,            InputChannel outInputChannel, InsetsState outInsetsState,            InsetsSourceControl[] outActiveControls) {        return mService.addWindow(this, window, attrs, viewVisibility, displayId,                UserHandle.getUserId(mUid), requestedVisibilities, outInputChannel, outInsetsState,                outActiveControls);    }

Activity.java

DecorView的状态设置为可见,那么布局也就可见了    void makeVisible() {        if (!mWindowAdded) {            ViewManager wm = getWindowManager();            wm.addView(mDecor, getWindow().getAttributes());            mWindowAdded = true;        }        mDecor.setVisibility(View.VISIBLE);    }

五、 推荐阅读

Java 专栏

SQL 专栏

数据结构与算法

Android学习专栏

来源地址:https://blog.csdn.net/fumeidonga/article/details/132146986

--结束END--

本文标题: Android Activity 启动流程 二:setContentView

本文链接: https://www.lsjlt.com/news/391824.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作