千家信息网

Activity显示界面的方法教程

发表于:2025-11-06 作者:千家信息网编辑
千家信息网最后更新 2025年11月06日,本篇内容主要讲解"Activity显示界面的方法教程",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"Activity显示界面的方法教程"吧!动画展示为了方便
千家信息网最后更新 2025年11月06日Activity显示界面的方法教程

本篇内容主要讲解"Activity显示界面的方法教程",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"Activity显示界面的方法教程"吧!

动画展示

为了方便大家理解,先通过动画的形式给大家展示这几位的关系:

源码解析

从小爱诞生说起

Activity界面展示之前,它还是个我们看不到的Activity,我先给它起个爱称-小爱

小爱是怎么诞生的呢?熟悉Activity启动流程的都知道,小爱的创建发生在performLaunchActivity中:

//ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//创建ContextImpl
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
//创建Activity
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
}

try {
if (activity != null) {
//完成activity的一些重要数据的初始化
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken);

//调用activity的onCreate方法
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
}
}

return activity;
}

这个过程中,主要做了三件事:

  • Activity被实例化出来
  • 调用了 attach方法进行初始化
  • 调用 onCreate方法开始从布局文件加载布局,做View显示的准备工作。

给小爱找个和View交互的帮手(PhoneWindow)

大家也都知道,小爱在被创建后,事务繁忙,肯定不能亲力亲为得管理每个View,所以他就找了一个帮手,帮助她和View交互,管理View。

(Activity和View的解耦)

这个帮手是啥呢?就是窗口Window,也就是实现类PhoneWindow了。

这个过程发生在attach方法中:

//Activity.java
final void attach() {
//创建PhoneWindow
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setCallback(this);
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);

}

为了方便记忆,我们管这个PhoneWindow管家叫做 窗管家

加载布局文件(DecorView)

有了窗管家之后,就可以继续onCreate方法了,在onCreate方法中最重要的就是这个setContentView方法

通过setContentView可以加载布局文件里的View。

之前说了,View相关的管理工作就交给窗管家,所以就直接调用到PhoneWindowsetContentView方法:

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

然后就开始加载布局文件的工作了。

但是考虑到一点,Activity是有不同的主题的,不同主题就有不同的布局结构。所以得在加载我们自己设置的布局文件之前,设置一个最顶级的View,作为所有View的老大。

而这个顶层的View就是DecorView,为了方便,我管他叫做 最顶的小弟,简称小弟

看看小弟DecorView是怎么被创建的:

//PhoneWindow.java
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
}


if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
}


private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor(-1);
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
}
}


protected DecorView generateDecor(int featureId) {
return new DecorView(context, featureId, this, getAttributes());
}

就是这样,小弟DecorView就被创建出来了,然后就该小弟工作了。

上文说过,小弟DecorView被创建出来是要干啥的?

要根据不同的主题设置不同的布局结构,这个工作就发生在generateLayout方法中了,具体咱今天就不分析了。

看似小弟的工作也完成了?

等等,应用自己的布局还没加载呢嘛,重要的事情还没开始做呢。

再回到上面的setContentView方法中,在调用installDecor方法创建了小弟之后,还做了一件事:

 //加载xml布局文件
mLayoutInflater.inflate(layoutResID, mContentParent);



public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();

final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}

而这个inflate就是我们熟知的加载布局文件的方法。传入xml布局文件,解析并结合我们传入的父view--mContentParent,将其转化为一个完整的树结构,最后返回顶层的View。

到这里,setContentView的工作算是完成了,

简单的说,就是创建了小弟DecorView,并且结合这个顶层的view和我们传入的xml布局文件,生成了一个多层结构的View

显示出这个View(ViewRootImpl)

View有了,结构也定下来了。接下来就是怎么显示出这个View结构,让我们的手机展示出画面?

没错,就是绘制

关于View的绘制工作交给谁做比较好呢?回忆下现在的成员:

  • 小爱Activity:大老板,负责统筹即可。
  • 窗管家PhoneWindow:负责管理各个View。
  • 小弟DecorView:最顶层的View,负责展示主题布局。

好像没有人选可以负责View绘制了?绘制这么重要,那就要再招一个朋友来了。

ViewRootImpl闪亮✨登场,为了方便,我管他叫做 小薇

小薇是什么时候创建的呢?

接着看Activity的调用过程,在onCreate调用完后,就会调用onResume方法,这又要从handleResumeActivity方法说起了。

    @Override
public void handleResumeActivity() {
//onResume
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
//addView
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes()
wm.addView(decor, l);
}

该方法主要做了两件事:

  • 调用 onResume方法
  • 调用WM的 addView方法。

小薇好像还没出来?

继续看addView方法:

//WindowManagerGlobal.java
public void addView() {

synchronized (mLock) {

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

view.setLayoutParams(wparams);

mViews.add(view);
mRoots.add(root);
mParams.add(wparams);


try {
root.setView(view, wparams, panelParentView);
}
}
}



public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
mThread = Thread.currentThread();
}


终于,小薇ViewRootImpl也被创建出来了,而这个ViewRootImpl中,有两个变量值得关注一下:

  • mWindowSession。类型为IWindowSession,是一个Binder对象,用于进程间通信。其在服务器端的实现为Session,可以通过它来完成WMS相关的工作。
  • mThread。设置了线程变量为当前线程,也就是实例化ViewRootImpl时候的线程。一般进行不同线程更新UI的时候,就会判断当前线程和mThread是否相等,如果不同,则会抛出异常。

接下来,就是调用ViewRootImplsetView方法,这个方法自然就是小薇ViewRootImpl做事的方法了:

//ViewRootImpl.java
public void setView() {
synchronized (this) {
//绘制
requestLayout();

//调用WMS的addWindow方法
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);

//设置this(ViewRootImpl)为view(decorView)的parent
view.assignParent(this);
}
}

主要有三个功能:

  • 触发绘制(具体包括测量、布局、绘制)
//ViewRootImpl.java
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}

->scheduleTraversals()
->performMeasure() performLayout() performDraw()
->measure、layout、draw方法
  • 通过Binder调用WMS的addWindow方法

addToDisplay方法最终会WMS所在进程的addWindow方法,为窗口分配Surface,而这个Surface就是负责显示最终的界面,并最终会绘制到屏幕上。

  • 设置ViewRootImpl为decorView的parent

这样设置之后,子view请求绘制的时候(requestLayout),就能一直通过parent最终找到ViewRootImpl,然后由ViewRootImpl来负责所有View的绘制工作。整个调用过程是:

View.requestLayout -> DecorView.requestLayout -> ViewRootImpl.requestLayout

//View.java
public void requestLayout() {
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
}

小结

到此,Activity终于完成了他的启动生命周期,界面也显示出来了,小爱也变为了成型的Activity

其实不难发现,虽然这中间角色比较多,但是每个角色又不可或缺:

因为需要管理View,创建出了 PhoneWindow

因为需要根据主题显示不同的布局结构,创建出了根View DecorView

因为需要处理View的各种事件,包括绘制、事件分发,创建出了ViewRootImpl

大家各忙各的,并听命于Activity

习题

以前上课的时候,总喜欢学习完知识后做几个习题,今天也给大家带来几个问题,巩固下知识。

Activity、PhoneWindow、DecorView、ViewRootImpl 之间的关系?

  • PhoneWindow:是Activity和View交互的中间层,帮助Activity管理View。
  • DecorView:是所有View的最顶层View,也就是所有View的parent。
  • ViewRootImpl:用于处理View相关的事件,比如绘制,事件分发,也是DecorView的parent。

四者的创建时机?

  • Activity创建于performLaunchActivity方法中,在startActivity时候触发。
  • PhoneWindow,同样创建于performLaunchActivity方法中,再具体点就是Activity的attach方法。
  • DecorView,创建于setContentView->PhoneWindow.installDecor。
  • ViewRootImpl,创建于handleResumeActivity方法中,最后通过addView被创建。

View的第一次绘制发生在什么时候?

第一次绘制就是发生在handleResumeActivity方法中,通过addView方法,创建了ViewRootImpl,并调用了其setView方法。

最后调用到requestLayout方法开始了布局、测量、绘制的流程。

线程更新UI导致崩溃的原因?

在触发绘制方法requestLayout中,有个checkThread方法:


void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}

其中对mThread和当前线程进行了比较。而mThread是在ViewRootImpl实例化的时候赋值的。

所以崩溃的原因就是 view被绘制到界面时候的线程(也就是ViewRootImpl被创建时候的线程)和进行UI更新时候的线程不是同一个线程。

到此,相信大家对"Activity显示界面的方法教程"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

方法 布局 就是 时候 线程 小弟 工作 文件 不同 界面 结构 管理 主题 管家 顶层 小薇 重要 也就是 事件 过程 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 创建数据库考试 校园网络安全小组工作职责 原神切换服务器2.5版本 河北电脑软件开发费用是多少 计算机网络技术发展历史 网络技术群定义 长宁区游戏软件开发系统 语音存储及平台管理服务器 杭州宸柚网络技术有限公司 计算机网络技术要敲代码么 java基于构件的软件开发 泰拉瑞亚服务器在哪儿弄 app教育软件开发报告书 网速慢常掉线会导致网络安全吗 完美平台怎么选择比赛服务器 京东客户端什么软件开发的 网络安全立法特征 沈阳国产信创服务器订购 正常高速服务器有没有摄像头 法国外交档案数据库 洛克王国服务器加载不进去 注册 登陆 数据库操作 软件开发 结项 制度 幼儿网络安全故事完整版2分钟 福建省信息技术数据库 网络安全数据可视化工具研究 哈尔滨软件开发专业怎么样 快速汇总多表数据库 数据库技术应用困境 全国核酸检测机构数据库形成
0