为什么view.post()能保证获取到view的宽高?

源码分析

view.post源码如下:

AttachInfo是View.java的静态内部类。

两种情况:

  • attachInfo为null的情况下;
  • attachInfo不为null的情况下。

AttachInfo为null时,分析如下:

getRunQueue()

HanderActionQueue.java

可见,最终会执行到 HanderActionQueue.java 类中的如下方法:

mActions是一个原始长度为4的HandlerAction数组,getRunQueue().post(action); 的作用是:将 action 消息 封装到 HandlerAction 类中,然后再存储到 mActions数组中。

AttachInfo不为null时,分析如下:

AttachInfo不为null时,执行attachInfo.mHandler.post(action); 这里,attachInfo.mHandler是在AttachInfo的构造方法中赋值的。那么AttachInfo又是在哪赋值的呢?

可以看到,在上述 dispatchAttachedToWindow(AttachInfo info, int visibility)方法中,mAttachInfo被赋值了,而且,方法里面还执行了如下逻辑:

  • 执行 HandlerActionQueue类中的executeActions(Handler handler)方法,遍历postDelayed消息。

mRunQueue即是通过 getRunQueue()方法赋值的,执行mRunQueue.executeActions(info.mHandler); 进入如下:

方法块中,遍历调用了 handler.postDelayed(handlerAction.action, handlerAction.delay);方法,往MessageQueue中发送消息。

  • onAttachedToWindow();

View.java类中的dispatchAttachedToWindow方法又是在哪调用的呢?

host.dispatchAttachedToWindow(mAttachInfo, 0);方法中,host为DecorView,mAttachInfo是在 ViewRootImpl的构造方法中赋值:

performTraversals()方法中,host被赋值为mView,其中,mViewDecorView

然后执行 host.dispatchAttachedToWindow(mAttachInfo, 0); mFirst为全局变量,在执行完performTraversals()方法后,被赋值为 false

然后再执行getRunQueue().executeActions(mAttachInfo.mHandler); 此时,将会通过mHandlerpostDelayed方法将消息传递到MessageQueue中。

​ 其次,还会根据条件执行以下方法:

需要注意的地方:

ViewRootImpl中,mView是在setView(View view, WindowManager.LayoutParams attrs, View panelParentView)方法中赋值的。

view.assignParent(this);最终在Viewview.assignParent(this);方法中执行:

ViewRootImpl的 setView(View view, WindowManager.LayoutParams attrs, View panelParentView)方法是在 WindowManagerGlobal类的addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) 方法中被调用。

ViewRootImplWindowManagerGlobal类的addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) 方法中 初始化。

WindowManagerGlobal类的addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) 方法是在 WindowManagerImpl类的addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) 方法被调用

WindowManagerImpl类 是 WindowManager接口的实现类,WindowManageraddView(@NonNull View view, @NonNull ViewGroup.LayoutParams params)方法是在 ActivityThread类的 handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) 方法中被调用

这意味着:ViewRootImpl中,mView的赋值是从:

  1. ActivityThreadhandleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) 方法中的 wm.addView(decor, l); 其中,decorDecorView
  2. WindowManagerImpl类的addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) 方法;
  3. WindowManagerGlobal类的addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) 方法;
  4. ViewRootImplsetView(View view, WindowManager.LayoutParams attrs, View panelParentView)方法。

ViewRootImpldoTraversal()方法中,调用了 performTraversals(); 方法。

TraversalRunnablerun 方法中,会执行 doTraversal(); 方法。

mTraversalRunnable 只在两个方法中被调用:

mChoreographer 是在 ViewRootImpl 的构造方法中初始化的,其中,赋值代码如下:

由于 ViewRootImpl 是在 主线程中初始化(从ActivityThreadhandleResumeActivity方法 -> WindowManagerImplwm.addView(decor, l); -> WindowManagerGlobaladdView 中初始化 ViewRootImpl),所以,这里的 Looper looper = Looper.myLooper(); 当前指的是 主线程的 LooperChoreographer 类中的 private final FrameHandler mHandler; 绑定的Looper为主线程的Looper。

mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); 内部代码如下:

由上面分析可知,mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); 是将消息传递到 MessageQueue 中去执行。

ViewRootImpl方法调用关系:scheduleTraversals()方法有众多的调用者,改变很多参数都会触发这个方法,然后它会将一次traversal排期进自己的schedule,具体绘制的时机由Choreographer决定。这是绘制的总入口。

ViewRootImpl方法调用关系

注意:在我们分析 ViewRootImplsetView(View view, WindowManager.LayoutParams attrs, View panelParentView) 方法时,里面有一处代码:

requestLayout(); 方法中:

这里面会执行:scheduleTraversals(); 方法。

由上述分析可知:

  1. ViewRootImpl.java 类的 setView(View view, WindowManager.LayoutParams attrs, View panelParentView) 方法中,除了给全局变量 mView赋值之外,还有就是调用 requestLayout(); 方法;
  2. requestLayout(); 方法内,会执行 scheduleTraversals() 方法,在该方法内,会通过 mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null) 方法,将 mTraversalRunnable 消息发送到 MessageQueue中;
  3. mTraversalRunnable 是一个实现了 Runnable 的类,它的 run 方法会调用 doTraversal(); 方法,而该方法会调用 performTraversals(); 方法;

ViewRootImpl.java 类的performTraversals(); 方法中,会调用 host.dispatchAttachedToWindow(mAttachInfo, 0); 方法,由于这里的 hostDecorView, 它继承了 FrameLayout 控件,往下查看可知,host.dispatchAttachedToWindow(mAttachInfo, 0); 方法最后是会调用 ViewGroup 控件里面的 dispatchAttachedToWindow(AttachInfo info, int visibility) 方法。

其中,ViewGroup.classdispatchAttachedToWindow(AttachInfo info, int visibility) 方法内部的 super.dispatchAttachedToWindow(info, visibility); 会进入到 View.class 类的 dispatchAttachedToWindow(AttachInfo info, int visibility) 方法中去执行。

注意:在ViewRootImpl.java 类的performTraversals(); 方法中,会调用 host.dispatchAttachedToWindow(mAttachInfo, 0); 方法,由上面分析可知,会调用到 View.class 类中的 dispatchAttachedToWindow(AttachInfo info, int visibility) 方法,在该方法内部,会执行:mRunQueue.executeActions(info.mHandler); 方法,从而遍历 Message 执行。performTraversals(); 方法中除了调用 host.dispatchAttachedToWindow(mAttachInfo, 0); 之外,其次还会执行 performMeasureperformLayoutperformDraw。从代码顺序上初步看:是先执行了 mRunQueue.executeActions(info.mHandler); 方法,再执行 performMeasure 方法去获取 View 的宽高,这样很容易给人一种错觉,View.post(XX) 方法传递到 MessageQueue 中的 Runnable 消息在 performMeasure 方法 之前执行,那样获取到的 View 的宽高还是为0阿?

这得从 Android 的消息机制来分析,当在 ActivityonCreate(XX) 方法中执行 View.post(Runnable action) 方法之后,会将该 Runnable 消息传递到主线程的 MessageQueue 中等待执行。根据 Android 消息机制可知,需要等待主线程的Handler执行完当前的任务(即performTraversals();),才会去执行我们 View.post 的那个Runnable,所以,在 ActivityonCreate(XX) 方法中执行 View.post(Runnable action) 方法能获取到 控件的宽高

参考的文章出处如下:

1、ViewRootImpl方法调用关系
2、应该还有其他的文章,暂时找不到文章出处了。。。

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
下一篇