View.post分析
来源:互联网 发布:ed2k for mac下载工具 编辑:程序博客网 时间:2024/05/21 08:56
View.post分析
我们在使用View的时候可以直接使用View对象进行post(runnable),难道View里面有主线程Handler对象?是每个View都有一个Handler,还是公用的?为何View 没有 AttachedToWindow的时候View.post无效呢,后面还会执行么?
本文所有的源码都是基于API19,也就是4.4KitKat版本,不同版本源码不同,思路雷同
View.post背后究竟是谁?
所有的View的post方法都是直接继承于View类的post(Runnable action)方法:
public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { // 1. AttachInfo.mHandler 这里是Handler return attachInfo.mHandler.post(action); } // Assume that post will succeed later // 2.ViewRootImpl.getRunQueue() 返回的RunQueue ViewRootImpl.getRunQueue().post(action); return true; }
I. AttachInfo.mHandler支线
- AttachInfo中的mHandler 是创建的时候从外部传进的
AttachInfo(IWindowSession session, IWindow window, Display display, ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer) { mSession = session; mWindow = window; mWindowToken = window.asBinder(); mDisplay = display; mViewRootImpl = viewRootImpl; // handler是从外部传进来的,那么是公用的,还是每个View对应一个呢? mHandler = handler; mRootCallbacks = effectPlayer; }
- ViewRootImpl的构造方法,一起创建了AttachInfo
public ViewRootImpl(Context context, Display display) { mContext = context; mWindowSession = WindowManagerGlobal.getWindowSession(); // 记住这一行,mThread为当前线程 mThread = Thread.currentThread(); // ..... // 创建了AttachInfo,并将自己的mHandler (ViewRootHandler extends Handler)赋给了AttachInfo的mHandler mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this); // ..... }
Question:
1.已知Handler的创建是与线程相关的,那么此Handler一定是在UI线程上创建么?
2.AttachInfo是所有View共用的么,好像不是,如何证明?
问题一解答:此Handler一定是在UI线程上创建的:
我们知道Android控件需要在UI线程显示更新,如果不然会报以下错误:
public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks{ void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } } // .....}
上面ViewRootImpl 的初始化中有mThread 的创建,就是当前线程对象,根据这里的异常判断,ViewRootImpl 的初始化一定是在UI线程上的,那么成员变量Handler也一定是在主线程上创建的。
问题二解答:AttachInfo每个View对应一个不同对象,且与View的Attach状态相关,创建于addView时:
public final class WindowManagerGlobal { public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; if (parentWindow != null) { parentWindow.adjustLayoutParamsForSubWindow(wparams); } ViewRootImpl root; View panelParentView = null; // 每次addView的时候都会创建ViewRootImpl对象 root = new ViewRootImpl(view.getContext(), display); // ..... view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams); // do this last because it fires off messages to start doing things try { root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { // ..... } } // ......}
很显然,每个View都是通过addView添加到父View中去的。所以每个View都会有其对应的ViewRootImpl,有其对应的mHandler。
II.ViewRootImpl.getRunQueue()支线
先看看getRunQueue:
static RunQueue getRunQueue() { RunQueue rq = sRunQueues.get(); if (rq != null) { return rq; } rq = new RunQueue(); sRunQueues.set(rq); return rq; }
再看看RunQueue :
// 注意这里是静态的static final class RunQueue { private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>(); void postDelayed(Runnable action, long delayMillis) { // runnable 存到 HandlerAction对象中 HandlerAction handlerAction = new HandlerAction(); handlerAction.action = action; handlerAction.delay = delayMillis; synchronized (mActions) { mActions.add(handlerAction); } } void executeActions(Handler handler) { synchronized (mActions) { final ArrayList<HandlerAction> actions = mActions; final int count = actions.size(); for (int i = 0; i < count; i++) { final HandlerAction handlerAction = actions.get(i); // 最终通过handler来执行储存的HandlerAction handler.postDelayed(handlerAction.action, handlerAction.delay); } actions.clear(); } } }
最后来看看谁执行了executeActions( 竟然是传说中的performTraversals() ):
private void performTraversals() { // cache mView since it is used so much below... final View host = mView; WindowManager.LayoutParams lp = mWindowAttributes; final View.AttachInfo attachInfo = mAttachInfo; // ...... // 这里,没错,就是这里 // Execute enqueued actions on every traversal in case a detached view enqueued an action getRunQueue().executeActions(attachInfo.mHandler); // ...... }
我们知道performTraversals是View绘制流程的总入口,当有新的View测量、位置变化、绘制的时候会触发此方法,这是会调用新View的mHandler将之前储存的runnable执行掉。
View.post不是任何时候都能用
之所以存在第二种逻辑,是因为某些情况下AttachInfo为null
第一种情况:View对象被创建,却没有被addView进父View:
handler.postDelayed(new Runnable() { @Override public void run() { LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); View inflateView = inflater.inflate(R.layout.layout_inflate_view, null); inflateView.post(new Runnable() { @Override public void run() { // 若布局不发生变化,此处不会执行 Toast.makeText(mainActivity.getApplicationContext(),"layout_inflate_view 没有Attach 竟然显示了",Toast.LENGTH_LONG).show(); } }); } },2000);
第二种情况:onDetachedFromWindow 之后post
// 跳转的同时remove掉自己,再延时post text_view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this,SecondActivity.class); startActivity(intent); removeView(); } }); private void removeView(){ text_view.setOnDetachedFromWindowListener(new MineTextView.OnDetachedFromWindowListener() { @Override public void onDetached() { // 会被调用 Toast.makeText(mainActivity.getApplicationContext(),"text_view 被 Detached 了",Toast.LENGTH_LONG).show(); } }); ((ViewGroup) text_view.getParent()).removeView(text_view); getWindow().getDecorView().postInvalidate(); handler.postDelayed(new Runnable() { @Override public void run() { text_view.post(new Runnable() { @Override public void run() { // 不会执行 Toast.makeText(mainActivity.getApplicationContext(),"text_view 被 Detached 了 竟然还能显示",Toast.LENGTH_LONG).show(); } }); } },2000); }
这里可以看到onDetachedFromWindow 之后的View中的AttachInfo被制空了,我们来看看:
void dispatchDetachedFromWindow() { AttachInfo info = mAttachInfo; if (info != null) { int vis = info.mWindowVisibility; if (vis != GONE) { onWindowVisibilityChanged(GONE); } } onDetachedFromWindow(); // ...... // 果然被制空了 mAttachInfo = null; if (mOverlay != null) { mOverlay.getOverlayView().dispatchDetachedFromWindow(); } }
代码验证View中的AttachInfo被制空了
- View.post分析
- Handler简单分析-runOnUiThread,view.post()
- view.post()
- Android从源码分析handler.post(runnable),view.post(runnable),runOnUiThread(runnable)执行时机
- View#post与Handler#post的区别,以及导致的内存泄漏分析
- View.post和View.postDelayed
- Android View.post(Runnable )
- Android View.post(Runnable )
- Android View.post(Runnable )
- Android View.post(Runnable )
- Android View.post(Runnable )
- Andorid学习 View.post
- Android View.post(Runnable )
- Android View.post(Runnable )
- Android View.post(Runnable )
- Android View.post()
- Android View.post(Runnable )
- android中的view.post()
- scott用户解锁
- 卫星导航开源代码库
- Linux x86_64平台下程序链接错误和-fPIC
- 内部类实现 LinkedStack
- 如何在GitHub找到需要的开源框架
- View.post分析
- POJ2159
- 用vs编写和调试linux C++程序
- android 自定义属性,分享一个自适应容器,多用于显示图片,保证图片不变形
- 正向代理与反向代理的区别
- 搭建第一个HelloWorld项目
- Java八种基本类型(byte、short、int、long、浮点数、char、boolean、基本类型转换)
- zabbix监控linux分区大小
- 容器的扩展-2