android activity view,Android从Activity启动到View显示中间发生了什么?

前言

在Activity的启动过程中,通常我们在onCreate生命周期中调用setContentView方法设置布局文件(即xml文件),似乎这样就完成了布局文件对应的View的绘制及显示,那么背后具体的原理是什么呢?

👉 Activity onCreate生命周期

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

👇 布局文件

setContentView(R.layout.activity_main);

}

很明显布局文件对应的View的绘制和显示是伴随着Activity的生命周期而进行的。在Activity的启动过程中,ActivityThread类中的handleLaunchActivity,performLaunchActivity,handleResumeActivity这3个主要的方法完成了Activity的创建到启动工作,完成了Activity的onCreate、onStart、onResume这三个生命周期的执行。

让我们伴随着Activity的生命周期执行过程,来具体进行分析和相关源码解读。先来张总结本文的图:

96bb79c88c4d

Activity Window DecorView.png

Activity启动过程

我们按照Activity的启动过程中涉及到的ActivityThread类三个方法来逐步说明,首先来看handleLaunchActivity方法。

1. ActivityThread#handleLaunchActivity()方法:调用performLaunchActivity()和handleResumeActivity()方法

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {

...

// 👉 此方法中完成 Activity 生命周期的 onCreate 和 onStart 方法

Activity a = performLaunchActivity(r, customIntent);

if (a != null) {

...

// 👉 此方法中完成 Activity 生命周期的 onResume 方法

handleResumeActivity(r.token, false, r.isForward,

!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);

...

}

...

}

handleLaunchActivity()方法中主要调用了performLaunchActivity()方法和handleResumeActivity()方法,performLaunchActivity()中完成onCreate和onStart回调;handleResumeActivity()中完成onResume回调。

2. ActivityThread#performLaunchActivity()方法:执行onCreate、onStart生命周期

performLaunchActivity()方法中主要做的工作包括:

1️⃣ 创建Activity实例:调用mInstrumentation.newActivity方法来创建Activity对象;

2️⃣ 创建Application对象,如果已经创建就不再创建,一个进程只有一个Application对象;

3️⃣ 调用Activity的attach方法,在其中创建了PhoneWindow对象(有关PhoneWindow对象下文再慢慢解释);

4️⃣ 调用了Instrumentation的callActivityOnCreate方法,从而间接执行了onCreate生命周期;

5️⃣ 调用了Activity的performStart方法,从而间接执行了onStart生命周期。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

...

// 👉 通过反射创建 Activity 实例

java.lang.ClassLoader cl = r.packageInfo.getClassLoader();

activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);

// 👉 通过反射构建 Application,如果已经构建则不会重复构建,一个进程只能有一个 Application

Application app = r.packageInfo.makeApplication(false, mInstrumentation);

if (activity != null) {

Context appContext = createBaseContextForActivity(r, activity);

...

// 👉 在这里实例化了 PhoneWindow,并将该 Activity 设置为 PhoneWindow 的 Callback 回调;

// 初始化 WindowManager

activity.attach(appContext, this, getInstrumentation(), r.token,

r.ident, app, r.intent, r.activityInfo, title, r.parent,

r.embeddedID, r.lastNonConfigurationInstances, config);

...

// 👉 调用了 Activity 的 performCreate 方法,间接调用了 Activity 的 onCreate 方法

mInstrumentation.callActivityOnCreate(activity, r.state);

// 👉 调用 Activity 的 onStart 方法

if (!r.activity.mFinished) {

activity.performStart();

r.stopped = false;

}

...

}

}

performLaunchActivity()方法的前两步工作:Activity对象和Application对象的创建过程,这里不再具体描述,可以参考Android Activity生命周期,启动模式,启动过程详解。

下面重点看下attach()方法,其中创建了PhoneWindow对象,而PhoneWindow是Window的一个子类(其实也是唯一子类)。

要想理解PhoneWindow的作用,肯定要先知道Window这个概念。

Window:视图容器

Window代表一个窗口,是视图的容器。Android中的视图是以View树的形式组织的,而View树必须依附在Window上才能工作,一个Window对应着一个View树。Activity并不负责视图控制,它只是控制生命周期和处理事件,真正控制视图的是Window。

启动Activity时会创建一个Window,显示Dialog时也会创建一个Window,而显示Toast时也会创建Window,因此Activity内部可以有多个Window。由于View的测量、布局、绘制只是在View树内进行的,因此一个Window内View的改动不会影响到另一个Window。Window是一个抽象类,它只有一个实现类PhoneWindow,也就是说在PhoneWindow中完成了Window的真正工作。

Activity#attach()方法:实例化PhoneWindow

介绍完Window和PhoneWindow后,再回到attach方法。其中主要做了以下两步工作:

1️⃣ 实例化PhoneWindow:直接new一个PhoneWindow对象;

2️⃣ 为Activity的Window设置WindowManager:Android 中对 Window 的管理都是通过 WindowManager来完成的,创建 PhoneWindow 之后还会为该 Window 对象设置 WindowManager ,WindowManager 是一个接口,继承 ViewManager 接口,从这里也能看出对 Window 的操作其实就是对 View 的操作。WindowManager 的实现类是 WindowMangerImpl 。具体关于 Window和WindowManager 的关系下文再说。

final void attach(Context context, ActivityThread aThread,

Instrumentation instr, IBinder token, int ident,

Application application, Intent intent, ActivityInfo info,

CharSequence title, Activity parent, String id,

NonConfigurationInstances lastNonConfigurationInstances,

Configuration config, String referrer, IVoiceInteractor voiceInteractor,

Window window, ActivityConfigCallback activityConfigCallback) {

...

// 👉 实例化 PhoneWindow

mWindow = new PhoneWindow(this, window, activityConfigCallback);

mWindow.setWindowControllerCallback(this);

mWindow.setCallback(this);

mWindow.setOnWindowDismissedCallback(this);

...

// 👉 为 Activity 的 Window 设置 WindowManager

mWindow.setWindowManager(

(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),

mToken, mComponent.flattenToString(),

(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);

mWindowManager = mWindow.getWindowManager();

...

}

Activity#onCreate生命周期:调用setContentView方法(设置Activity对应的布局)

在performLaunchActivity()方法的第4️⃣步中,调用了Instrumentation的callActivityOnCreate方法,源码较为简单,如下:

public void callActivityOnCreate(Activity activity, Bundle icicle) {

prePerformCreate(activity);

activity.performCreate(icicle);

postPerformCreate(activity);

}

// 👉 然后调用 Activity 的 performCreate 方法

final void performCreate(Bundle icicle) {

performCreate(icicle, null);

}

// 👉 执行 onCreate 生命周期

final void performCreate(Bundle icicle, PersistableBundle persistentState) {

....

if (persistentState != null) {

onCreate(icicle, persistentState);

} else {

onCreate(icicle);

}

......

}

Activity#setContentView方法

在onCreate方法中,我们通过setContentView来加载我们定义的布局文件。Activity的源码中提供了三个重载的setContentView方法,如下:

// 👉 最常用的 setContentView 方法

public void setContentView(int layoutResID) {

getWindow().setContentView(layoutResID);

initWindowDecorActionBar();

}

public void setContentView(View view) {

getWindow().setContentView(view);

initWindowDecorActionBar();

}

public void setContentView(View view, ViewGroup.LayoutParams params) {

getWindow().setContentView(view, params);

initWindowDecorActionBar();

}

这三种方法都先调用了getWindow()的setContentView方法,而getWindow()方法返回的正是之前创建的PhoneWindow对象;然后调用Activity的initWindowDecorActionBar方法。

// 👉 Activity 中 Window 成员变量

private Window mWindow;

public Window getWindow() {

// 👉 这个 mWindow 是在 attach 方法中设置的;

// 👉 mWindow 是一个 PhoneWindow 对象

return mWindow;

}

所以最终调用了PhoneWindow中的setContentView方法。

PhoneWindow#setContentView方法

PhoneWindow中也有三个对应的重载setContentView方法,下面以参数为布局文件

id(layoutResID)的方法进行说明,其中大概做了以下几步工作:

1️⃣ 初始化:首先判断 mContentParent 是否为null,如果为 null,则调用 installDecor()方法初始化mContentParent,此方法后续会说明;

mContentParent 用来装xml布局文件解析出来的view树,是一个FrameLayout,后文还会继续说明其作用。

2️⃣ 填充布局:默认情况下会将设置的布局文件解析为View树,并添加到 mContentParent 中;

3️⃣ 通知Activity布局改变:通过相关回调通知Activity布局已发改变。

public void setContentView(int layoutResID) {

// 👉 首先判断 mContentParent 是否为 null,如果是第一次调用,则调用 installDecor() 方法

if (mContentParent == null) {

👇

installDecor();

} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {

// 否则判断是否设置 FEATURE_CONTENT_TRANSITIONS Window属性(默认false),

// 如果没有就移除该 mContentParent 内所有的所有子View;

mContentParent.removeAllViews();

}

// 👉 填充布局

if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {

final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,

getContext()););

transitionTo(newScene);

} else {

// 将xml资源文件通过 LayoutInflater 转换为 View 树

// 并且添加至 mContentParent 视图中

👇

mLayoutInflater.inflate(layoutResID, mContentParent);

}

mContentParent.requestApplyInsets();

// 👉 通知 Activity 布局改变

// 获取 Callback,在 Activity attach 方法中通过 setCallback 设置

final Callback cb = getCallback();

if (cb != null && !isDestroyed()) {

// onContentChanged 是个空方法,当 Activity 的布局改动时,

// 即 setContentView() 或者 addContentView() 方法执行完毕时就会调用该方法

👇

cb.onContentChanged();

}

mContentParentExplicitlySet = true;

}

PhoneWindow的setContentView方法将我们想要显示的View添加到mContentParent中,而mContentParent又是由installDecor方法得来,下面我们关注此方法。

DecorView:窗口顶层视图

再介绍installDecor()方法之前,我们需要介绍一个重要的概念:DecorView。

installDecor()方法中重点完成了两个对象的初始化,一个是mDecor对象,另一个就是之前说的mContentParent对象,这两个对象的定义如下:

// 👉 是一个 DecorView 对象,而 DecorView 是窗口顶层视图

DecorView mDecor;

// 👉 是一个 ViewGroup ,本质上是一个 FrameLayout

ViewGroup mContentParent;

这里让我们先回顾下已经提到的概念,Activity(调用setContentView设置布局),PhoneWindow(setContentView中完成 mContentParent 初始化,填充布局等),这两个与视图有关的概念并未真正直接承载视图,而承载视图的便是 DecorView 对象,即此处的 mDecor,DecorView 继承自 FrameLayout,如下所示:

public class DecorView extends FrameLayout

DecorView既然是一个FrameLayout,那么肯定也有其对应的布局文件,只不过是系统默认设置的布局文件,由于在不同的Activity主题情况下,系统默认的 DecorView 对应的布局不一样,我们这里以其中一种举例说明:

// 👉 screen_simple.xml 为例

android:layout_width="match_parent"

android:layout_height="match_parent"

android:fitsSystemWindows="true"

android:orientation="vertical">

android:inflatedId="@+id/action_mode_bar"

android:layout="@layout/action_mode_bar"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:theme="?attr/actionBarTheme" />

👇 对应 mContentParent

android:id="@android:id/content"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:foregroundInsidePadding="false"

android:foregroundGravity="fill_horizontal|top"

android:foreground="?android:attr/windowContentOverlay" />

那么mDecor 和 mContentParent 之间是什么关系呢? DecorView 分为两部分,一部分是 ActionBar(有的Activity Theme情况下没有ActionBar),另一部分就是 mContentParent(即上图中id为content的FrameLayout)。

96bb79c88c4d

DecorView.jpg

那么Activity、PhoneWindow、DecorView三者之间有什么关系呢?见下图所示:

96bb79c88c4d

Activity PhoneWindow DecorView.png

PhoneWindow#installDecor方法

介绍完DecorView后,我们再回头介绍installDecor方法的具体工作,其中主要做了以下几步工作:

1️⃣ 初始化mDecor:调用generateDecor方法来创建mDecor对象;

2️⃣ 初始化mContentParent:调用generateLayout方法创建mContentParent对象;

private void installDecor() {

...

// 👉 初始化 mDecor

if (mDecor == null) {

// 如果 mDecor 为空,则生成一个 DecorView 对象

mDecor = generateDecor(-1);

...

} else {

// 将 PhoneWindow 设置给 mDecor

mDecor.setWindow(this);

}

if (mContentParent == null) {

// 👉 初始化 mContentParent

mContentParent = generateLayout(mDecor);

...

}

}

// 👉 generateDecor:初始化 DecorView

protected DecorView generateDecor(int featureId) {

...

return new DecorView(context, featureId, this, getAttributes());

}

// 👉 generateLayout:根据窗口的风格,为 DecorView 选择对应的布局文件

protected ViewGroup generateLayout(DecorView decor) {

// 获取窗口属性

TypedArray a = getWindowStyle();

...

int layoutResource;

int features = getLocalFeatures();

// 👉 根据设定好的 features 值选择不同的窗口布局文件,得到 layoutResource 值,前文中曾以 screen_simple 举例

// 注意:此处还有很多分支判断代码省略了

if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {

layoutResource = R.layout.screen_swipe_dismiss;

}

...

// 👉 把选中的窗口布局文件解析成 View 树,并添加到 DecorView 中

mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

// 👉 指定 contentParent 值,对应的是布局文件中 id 为 content 的 View

ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

if (contentParent == null) {

throw new RuntimeException("Window couldn't find content container view");

}

...

// 👉 返回 contentParent,赋值给 mContentParent,所以 mContentParent 对应的是 R.id.content

return contentParent;

}

介绍完installDecor方法,我们再来看看PhoneWindow.setContentView方法中的第2️⃣步工作:填充布局,即默认情况下会将我们设置的布局文件解析为 View 树,并添加到 mContentParent 中,现在我们应该理解的更加清楚了。

3. ActivityThread#handleResumeActivity()方法:执行onResume生命周期

执行完onCreate、onStart生命周期后,来到了handleResumeActivity()方法中执行onResume生命周期,有一点需要注意的是,到目前为止,我们也仅是生成了一个Activity,一个PhoneWindow,一个DecorView,而并未真正的将我们需要显示的内容和Android系统进行交互,以进行View绘制,而onResume是我们启动Activity过程中的生命周期的最后一步,那我们有理由猜想有关绘制的执行应该在此生命周期中执行。此方法主要工作包括以下几步:

1️⃣ 执onResume生命周期:调用 performResumeActivity 来间接执行 Activity 的 onResume 生命周期;

2️⃣ 获取Activity的Window和DecorView:获取这两个变量赋值给相关变量,同时暂时使 DecorView 不可见;

3️⃣ 获取 WindowManager:在Activity.attach()方法中,我们为Activity设置了WindowManager;

4️⃣ WindowManager添加DecorView:调用WindowManager.addView方法为Window添加DecorView;

5️⃣ 使得 DecorView 可见:调用Activity.makeVisible方法使得DecorView重新可见。

final void handleResumeActivity(IBinder token,

boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {

...

// 👉 在其内部间接执行 Activity 的 onResume 方法,此时界面还不可见

ActivityClientRecord r = performResumeActivity(token, clearHide, reason);

...

final Activity a = r.activity;

...

// 👉 获取 Window,DecorView 等对象

r.window = r.activity.getWindow();

View decor = r.window.getDecorView();

// 👉 使 DecorView 不可见

decor.setVisibility(View.INVISIBLE);

// 👉 获取 WindowManager

ViewManager wm = a.getWindowManager();

WindowManager.LayoutParams l = r.window.getAttributes();

a.mDecor = decor;

if (a.mVisibleFromClient && !a.mWindowAdded) {

...

// 👉 WindowManager 添加 DecorView,此时依然不可见

wm.addView(decor, l);

...

}

...

if (r.activity.mVisibleFromClient) {

// 👉 使得 DecorView 可见

r.activity.makeVisible();

}

...

}

// 👉 Activity.makeVisible方法:使得 DecorView 可见

void makeVisible() {

...

mDecor.setVisibility(View.VISIBLE);

}

可见到目前为止,还是没有相关直接绘制View的操作,但对DecorView的操作有好几处,重点是第4️⃣步,其中调用WindowManager的addView方法添加DecorView。我们之前解释过View必须依附于Window才能显示,而Android 中对 Window 的管理都是通过 WindowManager 来完成的,那相关的绘制操作应该就是在此方法中了。

在理解addView方法之前,我们先来看看WindowManager的初始化过程,即Activity.attach方法中调用的setWindowManager方法:

// 👉 Window.setWindowManager 方法

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,

boolean hardwareAccelerated) {

...

mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);

}

// 👉 WindowManagerImpl.createLocalWindowManager 方法

public WindowManagerImpl createLocalWindowManager(Window parentWindow) {

return new WindowManagerImpl(mContext, parentWindow);

}

可见此处WindowManager它的真正实现是WindowManagerImpl,而WindowManagerImpl也并没有真正实现ViewManager接口的三大操作(addView,updateViewLayout,removeView),而是交给了WindowManagerGlobal。

// 👉 WindowManagerImpl.addView 方法

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {

applyDefaultToken(params);

👇 WindowManagerGlobal对象

mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);

}

mGlobal 对象的初始化如下,就是 WindowManagerGlobal 的单例模式。

private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

WindowManagerGlobal#addView方法

下面再来看看 WindowManagerGlobal.addView 方法,主要做了两步工作:

1️⃣ 初始化ViewRootImpl:ViewRootImpl 是视图层次的最顶层,连接WindowManagerService和DecorView的纽带, 同时 ViewRootImpl 的内容大部分都是 WindowManagerGlobal 类的内部实现细节;

2️⃣ 调用 ViewRootImpl.setView 方法:调用此方法来完成真正的添加 View 操作。

public void addView(View view, ViewGroup.LayoutParams params,

Display display, Window parentWindow) {

...

// 👉 ViewRootImpl 对象

ViewRootImpl root;

synchronized (mLock) {

...

// 👉 初始化 ViewRootImpl

root = new ViewRootImpl(view.getContext(), display);

...

// 👉 调用 ViewRootImpl.setView 方法

root.setView(view, wparams, panelParentView);

}

}

ViewRootImpl#setView方法

真正完成添加 View 的操作是此方法:

// 👉 ViewRootImpl.setView 方法

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {

...

👇

requestLayout();

...

👇

res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,

getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,

mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,

mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,

mTempInsets);

...

👇 将 DecorView 的 parent 设置为 ViewRootImpl

view.assignParent(this);

...

}

此方法中做的工作主要包括以下两个方面:

1️⃣ requestLayout()方法:调用View的测量,布局,绘制三个流程;

2️⃣ mWindowSession.addToDisplay()方法:mWindowSession 是一个aidl,ViewRootImpl 利用它来和 WindowManagerService 进行跨进程交互,这里先不过多介绍有关 WindowManagerService 内容,只简单介绍下其作用,如下。

WindowManagerService(WMS)的作用有很多:

窗口的管理者:负责窗口的启动、添加和删除,另外窗口的大小和层级也是由WMS进行管理;

事件的管理和派发工作:通过对窗口的触摸从而产生触摸事件,InputManagerService(IMS)会对触摸事件进行处理,它会寻找一个最合适的窗口来处理触摸反馈信息,WMS是窗口的管理者,因此,WMS“理所应当”的成为了事件的中转站。

Surface管理:窗口并不具备有绘制的功能,因此每个窗口都需要有一块Surface来供自己绘制。为每个窗口分配Surface是由WMS来完成的。

我们重点看看 requestLayout() 方法,源码如下:

public void requestLayout() {

if (!mHandlingLayoutInLayoutRequest) {

// 👉 检查是不是主线程

checkThread();

mLayoutRequested = true;

// 👉 view绘制三大流程入口

scheduleTraversals();

}

}

熟悉View绘制流程的同学应该知道,当View的大小、形状发生了变化的时候,可以调用此方法来进行重新绘制,此方法会从View树重新进行一次测量、布局、绘制这三个流程。此方法主要做了两步工作:

1️⃣ 检查当前线程:如果调用此方法的现场不是主线程,那么就在checkThread()方法中抛出异常,这一点与我们"通常意义"上所说的:只能在主线程更新UI相吻合,具体checkThread()方法稍后写明;

2️⃣ 调用 scheduleTraversals 方法:此方法后续完成了View的三大流程(测量(measure),布局(layout),绘制(draw)),具体分析见后续 。

再看下 checkThread() 方法:

void checkThread() {

👇

if (mThread != Thread.currentThread()) {

throw new CalledFromWrongThreadException(

"Only the original thread that created a view hierarchy can touch its views.");

}

}

mThread 的初始化是在 ViewRootImpl 的构造函数中完成的:

// ViewRootImpl 构造函数

public ViewRootImpl(Context context, Display display) {

...

👇

mThread = Thread.currentThread();

...

}

回忆下前文, ViewRootImpl 的初始化是在 WindowManagerGlobal.addView 方法中完成的,因此mThread 肯定对应的是主线程,因为 ActivityThread.handleLaunchActivity 方法就是在主线程中执行的,而其中并未切换过线程。因此如果我们在子线程中更新UI,那么最终会走到 requestLayout 方法进行重绘制,但此时会发现 mThread(主线程) 和 Thread.currentThread()(子线程)不是同一个线程,那么便会抛出异常。

再来说说 ViewRootImpl ,其作用非常重大。所有View的绘制以及事件分发等交互都是通过它来执行或传递的。View的三大流程(测量(measure),布局(layout),绘制(draw))均通过ViewRootImpl来完成。Android的所有触屏事件、按键事件、界面刷新等事件都是通过ViewRootImpl进行分发的。

从源码实现上来看,ViewRooImpl 既非View的子类,也非View的父类,但是,它实现了ViewParent接口,这让它可以作为View的名义上的父视图。

在 ViewRootImpl.setView 方法中调用了 View.assignParent 方法:

void assignParent(ViewParent parent) {

if (mParent == null) {

mParent = parent;

}

...

}

View是以View树组织的,每个View都有其Parent,DecorView作为最顶层的View,其Parent被设置为ViewRootImpl对象。这样它可以作为View的名义上的父视图,实质上完成了View的更新,绘制等工作。

96bb79c88c4d

View Tree.png

View类中也有requestLayout方法:用于 View 的位置,大小、形状发生了变化的时候进行调用。当一个子 View 调用此方法时,便会令 View 树重新进行一次测量、布局、绘制这三个流程,具体源码如下:

// 👉 View#requestLayout:

public void requestLayout() {

...

if (mParent != null && !mParent.isLayoutRequested()) {

// 👉 调用父容器的 `requestLayout` 方法

mParent.requestLayout();

}

...

}

此方法中最重要的是调用mParent.requestLayout方法,是向父容器请求布局,即调用父容器的 requestLayout 方法,此方法沿着View树向上传递,最终来到了 DecorView#requestLayout中,而DecorView是顶层View,其mParent便是ViewRootImpl,所以子View的requestLayout方法,经过层层传递,最终会被ViewRootImpl 接收并处理。

那具体的 scheduleTraversals 方法中主要完成了 View 的三大流程,本文暂且不进行分析。

总结

通过以上可以知道,Activity就像个控制器,不负责视图部分。Window像个承载器,装着内部视图。DecorView是个顶层视图,是所有View的最外层布局。ViewRootImpl像个连接器,负责沟通,通过硬件的感知(分发事件,绘制View内容)来通知视图,进行用户之间的交互。

其实本文还存在两个问题没有说明:

1️⃣ WindowManagerService具体原理:ViewRootImpl 和 WindowManagerService 进行跨进程交互的背后原理是什么,怎么做到View的真正显示?

2️⃣ View绘制的三大流程:View 测量,布局,绘制的详细流程是什么?

这些分析后续陆续进行说明。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值