1.view.post
文中涉及Activity启动相关
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//view获取宽高
view.post{
val measuredWidth = view.measuredWidth
}
}
2. View.java post()
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
//当mAttachInfo!=null时,
//向主线程的mHandler里发生消息,
//这里view已经绘制完成,自然可以拿到view的宽高
return attachInfo.mHandler.post(action);
}
//在HandlerActionQueue里保存runnable
getRunQueue().post(action);
return true;
}
private HandlerActionQueue getRunQueue() {
if (mRunQueue == null) {
mRunQueue = new HandlerActionQueue();
}
return mRunQueue;
}
//消息临时存放的容器
public class HandlerActionQueue {
private HandlerAction[] mActions;
private int mCount;
public void post(Runnable action) {
postDelayed(action, 0);
}
public void postDelayed(Runnable action, long delayMillis) {
final HandlerAction handlerAction = new HandlerAction(action, delayMillis);
synchronized (this) {
if (mActions == null) {
mActions = new HandlerAction[4];
}
mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
mCount++;
}
}
//拿来存放的消息,向主线程Handler发送消息
public void executeActions(Handler handler) {
synchronized (this) {
final HandlerAction[] actions = mActions;
for (int i = 0, count = mCount; i < count; i++) {
final HandlerAction handlerAction = actions[i];
//向主线程发送消息
handler.postDelayed(handlerAction.action, handlerAction.delay);
}
mActions = null;
mCount = 0;
}
}
源码分析
AttachInfo 不为空时,
view已经绘制完成,自然可以拿到view的宽高。
AttachInfo 什么情况下不为空呢?
不为空时为什么会获取到狂高呢?
dispatchAttachedToWindow()
View.java
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
//mAttachInfo赋值
mAttachInfo = info;
...
//执行mRunQueue里的runnable
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);
mRunQueue = null;
}
ViewGroup.java
@Override
@UnsupportedAppUsage
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
super.dispatchAttachedToWindow(info, visibility);
...
for (int i = 0; i < transientCount; ++i) {
View view = mTransientViews.get(i);
//赋值给子view
view.dispatchAttachedToWindow(info,
combineVisibility(visibility, view.getVisibility()));
}
}
Activity视图结构
我们知道Activity的视图结构
PhoneWindow->DecorView(ViewRootImpl)->setContentView()
ViewRootImpl是在onResume时创建和DecorView关联,
ViewRootImpl.java
public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
boolean useSfChoreographer) {
//创建mAttachInfo,
...
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
context);
...
//创建Choreographer
mChoreographer = useSfChoreographer
? Choreographer.getSfInstance() : Choreographer.getInstance();
...
}
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
synchronized (this) {
if (mView == null) {
//decorView
mView = view;
//执行绘制流程
requestLayout();
//设置decorview 的parent
view.assignParent(this);
}
//执行绘制
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
scheduleTraversals
void scheduleTraversals() {
if (!mTraversalScheduled) {
//设置同步屏障,只能处理异步消息 msg.target==null
mHandler.getLooper().getQueue().postSyncBarrier();
//mainHandler 发送mTraversalRunnable
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
//执行doTraversal
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
//移除同步屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
//执行绘制流程
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
performTraversals
private void performTraversals() {
//decorView
final View host = mView;
//给所有的view设置mAttachInfo
host.dispatchAttachedToWindow(mAttachInfo, 0);
...
//测量布局绘制
performMeasure()
perforLayout()
perforDraw()
...
dispatchAttachedToWindow()的执行
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;
// Transfer all pending runnables.
//向handler发送view.post的消息
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);
mRunQueue = null;
}
}
3. 总结:
1.AttachInfo不为空时,view的测量已经完成,所有能获取到宽高。 2.getRunQueue().post(action);怎么获取的宽高
源码执行流程:
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
->dispatchAttachedToWindow()->mRunQueue.executeActions(info.mHandler);
->handler.postDelayed(handlerAction.action, handlerAction.delay);
->performMeasure()
明明是先发送的获取宽高的消息,在执行的测量,为何还能获取到宽高呢?
首先
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
向主线程的Handler发送《测量绘制的异步任务》,
然后才向主线程Handler发送的《获取宽高任务》,Hanlder消息是排队执行的,
所以《测量绘制消息》的任务执行完毕,才会执行《获取宽高消息》的任务。
自然可以获取到宽高。
在main-Handler当消息1在执行中,handler又添加了消息2,
只有消息1执行完毕,才会执行消息2,
所以始终是先测量完,在去获取的宽高。