View.post在Android 7.0 api24(以上)已不再100%执行
来源:互联网 发布:qq伪装手机型号软件 编辑:程序博客网 时间:2024/05/21 15:33
在Android 7.0 api24,Android 8.0 api25的手机上如果通过new创建的View,如果没有将它通过addView()加入到ViewGroup布局中,那通过View.post()发送出去的任务将不再执行,也就无法通过Viwe.post更新UI。
实例
private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { mTv1.setText("这是Handler发送的Message"); } }; private void btn2Click() { final View view = new View(this); Log.d("liuyz:", "版本号:" + Build.VERSION.SDK_INT); view.post(new Runnable() { @Override public void run() { Log.d("liuyz:", "创建View后直接post"); } }); //Handler发送一个延迟2秒的任务 mHandler.postDelayed(new Runnable() { @Override public void run() { Log.d("liuyz:", "ViewGroup添加了addView()"); mRoot.addView(view); } }, 2000); }
很简单,创建一个View,一个直接调用View.post(),一个通过Handler.post()延迟2秒执行ViewGroup.addView()方法。
当在api23上不注释掉Handler.post()方法中mRoot.addView(view)时,打印结果:
com.cn.liuyz.customviewdemo D/liuyz:: 版本号:23com.cn.liuyz.customviewdemo D/liuyz:: 创建View后直接postcom.cn.liuyz.customviewdemo D/liuyz:: ViewGroup添加了addView()
当注释掉Handler.post()方法中mRoot.addView(view)时,打印结果:
com.cn.liuyz.customviewdemo D/liuyz:: 版本号:23com.cn.liuyz.customviewdemo D/liuyz:: 创建View后直接post
当在api24上不注释掉Handler.post()方法中mRoot.addView(view)时,打印结果:
com.cn.liuyz.customviewdemo D/liuyz:: 版本号:24com.cn.liuyz.customviewdemo D/liuyz:: ViewGroup添加了addView()com.cn.liuyz.customviewdemo D/liuyz:: 创建View后直接post
当注释掉Handler.post()方法中mRoot.addView(view)时,打印结果:
com.cn.liuyz.customviewdemo D/liuyz:: 版本号:24com.cn.liuyz.customviewdemo D/liuyz:: ViewGroup添加了addView()
由此可得知在Android 7.0 api24的手机上,自定义View如果不通过addView()加入到ViewGroup中,那view.post()中的任务将不再执行
Android 7.0源码分析
View.post源码
public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.post(action); } //假设它待会会成功执行 getRunQueue().post(action); return true; }
当AttachInfo不为空时,调用Handler.post()执行任务,这个没啥说的。
当AttachInfo为空时,官方给的注释 假设它待会会成功执行 也就是说可能不执行。
那下面就看下怎么不执行
getRunQueue方法
private HandlerActionQueue getRunQueue() { if (mRunQueue == null) { mRunQueue = new HandlerActionQueue(); } return mRunQueue; }
返回一个HandlerActionQueue对象
再看post()方法
public void post(Runnable action) { postDelayed(action, 0); }public void postDelayed(Runnable action, long delayMillis) { //创建一个HandlerAction对象 final HandlerAction handlerAction = new HandlerAction(action, delayMillis); synchronized (this) { if (mActions == null) { //创建一个大小为4的数组 mActions = new HandlerAction[4]; } //给HandlerAction数组赋值即添加任务 mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction); mCount++; } }
post方法就是把任务封装到HandlerAction对象中,再添加到HandlerAction数组中,当需要执行的时候,通过循环执行任务。
那看下最终处理任务的方法
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; } }
当需要处理任务时,调用的是executeActions方法,通过for循环执行封装到HandlerAction对象中的任务,最后任务的执行是通过handler.postDelayed()执行,也是Handler的那套机制。
那看起来也挺靠谱啊,为什么就不是100%执行呢?请往下看
executeActions方法是在View的dispatchAttachedToWindow()方法中调用的
void dispatchAttachedToWindow(AttachInfo info, int visibility) { mAttachInfo = info; ....省略.... if (mRunQueue != null) { mRunQueue.executeActions(info.mHandler); mRunQueue = null; } ....省略.... }
dispatchAttachedToWindow方法的调用,是依赖于ViewGroup的addView(View child)方法,看源码
public void addView(View child) { addView(child, -1); }public void addView(View child, int index) { ....省略.... addView(child, index, params); }public void addView(View child, int index, LayoutParams params) { ....省略.... addViewInner(child, index, params, false); }private void addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout) { ....省略.... AttachInfo ai = mAttachInfo; if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) { boolean lastKeepOn = ai.mKeepScreenOn; ai.mKeepScreenOn = false; //在此调用了自定义View的方法 child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK)); if (ai.mKeepScreenOn) { needGlobalAttributesUpdate(true); } ai.mKeepScreenOn = lastKeepOn; } ....省略.... }
看到这里应该能稍微看明白点了吧,其实只要记住,在Android 7.0以后不要使用View.post了,使用Handler.post或其他方式代替就行
总结
如果使用new或者LayoutInflater创建一个View,而没有通过addView()将它添加到ViewGroup布局中,那View.post()发出去的Runnable任务将不会被执行到,也就起不到更新UI的作用了。
- View.post在Android 7.0 api24(以上)已不再100%执行
- [Android]View.post(),android7.0(sdk24以上)不执行的问题(部分Click点击事件无效的原因)
- Android API24以上Error inflating class android.support.design.widget.TabLayout
- Android 自定义View的post(Runnable)方法非100%执行的原因和处理方法解析
- Android 自定义View的post(Runnable)方法非100%执行引起的问题定位
- Android View.post(Runable)某些情况不执行的原因
- Android从源码分析handler.post(runnable),view.post(runnable),runOnUiThread(runnable)执行时机
- Android View.post(Runnable )
- Android View.post(Runnable )
- Android View.post(Runnable )
- Android View.post(Runnable )
- Android View.post(Runnable )
- Android View.post(Runnable )
- Android View.post(Runnable )
- Android View.post(Runnable )
- Android View.post()
- Android View.post(Runnable )
- android中的view.post()
- 低权用户调用高权用户创建的存储过程出错:noAccessToProcedureBodies
- UVALive
- Android-Gson使用详解
- <C语言经典100例>c37 百元买百鸡
- 随笔 | macports 配置
- View.post在Android 7.0 api24(以上)已不再100%执行
- Android异步更新UI的四种方法及源码简析
- UOJ #164. 【清华集训2015】V 线段树
- 覆盖索引---MyISAM 与 InnoDB
- Mysql数据库用户密码暴力破解
- SpringBoot之SSL配置(SpringBoot实战)
- js基础笔记01
- 主机不能访问虚拟机CentOS中的站点
- 害死人不偿命的(3n+1)猜想 (15)方法2