view.post不执行的坑点

view.post没执行,runOnUiThread,Handler

目录

  • 坑点
  • 处理
  • 原因
  • 经历
  • 复盘
    • 6.0版本
    • 10.0版本
  • 总结

坑点

子线程执行view.post(Runnable) 部分 手机没有效果。

usernameEditText.post(new Runnable() {@Overridepublic void run() {usernameEditText.setText("text set by runnable!");}});

处理

  1. 使用handler.post
  2. 使用runOnUiThread

原因

低版本android基于ThreadLocal实现线程与数据关联,且线程的数据独立不共享。遇到多线程使用时,一个线程存储了数据,另一个线程取不到的数据的原因。
子线程调用view.post时候,会构造一个队列存储到对应的线程数据空间,并将runnable加到此队列。当view要显示时候,ui线程会从ui线程的数据空间取出队列,遍历执行队列中的runnable,但是由于ThreadLocal的缘故,ui线程取到的队列肯定不包含子线程存到队列的runnable,所以这个runnable是不被执行的。因为刚才是子线程存的runnable,子线程可以取到,而UI线程并没有存我们期望的runnable,所以取不到。ThreadLocal特点就是线程之间的数据相互隔离,各自使用各自的数据,多线程使用时保证数据的“安全”。
还没明白的话,这样讲一下:
A、B钱包都没钱了,A从银行取了1000 人民币,装入了自己的钱包,B去商店买1000的商品,此时B从自己钱包里面拿钱时,钱包是空的。所以B是买不了商品的。

7.0之前的系统存在这个问题,7.0之后已经被修复了。用的时候小心一点。

经历

给同事写了个程序,当时是一个子线程处理了数据之后调用view.post更新到界面上,自测是没问题的,结果同事那边告知不显示,当时也查看了源代码,同时也是反复验证没有问题,同事那里始终是有问题的,后来同事也没再提说。一段时间之后见了同事,问及此事,他说 只有他手机有问题,其他的手机没问题,所以就没再说这个事情了。让其掏出手机看了下,果真不显示,当即开始加日志调试,结果runnable代码块没有被执行,隐隐约约感觉到view.post不靠谱,直接在外层再加一个runOnUiThread之后,他的设备正常。 虽然问题是过去了,但一直没时间去弄清楚出现这个问题的原因,最近看到项目中有其他小伙伴也写了同样的代码,心里面有点慌。
同时也查了些资料,总结记录之。

复盘

View.post()方法在android7.0之前,在view没有attachToWindow的时候调用该方法可能失效,尤其异步线程,如在onCreate,onBindViewHolder时调用view.post方法,可能会不生效,在异步线程view.post方法不执行的情况居多。建议使用Handler post方法代替。
longlong2015 这里也对次问题进行说明

于是乎,下载了一份6.0版本的sdk源码,以及9.0的源码进行对比,对比情况和引用文章差不多,也进一步对引用文章进行验证。

6.0版本

  1. View的post函数
 public boolean post(Runnable action) {final AttachInfo attachInfo = mAttachInfo;if (attachInfo != null) {return attachInfo.mHandler.post(action);}// Assume that post will succeed laterViewRootImpl.getRunQueue().post(action);return true;}

如果attachInfo有值,则是用attachInfo中的handler去post这个runnable,如果attachInfo没有值,则是ViewRootImpl.getRunQueue() 去执行post这个runnable。而attachInfo则是分别dispatchAttachedToWindow (首行)赋值的:

void dispatchAttachedToWindow(AttachInfo info, int visibility) {//System.out.println("Attached! " + this);mAttachInfo = info;if (mOverlay != null) {mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility);}mWindowAttachCount++;// We will need to evaluate the drawable state at least once.mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY;if (mFloatingTreeObserver != null) {info.mTreeObserver.merge(mFloatingTreeObserver);mFloatingTreeObserver = null;}if ((mPrivateFlags&PFLAG_SCROLL_CONTAINER) != 0) {mAttachInfo.mScrollContainers.add(this);mPrivateFlags |= PFLAG_SCROLL_CONTAINER_ADDED;}performCollectViewAttributes(mAttachInfo, visibility);onAttachedToWindow();ListenerInfo li = mListenerInfo;final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =li != null ? li.mOnAttachStateChangeListeners : null;if (listeners != null && listeners.size() > 0) {// NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to// perform the dispatching. The iterator is a safe guard against listeners that// could mutate the list by calling the various add/remove methods. This prevents// the array from being modified while we iterate it.for (OnAttachStateChangeListener listener : listeners) {listener.onViewAttachedToWindow(this);}}int vis = info.mWindowVisibility;if (vis != GONE) {onWindowVisibilityChanged(vis);}// Send onVisibilityChanged directly instead of dispatchVisibilityChanged.// As all views in the subtree will already receive dispatchAttachedToWindow// traversing the subtree again here is not desired.onVisibilityChanged(this, visibility);if ((mPrivateFlags&PFLAG_DRAWABLE_STATE_DIRTY) != 0) {// If nobody has evaluated the drawable state yet, then do it now.refreshDrawableState();}needGlobalAttributesUpdate(false);}

dispatchDetachedFromWindow(倒数第三行)中赋空

void dispatchDetachedFromWindow() {AttachInfo info = mAttachInfo;if (info != null) {int vis = info.mWindowVisibility;if (vis != GONE) {onWindowVisibilityChanged(GONE);}}onDetachedFromWindow();onDetachedFromWindowInternal();InputMethodManager imm = InputMethodManager.peekInstance();if (imm != null) {imm.onViewDetachedFromWindow(this);}ListenerInfo li = mListenerInfo;final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =li != null ? li.mOnAttachStateChangeListeners : null;if (listeners != null && listeners.size() > 0) {// NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to// perform the dispatching. The iterator is a safe guard against listeners that// could mutate the list by calling the various add/remove methods. This prevents// the array from being modified while we iterate it.for (OnAttachStateChangeListener listener : listeners) {listener.onViewDetachedFromWindow(this);}}if ((mPrivateFlags & PFLAG_SCROLL_CONTAINER_ADDED) != 0) {mAttachInfo.mScrollContainers.remove(this);mPrivateFlags &= ~PFLAG_SCROLL_CONTAINER_ADDED;}mAttachInfo = null;if (mOverlay != null) {mOverlay.getOverlayView().dispatchDetachedFromWindow();}}
  1. ViewRootImpl.getRunQueue()
    ViewRootImpl 的静态成员 sRunQueues 和静态函数getRunQueue
static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>();
static RunQueue getRunQueue() {RunQueue rq = sRunQueues.get();if (rq != null) {return rq;}rq = new RunQueue();sRunQueues.set(rq);return rq;}

ui线程执行“存到队列中的任务"

 // Execute enqueued actions on every traversal in case a detached view enqueued an actiongetRunQueue().executeActions(mAttachInfo.mHandler);

根源是sRunQueues.get(),其实也是ThreadLocal的特性。当子线程调用的时候,这里返回的rq 是空的,接着创建一个rt后存入。之后UI线程调用,这里返回的不是子线程创建的rq。

  1. ViewRootImpl.RunQueue.executeActions
 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.postDelayed(handlerAction.action, handlerAction.delay);}actions.clear();}}
  1. ThreadLocal.get()
    再进一步看一下这个ThreadLocal的get实现(get的样子往往容易被忽视)
public T get() {// Optimized for the fast path.Thread currentThread = Thread.currentThread();Values values = values(currentThread);if (values != null) {Object[] table = values.table;int index = hash & values.mask;if (this.reference == table[index]) {return (T) table[index + 1];}} else {values = initializeValues(currentThread);}return (T) values.getAfterMiss(this);}

好的,到这里已经看到取当前的线程做了一系列的事情,因此不同线程返回的自然就不一样。

10.0版本

  1. View.post
public boolean post(Runnable action) {final AttachInfo attachInfo = mAttachInfo;if (attachInfo != null) {return attachInfo.mHandler.post(action);}// Postpone the runnable until we know on which thread it needs to run.// Assume that the runnable will be successfully placed after attach.getRunQueue().post(action);return true;}

可以看到这里以不是用ViewRootImpl.getRunQueue(),而是view内部的函数getRunQueue().

  1. View.getRunQueue()
private HandlerActionQueue getRunQueue() {if (mRunQueue == null) {mRunQueue = new HandlerActionQueue();}return mRunQueue;}

好家伙,现在的队列是属于view的了,不再是归属于线程,变成了共享变量。
因此子线程向队列里面添加一个runnable之后,ui线程做来取队列就能取到。执行就是我们期望的结果了。

  1. View.dispatchAttachedToWindow
 void dispatchAttachedToWindow(AttachInfo info, int visibility) {mAttachInfo = info;if (mOverlay != null) {mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility);}mWindowAttachCount++;// We will need to evaluate the drawable state at least once.mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY;if (mFloatingTreeObserver != null) {info.mTreeObserver.merge(mFloatingTreeObserver);mFloatingTreeObserver = null;}registerPendingFrameMetricsObservers();if ((mPrivateFlags&PFLAG_SCROLL_CONTAINER) != 0) {mAttachInfo.mScrollContainers.add(this);mPrivateFlags |= PFLAG_SCROLL_CONTAINER_ADDED;}// Transfer all pending runnables.if (mRunQueue != null) {mRunQueue.executeActions(info.mHandler);mRunQueue = null;}performCollectViewAttributes(mAttachInfo, visibility);onAttachedToWindow();ListenerInfo li = mListenerInfo;final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =li != null ? li.mOnAttachStateChangeListeners : null;if (listeners != null && listeners.size() > 0) {// NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to// perform the dispatching. The iterator is a safe guard against listeners that// could mutate the list by calling the various add/remove methods. This prevents// the array from being modified while we iterate it.for (OnAttachStateChangeListener listener : listeners) {listener.onViewAttachedToWindow(this);}}int vis = info.mWindowVisibility;if (vis != GONE) {onWindowVisibilityChanged(vis);if (isShown()) {// Calling onVisibilityAggregated directly here since the subtree will also// receive dispatchAttachedToWindow and this same callonVisibilityAggregated(vis == VISIBLE);}}// Send onVisibilityChanged directly instead of dispatchVisibilityChanged.// As all views in the subtree will already receive dispatchAttachedToWindow// traversing the subtree again here is not desired.onVisibilityChanged(this, visibility);if ((mPrivateFlags&PFLAG_DRAWABLE_STATE_DIRTY) != 0) {// If nobody has evaluated the drawable state yet, then do it now.refreshDrawableState();}needGlobalAttributesUpdate(false);notifyEnterOrExitForAutoFillIfNeeded(true);notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);}

其中下面这段就是UI线程来执行存入需要处理的任务:

 if (mRunQueue != null) {mRunQueue.executeActions(info.mHandler);mRunQueue = null;}

总结

子线程在onAttachedToWindow之后调用view.post,是有效的。其次是与系统版本有一定关系,出现问题的场景就是子线程处理的完成数据之后调用view.post时,onAttachedToWindow还没有回调,一般是activity onCreate函数中初始化完成view之前这段时间可能出现不执行的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/558258.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

扫地机器人划伤地板_扫地机器人哪个牌子好?会选的才能买到好产品

做为一种既时尚潮流又智能化的家用电器,扫地机器人一经面世就遭受大伙儿的关心。大家最先会感觉扫地机器人十分便捷,次之也会对它造成疑惑:就那么一台小小环形设备,确实能处理困惑人们这些年的打扫难题吗?事实上在扫地机器人的应用全过程中,客户对它的印像确实有很多种多样,有…

ThreadLocal初识

目录背景ThreadLocal的作用ThreadLocal特性ThreadLocal实现1. T get()2. set(T value)3. remove()验证一个对象只存一个数据多少个对象就能存多少个数据背景 这两天稍微有点空&#xff0c;在追溯之前的android 7.0之前的手机用View.post 出现不执行的问题时&#xff0c;结识了…

Gensee SDK UserInfo类函数详细说明

目录基本信息rolestatusclientType其他UserInfo是一个公共的类&#xff0c;用于RtSDK和PlayerSDK。由于role和status是复合1量&#xff0c;列出相关的判断方式&#xff0c;true为注释说明&#xff0c;false反之。 如 isHost() true 老师/false 不是老师。 基本信息 getUserId…

python中shutil模块_Python中shutil模块的学习笔记教程

介绍shutil 名字来源于 shell utilities&#xff0c;有学习或了解过Linux的人应该都对 shell 不陌生&#xff0c;可以借此来记忆模块的名称。该模块拥有许多文件(夹)操作的功能&#xff0c;包括复制、移动、重命名、删除等等一、chutil.copy(source, destination)shutil.copy()…

HTML转义字符大全<转>

为什么要用转义字符串&#xff1f; HTML中<&#xff0c;>&#xff0c;&等有特殊含义&#xff08;<&#xff0c;>&#xff0c;用于链接签&#xff0c;&用于转义&#xff09;&#xff0c;不能直接使用。这些符号是不显示在我们最终看到的网页里的&#xff0c…

python测网速_tespeed-测试网速的Python工具

1.安装(环境CentOS7)#pip install lxml#wget wget http://sourceforge.net/projects/socksipy/files/socksipy/SocksiPy%201.00/SocksiPy.zip#git clone https://github.com/Janhouse/tespeed.git#mv SocksiPy.zip tespeed/SocksiPy/#cd tespeed/SocksiPy/#unzip SocksiPy.zip#…

Gensee SDK RoleType详解

目录RoleType使用场景&#xff1a;方法与使用说明源码RoleType 接着之前的 UserInfo,对RoleType做进一步说明. 此类是SDK的公共类&#xff0c;可以在任一SDK使用方进行调用&#xff0c;之前有提到role是一个“复合”的值1&#xff0c;所以不能简单的用“” 来判断&#xff0c;…

vc 通过句柄修改窗口大小_漫画:对象是如何被找到的?句柄 OR 直接指针?

小贴士&#xff1a;想要使用并定位 Java 对象&#xff0c;就要用到 Java 虚拟机栈&#xff08;Java Virtual Machine Stack&#xff09;&#xff0c;它描述的是 Java 方法执行的线程内存模型&#xff1a;每个方法被执行的时候&#xff0c;Java 虚拟机都会同步创建一个栈帧&…

数据结构快速掌握和温习-面试神器

由于原文有部分文字没有显示&#xff0c;本文有所修改。主要包括文字和缩进。 目录 Q1&#xff1a;数据结构和算法的知识点整理&#xff1a; Q2&#xff1a;链表&#xff0c;队列和栈的区别 Q3&#xff1a;简述快速排序过程 Q4&#xff1a;快速排序算法的原理 Q5&#xff1a;简…

python阴阳师_如何用Python找到阴阳师妖怪屋的最佳探索队伍!强不强?

程序由来最初想要写这个小程序是因为&#xff0c;9月份那段时间我在玩妖怪屋。因为刚开始抽卡&#xff0c;要啥啥没有&#xff0c;所以探索队伍也只是放了几个自己记得有关联的式神。在网上有找到完整版的羁绊&#xff0c;但是怎么根据羁绊找到最佳的式神组合就成问题了。当时我…

Android iOS防录屏截屏

目录Android防录屏和截屏关于WindowManager.LayoutParams.FLAG_SECURE关于Display.FLAG_SECUREiOS防录屏和截屏监听截屏录屏监听需求与安全总是对立的&#xff0c;有新的需求&#xff0c;就有新的接口开放&#xff0c;但随之而来的就是利用新接口或者新接口的使用者&#xff08…

work节点使用外部包_AFLSmart工具简单分析及使用介绍

AFLSmart 是一个在 AFL 基础上&#xff0c;结合了 Peach 的结构化输入组件的灰盒 smart fuzz 工具。AFLSmart 链接&#xff1a;https://github.com/aflsmart/aflsmart参考资料&#xff1a;《Smart Greybox Fuzzing》什么是 AFLSmart灰盒 smart fuzz灰盒测试是基于程序运行时刻的…

Android adb 启动APP

目录启动命令一 常规命令 包名/activity二 常规命令 包名命令关闭App获取包名和activity的路径代码获取1 命令获取( 需要app运行在前台&#xff0c;停留在启动界面)2命令获取(先执行命令&#xff0c;再点击app启动)启动命令 一 常规命令 包名/activity adb shell am start…

python语言使用什么语句实现上下文管理协议_Python 上下文管理器

上下文管理器在使用Python编程中&#xff0c;可以会经常碰到这种情况&#xff1a;有一个特殊的语句块&#xff0c;在执行这个语句块之前需要先执行一些准备动作&#xff1b;当语句块执行完成后&#xff0c;需要继续执行一些收尾动作。例如&#xff1a;当需要操作文件或数据库的…

Android日志[基础篇]Android Log日志输出

Android日志[基础篇]二 Android Studio修改LogCat日志的颜色 android.util.Log输出日志的常用方法如下&#xff1a; Log.v(String tag, String msg)Log.d(String tag, String msg)Log.i(String tag, String msg)Log.w(String tag, String msg)Log.e(String tag, String msg) …

python函数应用_python 函数应用

#函数的参数就是个变量#定义函数的时候&#xff0c;使用关键字参数&#xff0c;可以指定默认值def hello(namereboot,age1):return hello %s,your age is %s %(name,age)print hello(reboot,3)print hello(3,reboot)#print hello(age3,namereboot)print hello(reboot)def f(n):…

Android日志[基础篇]二 Android Studio修改LogCat日志的颜色

上一篇提到Android日志的5个级别的日志输出&#xff0c;在logcat里面设置自己喜欢或习惯的颜色&#xff0c;本文不只讲Android Sudio修改logcat的日志颜色。 代码和效果 代码 private void logColor(){Log.v(TAG,"logColor verbose");Log.d(TAG,"logColor de…

readfile函数使用方法_1分钟学会LOOKUP函数,有网友说使用这个方法,初学者秒变大神...

Hi&#xff0c;大家好&#xff0c;本专栏将会从零开始和大家用图文的方式&#xff0c;30天让你从不会到熟练使用函数&#xff0c;0基础开始学习Excel函数&#xff0c;让你喜欢上它&#xff01;有兴趣的小伙伴可以持续关注我&#xff0c;或者在专栏进行查看学习&#xff0c;愿与…

Android JNI Attempt to remove non-JNI local reference, dumping thread

Attempt to remove non-JNI local reference, dumping thread 解决办法&#xff1a; 去除Jni代码 env->DeleteLocalRef(javaObject);注意&#xff1a;是java层传递给jni层的对象不需用了DeleteLocalRef来进行对象删除&#xff0c;jni层创建的对象仍然需要保留代码。 这个…

背景图层和普通图层的区别_图层样式(一)—高级混合选项

一、图层顺序为了便于说明&#xff0c;首先建立例子&#xff0c;新建图层&#xff0c;用画笔随便画个圈&#xff0c;新建蒙版随便画一笔&#xff0c;然后把所有图层样式加给它。可以看到样式从上到下的顺序&#xff0c;这也是它们混合的图层顺序。图层顺序我的效果&#xff0c;…