目录前言1.ViewRootImpl哪来的?2 ViewRootImpl 一个View链渲染的中转站3 不能在子线程操作View?4 View 挂载5 View.post()的Run
这两个类就是ActivityThread和ViewRootImpl,之所以说碰不到是因为我们无法通过正常的方式引用这两个类或者其类的对象,调用方法或者直接拿他的属性。但他们其实又无处不在,应用开发中很多时候都和他们息息相关,阅读他们掌握其内部实现对我们理解Android运行机理有醍醐灌顶之疗效,码读百变其义自见,常读常新。本文就尝试从几个我们经常接触的方面先谈谈ViewRootImpl。
首先是ViewRootImpl,位于android.view
包下,从它所处的位置大概能猜到,跟View相关。其作用一句话总结,就是连接Window和View的纽带。
这个要从我们最熟悉的Activity开始,我们知道Activity的设置布局View是通过setContentView()
方法这个方法里面也大有文章,我们简单的梳理下。
getWindow().setContentView(layoutResID);
也就是调用了Window的setContentView
方法,Android里Window的唯一实现类就是PhoneWindow,PhoneWindow setContentView,初始化DecorView和把我们设置的View作为其子类。ActivityThread
没错是我们提及的另外一个主角,先关注他的handleResumeActivity()方法,里面关键的部门代码,public void handleResumeActivity(){
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
ViewManager wm = a.getWindowManager();
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
wm.addView(decor, l);
}
WindowManager
的实现类WindowManageImpl
的addView方法里调用了mGlobal.updateViewLayout(view, params);
public void addView(){
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
小结
View的渲染是自定而上层层向下发起的,大致经历测量布局和绘制,View链的管理者就是ViewRootImpl
。通过
scheduleTraversals()
方法发起渲染动作。交给Choreographer安排真正执行的时间关于Choreographer
不熟悉的可以参考我的其他文章。最终执行perfORMTraversals()
方法。
private void performTraversals(){
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
performLayout(lp, mWidth, mHeight);
performDraw();
}
ViewRoot的RequestLayout中有这样一段代码:
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
requestLayout()
方法,该方法有如上的一个check逻辑。这就是我们常说的不能在子线程中更新View。performTraversals()
里有这个代码private void performTraversals(){
host.dispatchAttachedToWindow(mAttachInfo, 0);//此处的host为ViewGroup
}
dispatchAttachedToWindo()
方法会把AttachInfo对象分配每一个View,最终实现我们所谓的挂载。void dispatchAttachedToWindow(AttachInfo info, int visibility) {
for (int i = 0; i < count; i++) {
final View child = children[i];
child.dispatchAttachedToWindow(info,
combineVisibility(visibility, child.getVisibility()));
}
通过addView添加进的View也是会收到父View的mAttachInfo这里不展开了。
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
getRunQueue().post(action);
return true;
}
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
..
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);//内部也是调用handler.post()
mRunQueue = null;
}
..
}
dispatchAttachedToWindow()
方法里执行。这个是是一个问题延伸,在Activity中直接获取宽高是获取不到的,我们通常会使用view.post一个Runnable来获取。原因就是Activity onCreate
时通过setContentView只是创建了View而未实现挂载,挂载是在onResume时,未挂载的View其实没有经历测量过程。。
而通过post的方式,通过上一小节知道,未挂载的View上post之后,任务会在挂载之后,通过handler重新post,此时已经ViewRootImpl已经执行了performTraversals()
完成了测量自然可以得到宽高。
ViewRootImpl
不单单是渲染的中转站,还是触摸事件的中转站。
硬件传感器接收到触摸事件经过层层传递分发到应用窗口的第一站就是ViewRootImpl。为什么这么说?因为我有证据~。这是ViewRoot里的代码
public void setView(){
..
mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
Looper.myLooper());
}
以上就是Android那两个你碰不到但是很重要的类之ViewRootImpl的详细内容,更多关于Android ViewRootImpl的资料请关注编程网其它相关文章!
--结束END--
本文标题: Android那两个你碰不到但是很重要的类之ViewRootImpl
本文链接: https://www.lsjlt.com/news/212749.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