关于作者: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变量)
先上一张流程图
我们跟踪一下源码,看看这个方法是怎么做的
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布局的父容器
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,这些操作在后面
每一个Activity组件都有一个关联的Window对象,用来描述一个应用程序窗口。每一个应用程序窗口内部又包含一个View对象,用来描述应用程序窗口的视图。
我们再看下图:
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 设置 可见;
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的测量、布局、绘制。
一个 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;}
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); }
来源地址: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文档到电脑,方便收藏和打印~
2024-01-21
2023-10-28
2023-10-28
2023-10-27
2023-10-27
2023-10-27
2023-10-27
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0