Android(安卓)子线程修改UI方法对比
Android 子线程修改UI方法对比
在Android 开发中经常是在子线程中获取数据或者处理数据,然后修改UI显示,修改UI一般有四种方法:
1.Handler()的handleMessage()和handler.sendMessage(msg)
2.handler.post(runnable)和handler.postDelayed(runnable,milliseconds)
3.activity.runOnUiThread();
4.View.post(runnable)和View.postDelayed(runnable,milliseconds);
原理
1.Handler()的handleMessage()和handler.sendMessage(msg)
使用时创建Handler对象:
private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); } };
在子线程中handler发送message
new Thread(){ @Override public void run() { super.run(); Message message = Message.obtain(); handler.sendMessage(message); } }.start();
sendMessage()方法会最终会调用Handler.enqueueMessage()方法把Message放入MessageQueue中;
public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); }public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); }private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); } boolean enqueueMessage(Message msg, long when) { ........ msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } return true; }
在Looper循环中通过Looper的next方法取出Message,调用msg.target.dispatchMessage(msg);将message分发出去;
public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } ....... } }
msg.target是Handler的一个实例,dispatchMessage()是Handler的方法;
首先判断msg.callback不为null时,执行handleCallback(msg);
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
callback是一个Runnable对象,当通过handler.post方法发送一个runnable的时候就会被封装到这个msg中,
handleCallback(msg)方法直接调用的是Runnable的run方法
private static void handleCallback(Message message) { message.callback.run(); }
mCallback是一个Callback接口对象,构造Handler时可以传递一个Callback 对象进来,实现 handleMessage()方法,当handleMessage()返回true时就不在执行Handler的handleMessage()方法
public interface Callback { public boolean handleMessage(Message msg); }
private Handler mHandler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { return false; } });
以上就是调用Handler的sendMessage()方法的执行流程;
2.handler.post(runnable)和handler.postDelayed(runnable,milliseconds)
handler.post()和handler.postDelayed()方法都会调用sendMessageDelayed()方法;
最后还是通过Handler的enqueueMessage() 将Message放入MessageQuene中;
getPostMessage()方法会新建一个Message将Runnable 传递给Message的callback 最后执行的时候回判断callback 不为null时执行Runnable 的run方法
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
3.activity.runOnUiThread();
获取当前线程是否是主线程,如果是主线程就直接执行Runnable 的run方法;不是主线程就通过Handler post(Runnable )形式将message放入MessageQuene中,最后通过Handler的dispatchMessage方法执行Runnable 的run方法
public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { mHandler.post(action); } else { action.run(); } }
4.View.post(runnable)和View.postDelayed(runnable,milliseconds);
这种方式会先判断一下 attachInfo 是否为null;如果不为null,会调用attachInfo.mHandler.post(runnable);AttachInfo 里的mHandler是ViewRootHandler对象,ViewRootHandler是Handler的子类;最后还是通过handler来调用runnable的run方法;如果attachInfo 为null就调用getRunQueue().post(action);
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; }public boolean postDelayed(Runnable action, long delayMillis) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.postDelayed(action, delayMillis); } // Postpone the runnable until we know on which thread it needs to run. // Assume that the runnable will be successfully placed after attach. getRunQueue().postDelayed(action, delayMillis); return true; }
AttachInfo 是什么东西?
AttachInfo 看到这个类名,我们就知道,他是代表着绑定的信息,View.AttachInfo 里面的信息,就是View和Window之间的信息。每一个被添加到窗口上的View我们都会看到有一个AttachInfo,AttachInfo 会通过View的diapatchAttachedTowWindow分发给View。如果是一个ViewGroup 那么这个这个AttachInfo也会以引用的方式分发给所有子View。
void dispatchAttachedToWindow(AttachInfo info, int visibility) { mAttachInfo = info; if (mOverlay != null) { mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility); } ..... }
attachInfo == null 说明view还没有显示到window上,如果View还未attach到window,将runnable放入ViewRootImpl的RunQueue中。
那么post到RunQueue里的runnable什么时候执行呢,又是为何当View还没attach到window的时候,需要post到RunQueue中。
View#post与Handler#post的区别
其实,当View已经attach到了window,两者是没有区别的,都是调用UI线程的Handler发送runnable到MessageQueue,最后都是由handler进行消息的分发处理。
但是如果View尚未attach到window的话,runnable被放到了ViewRootImpl#RunQueue中,最终也会被处理,但不是通过MessageQueue。
ViewRootImpl#RunQueue源码注释如下:
/** * The run queue is used to enqueue pending work from Views when no Handler is * attached. The work is executed during the next call to performTraversals on * the thread. * @hide */
大概意思是当视图树尚未attach到window的时候,整个视图树是没有Handler的(其实自己可以new,这里指的handler是AttachInfo里的),这时候用RunQueue来实现延迟执行runnable任务,并且runnable最终不会被加入到MessageQueue里,也不会被Looper执行,而是等到ViewRootImpl的下一个performTraversals时候,把RunQueue里的所有runnable都拿出来并执行,接着清空RunQueue。
由此可见RunQueue的作用类似于MessageQueue,只不过,这里面的所有
runnable最后的执行时机,是在下一个performTraversals到来的时候,MessageQueue里的消息处理的则是下一次loop到来的时候。
ViewRootImpl#performTraversals:
private void performTraversals() { // .... // Execute enqueued actions on every traversal in case a detached view enqueued an action getRunQueue().executeActions(mAttachInfo.mHandler); // ....}void executeActions(Handler handler) { synchronized (mActions) { final ArrayList 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(); }}
也就是说,当View没有被attach到window的时候,最后runnable的处理不是通过MessageQueue,而是ViewRootImpl自己在下一个performTraversals到来的时候执行。最后还是handler.postDelayed(handlerAction.action, handlerAction.delay);执行run()方法。
更多相关文章
- Android中打电话的数据流程
- Android(安卓)最火框架XUtils之注解机制详解
- Android(安卓)SDK 2.2 下载安装方法
- Android的service相关讲解
- Android中全屏或者取消标题栏
- [置顶] Android(安卓)MediaPlayer+Stagefright框架(音频)图解
- 监控android binder size
- 浅谈Java中Collections.sort对List排序的两种方法
- Python list sort方法的具体使用