非UI线程下页面处理:view的postInvalidate和post对消息处理的差异化
我们知道view有一系列post方法 用于在非UI线程中发出一些页面处理。view还有另外一个postInvalidate方法 同样在非UI线程中发起重绘。
同样是在非UI线程向UI线程发出消息 但是这里面有很大的区别。
1、postInvalidate先来看看postInvalidate
public void postInvalidate() { postInvalidateDelayed(0); public void postInvalidateDelayed(long delayMilliseconds) { // We try only with the AttachInfo because there s no point in invalidating // if we are not attached to our window final AttachInfo attachInfo mAttachInfo; if (attachInfo ! null) { attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds); 复制代码
可以看到当mAttachInfo为null的时候 这个流程就直接结束了。而mAttachInfo则是当view被DetachedFromWindow的时候会被置为null 代码如下
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) { 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(); 复制代码
所以当view被从页面上移除后 postInvalidate就无效了。
当mAttachInfo不为null的时候 则执行mViewRootImpl的dispatchInvalidateDelayed函数 代码如下
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) { Message msg mHandler.obtainMessage(MSG_INVALIDATE, view); mHandler.sendMessageDelayed(msg, delayMilliseconds); 复制代码
直接用mHandler发出了消息。
下面再来看看post
public boolean post(Runnable action) { final AttachInfo attachInfo mAttachInfo; if (attachInfo ! null) { return attachInfo.mHandler.post(action); // Assume that post will succeed later ViewRootImpl.getRunQueue().post(action); return true; 复制代码
同样当mAttachInfo不为null的时候 直接使用mHandler发出消息。
但是 注意但是 当mAttachInfo为null时 并不直接结束流程 而是将runnable存入了一个RunQueue。RunQueue是一个队列 部分代码如下
static final class RunQueue { private final ArrayList HandlerAction mActions new ArrayList HandlerAction void post(Runnable action) { postDelayed(action, 0); void postDelayed(Runnable action, long delayMillis) { HandlerAction handlerAction new HandlerAction(); handlerAction.action action; handlerAction.delay delayMillis; synchronized (mActions) { mActions.add(handlerAction); void removeCallbacks(Runnable action) { void executeActions(Handler handler) { synchronized (mActions) { final ArrayList HandlerAction actions mActions; final int count actions.size(); for (int i i count; i ) { final HandlerAction handlerAction actions.get(i); handler.postDelayed(handlerAction.action, handlerAction.delay); actions.clear(); private static class HandlerAction { 复制代码
当RunQueue的executeActions函数被调用时 会遍历队列再去用handler发送消息。
那么executeActions什么时候被调用
在ViewRootImpl的performTraversals函数中 如下
private void performTraversals() { // Execute enqueued actions on every traversal in case a detached view enqueued an action getRunQueue().executeActions(mAttachInfo.mHandler); 复制代码
而performTraversals在doTraversal函数中被调用
void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing( ViewAncestor performTraversals(); if (mProfile) { Debug.stopMethodTracing(); mProfile false; 复制代码
doTraversal则在ViewRootImpl中一个Runnable对象mTraversalRunnable中执行
final class TraversalRunnable implements Runnable { Override public void run() { doTraversal(); final TraversalRunnable mTraversalRunnable new TraversalRunnable(); 复制代码
mTraversalRunnable则在ViewRootImpl的scheduleTraversals函数中被post出去
void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled true; mTraversalBarrier mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); 复制代码
而scheduleTraversals则在很多地方被执行 比如
void handleAppVisibility(boolean visible) { if (mAppVisible ! visible) { mAppVisible visible; scheduleTraversals(); if (!mAppVisible) { WindowManagerGlobal.trimForeground(); void handleGetNewSurface() { mNewSurfaceNeeded true; mFullRedrawNeeded true; scheduleTraversals(); 复制代码
这里就不一一列举了 大家有兴趣可以自己去源码里搜索一下。
总结一下就是当view被从页面上移除后 通过post系列函数传的消息并不会立刻用handler发出去 而是先将其存入一个队列里。当view再次被添加到页面上时 会从队列中的取出消息再用handler发出去。
所以当我们使用
post(new Runnable() { Override public void run() { invalidate(); 复制代码
它其实与postInvalidate还是有区别的。
线程与更新UI,细谈原理(下) 相信不少读者都阅读过相类似的文章了,但是我还是想完整的把这之间的关系梳理清楚,细节聊好,希望你也能从中学到一些。
线程与更新UI,细谈原理(上) 相信不少读者都阅读过相类似的文章了,但是我还是想完整的把这之间的关系梳理清楚,细节聊好,希望你也能从中学到一些。
WPF异常捕获三种处理 UI线程, 全局异常,Task异常 原文:WPF异常捕获三种处理 UI线程, 全局异常,Task异常 protected override void OnStartup(StartupEventArgs e){base.OnStartup(e);RegisterEvents();} private void RegisterEvents(){//TaskScheduler.
C#.NET使用Task,await,async,异步执行控件耗时事件(event),不阻塞UI线程和不跨线程执行UI更新,以及其他方式比较 原文:C#.NET使用Task,await,async,异步执行控件耗时事件(event),不阻塞UI线程和不跨线程执行UI更新,以及其他方式比较 使用Task,await,async,异步执行事件(event),不阻塞UI线程和不跨线程执行UI更新 使用Task,await,async 的异步模式 去执行事件(event) 解决不阻塞UI线程和不夸跨线程执行UI更新报错的最佳实践,附加几种其他方式比较 由于是Winform代码和其他原因,本文章只做代码截图演示,不做界面UI展示,当然所有代码都会在截图展示。
.NET一个线程更新另一个线程的UI(两种实现方法及若干简化) 原文:.NET一个线程更新另一个线程的UI(两种实现方法及若干简化) 本片博文接上一篇:.NET多线程执行函数,给出实现一个线程更新另一个线程UI的两种方法。 Winform中的控件是绑定到特定的线程的(一般是主线程),这意味着从另一个线程更新主线程的控件不能直接调用该控件的成员。
线程1和线程2均为普通Java线程,在Android中创建,然后在这两个普通Java线程中使用Handler发送和接收消息。