深入解析Android中Handler消息机制
来源:互联网 发布:淘宝网企业店铺申请 编辑:程序博客网 时间:2024/05/29 11:31
Android提供了Handler 和 Looper 来满足线程间的通信。Handler先进先出原则。Looper类用来管理特定线程内对象之间的消息交换(MessageExchange)。Handler消息机制可以说是Android系统中最重要部分之一,所以,本篇博客我们就来深入解析Android中Handler消息机制。
Handler的简单使用
为什么系统不允许子线程更新UI
因为的UI控件不是线程安全的。
如果在多线程中并发访问可能会导致UI控件处于不可预期的状态,那为什么不对UI控件的访问加上上锁机制呢?因为有这么两个缺点:
1.上锁会让UI控件变得复杂和低效
2.上锁后会阻塞某些进程的执行
对于手机系统来说,这两个缺点是不可接受的,所以最简单高效的方法就是 —— 采用单线程模型来处理UI操作。
对开发者而言也不是很麻烦,只是通过Handler切换一下访问的线程的就好。
Handler的简单使用
既然子线程不能更改界面,那么我们现在就借助Handler让我们更改一下界面:
主要步骤是这样子的:
1.new出来一个Handler对象,复写handleMessage方法
2.在需要执行更新UI的地方 sendEmptyMessage 或者 sendMessage
3.在handleMessage里面的switch里面case不同的常量执行相关操作
public class MainActivity extends ActionBarActivity { private TextView mTextView; private Handler mHandler; private static final int UI_UPDATE1 = 0; private static final int UI_UPDATE2 = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case UI_UPDATE1: mTextView.setText("通过Handler方法1修改UI"); break; case UI_UPDATE2: mTextView.setText("通过Handler方法2修改UI"); break; } } }; mTextView = (TextView) findViewById(R.id.textview); mTextView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Log.d("Test", "点击文字"); updateUi(); } }); } protected void updateUi() { new Thread(new Runnable() { @Override public void run() { // 方式一和方式二可以达到相同的效果,就是更改界面 //方式一 //mHandler.sendEmptyMessage(UI_UPDATE1); //方式二 Message msg = Message.obtain(); msg.what = UI_UPDATE2; mHandler.sendMessage(msg); } }).start(); }}
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.handlerdemo3.MainActivity" > <TextView android:id="@+id/textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" android:layout_centerInParent="true" android:clickable="true" /></RelativeLayout>
消息机制的分析理解
安卓的异步消息处理机制就是handler机制。
主线程,ActivityThread被创建的时候就会创建Looper
Looper被创建的时候创建MessageQueue。
也就是说主线程会直接或间接创建出来Looper和MessageQueue。
Handler的工作机制简单来说是这样的
1.Handler发送消息仅仅是调用MessageQueue的enqueueMessage向插入一条信息到MessageQueue
2.Looper不断轮询调用MeaasgaQueue的next方法
3.如果发现message就调用handler的dispatchMessage,dispatchMessage被成功调用,接着调用handlerMessage()
Handler消息机制的源码分析
ThreadLocal工作原理
首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。
另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本。通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map中,每个线程都有这样一个map,执行ThreadLocal.get()时,各线程从自己的map中取出放进去的对象,因此取出来的是各自自己线程中的对象,ThreadLocal实例是作为map的key来使用的。
如果ThreadLocal.set()进去的东西本来就是多个线程共享的同一个对象,那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,还是有并发访问问题。
我们看一下ThreadLocal的set方法
public void set(T value) { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) { values = initializeValues(currentThread); } values.put(this, value);}
查看values方法里面做了什么
Values values(Thread current) { return current.localValues;}
通过上面代码我们可以知道,当values为空的时候,才调用initializeValues方法进行初始化,查看inheritValues相关逻辑:
private void inheritValues(Values fromParent) { // Transfer values from parent to child thread. Object[] table = this.table; for (int i = table.length - 2; i >= 0; i -= 2) { Object k = table[i]; if (k == null || k == TOMBSTONE) { // Skip this entry. continue; } // The table can only contain null, tombstones and references. Reference<InheritableThreadLocal<?>> reference = (Reference<InheritableThreadLocal<?>>) k; // Raw type enables us to pass in an Object below. InheritableThreadLocal key = reference.get(); if (key != null) { // Replace value with filtered value. // We should just let exceptions bubble out and tank // the thread creation table[i + 1] = key.childValue(fromParent.table[i + 1]); } else { // The key was reclaimed. table[i] = TOMBSTONE; table[i + 1] = null; fromParent.table[i] = TOMBSTONE; fromParent.table[i + 1] = null; tombstones++; fromParent.tombstones++; size--; fromParent.size--; } } }
其实就是各种赋值table数组,进行初始化
最后才是调用values.put(this, value)把ThreadLocal和value一起保存,我们可以看一下values.put(this, value)方法
void put(ThreadLocal<?> key, Object value) { cleanUp(); // Keep track of first tombstone. That's where we want to go back // and add an entry if necessary. int firstTombstone = -1; for (int index = key.hash & mask;; index = next(index)) { Object k = table[index]; if (k == key.reference) { // Replace existing entry. table[index + 1] = value; return; } if (k == null) { if (firstTombstone == -1) { // Fill in null slot. table[index] = key.reference; table[index + 1] = value; size++; return; } // Go back and replace first tombstone. table[firstTombstone] = key.reference; table[firstTombstone + 1] = value; tombstones--; size++; return; } // Remember first tombstone. if (firstTombstone == -1 && k == TOMBSTONE) { firstTombstone = index; } } }
可以看出,Threadlocal的值在table数组的存储位置总是reference的下一个位置.
接下来,查看Threadlocal的get方法
Get方法的逻辑是:通过values方法取出当前线程的localValues对象,如果为null,就返回初始值。如果localValues不为null,取出其table数组,如果reference等于table数组index角标的值,就在table[index + 1]取出其Threadlocal值。
MessageQueue工作原理
MessageQueue中文翻译就是消息队列,它内部存储了一组信息,存放的是Message,以队列的形式对外提供了插入和删除的工作(虽然名字叫做队列,但是其内部的 存储结构是单链表)
主要 插入 和 读取 两个操作,这两个操作对应着两个方法:
插入(入队) enqueueMessage(Message msg, long when)读取(出队) next()
查看enqueueMessage的源码:
boolean enqueueMessage(Message msg, long when) { if (msg.isInUse()) { throw new AndroidRuntimeException(msg + " This message is already in use."); } if (msg.target == null) { throw new AndroidRuntimeException("Message must have a target."); } boolean needWake; synchronized (this) { if (mQuiting) { RuntimeException e = new RuntimeException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", e.getMessage(), e); return false; } msg.when = when; Message p = mMessages; 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; } } if (needWake) { nativeWake(mPtr); } return true;
从enqueueMessage的实现来看,主要操作就是单链表的插入操作,接下来查看next方法的实现:
Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; } }
从上面代码可以得出以下结论:
1.next方法是一个无限循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞
2.当有新消息到来,next方法会返回这条消息,并将其从单链表中移除。
Looper的工作原理
Looper是一个轮询器,它的作用不断轮询MessageQueue,当如果有新的消息就交给Handler处理,如果轮询不到新的消息,那就自身就处于阻塞状态。
查看Looper类的源码,可以发现的他的构造方法里面创建了一个MessageQueue,然后将当前线程的对象保存起来
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
在Looper的构造方法中初始化了一个消息队列MessageQueue和一个线程Thread。从这可看出:一个Looper对应着一个消息队列以及当前线程。
当收到消息Message后系统会将其存入消息队列中等候处理。至于Looper,它在Android的消息机制中担负着消息轮询的职责,它会不间断地查看MessageQueue中是否有新的未处理的消息;若有则立刻处理,若无则进入阻塞。
相信大家一定有遇到过,在子线程中创建Handler会报如下错误
解决办法就是new Handler的时候加上Looper.prepare();
而Looper.prepare()的内部实现逻辑就是创建一个Looper
public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
线程默认是没有Looper的,但是为什么在主线程没有创建的Looper就可以使用Handler?主线程是特别的。主线程,也就是ActivityThread,当主线程被创建的时候,会调用Looper内的prepareMainLooper方法,创建Looper,该方法是专门给主线程创建Looper用的。也正因为这点,所以我们在主线程创建了Handler就直接能用了。
public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
由于这个特殊性,Looper还提供了一个getMainLooper方法,使得可以在任何地方获取主线程的Looper。
public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } }
接下来,我们看一下Looper的退出
Looper提供了两个方法进行退出操作,分别是quit和quitSafely,他们调用的是MessageQueue的quit方法
public void quit() { mQueue.quit(false); }
public void quitSafely() { mQueue.quit(true); }
MessageQueue的quit方法:
void quit(boolean safe) { if (!mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit."); } synchronized (this) { if (mQuitting) { return; } mQuitting = true; if (safe) { removeAllFutureMessagesLocked(); } else { removeAllMessagesLocked(); } // We can assume mPtr != 0 because mQuitting was previously false. nativeWake(mPtr); } }
如果调用Looper.quit方法,最终会调用removeAllMessagesLocked方法,该方法逻辑:直接遍历所有的消息,并将消息强制回收
private void removeAllMessagesLocked() { Message p = mMessages; while (p != null) { Message n = p.next; p.recycleUnchecked(); p = n; } mMessages = null; }
如果调用Looper.quitSafely方法,最终会调removeAllFutureMessagesLocked方法,该方法逻辑:
void removeCallbacksAndMessages(Handler h, Object object) { if (h == null) { return; } synchronized (this) { Message p = mMessages; // Remove all messages at front. while (p != null && p.target == h && (object == null || p.obj == object)) { Message n = p.next; mMessages = n; p.recycleUnchecked(); p = n; } // Remove all messages after front. while (p != null) { Message n = p.next; if (n != null) { if (n.target == h && (object == null || n.obj == object)) { Message nn = n.next; n.recycleUnchecked(); p.next = nn; continue; } } p = n; } } }
一个无限for循环,只有n.when > now和n == null才会跳出循环,说明是等消息队列中已有的消息处理完毕后,才会跳出,然后执行回收。
Looper这个类里面最重要的方法就是loop()开启消息循环这个方法了,看一下loop代码的实现逻辑:
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; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. 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.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); } }
通过代码我们知道:looper方法是一个死循环,唯一跳出的循环的方式是MessageQueue的next方法返回null,但这几乎不可能,因为在MessageQueue的next方法中,假如没有消息加入队列,next方法会一直阻塞,不会返回null。如果我们不手动调用quit或者quitSafely方法的话,MessageQueue的next方法是不可能返回null的。
当MessageQueue没有消息时,next方法会一直阻塞在那里,因为MessageQueue的next方法阻塞了,就导致Looper的loop方法也一直在阻塞了。
这里我们那一分为二的谈,
loop轮询不到消息:那么处于阻塞状态,然后就没有然后了,除了又轮询到了新的消息
loop轮到了新的消息:Looper就会处理消息
1、msg.target.dispatchMessage(msg),这里的 msg.target就是指Handler对象
2、到了最后,Handler发送的消息又交给了自己的dispatchMessage方法来处理了。(这个dispatchMessage方法不是Handler自己调用时,是与Handler相相关的Looper间接调用的),这样下来,就成功地将逻辑切换到指定的线程当中去了
Handler的工作原理
Handler的主要工作:消息的 发送 和 接收 。Handler消息发送的形式有post和send两种。
查看Handler中sendMessage代码
public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); }
sendMessage调用sendMessageDelayed
public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }
sendMessageDelayed调用sendMessageAtTime
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); }
sendMessageAtTime调用enqueueMessage
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) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } synchronized (this) { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } 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; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }
得出结论:Handler的发送消息仅仅是调用MessageQueue的enqueueMessage向插入一条信息到MessageQueue,MessageQueue就会返回这条消息给Looper,Looper会交给msg.target.dispatchMessage(msg)方法处理,就进入消息处理阶段。
查看dispatchMessage方法
/** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
首先,判断 Message. Callback是否为null,不为null,就调用handleCallback方法
private static void handleCallback(Message message) { message.callback.run(); }
Callback是一个Runnable对象,实际上就是post方法传递的Runnable参数。
其次,检查mCallback是否为null,如果不为null就调用mCallback.handleMessage方法,查看mCallback.handleMessage方法
public interface Callback { public boolean handleMessage(Message msg); }
源码中注释已经对Callback进行了解释:
可以用来创建一个Handler的实例但不需要派生Handler的子类
在日常开发中,创建Handler最常见的方式就是派生一个Handler的子类并重写handleMessage方法来处理具体的消息,而Callback给我们提供了另外一种方式,不需要派生Handler的子类。
最后,调用Handler的handleMessage方法,这就是我们平时写Handler要实现的方法
主线程的消息循环
Android的主线程就是ActivityThread,主线程的路口方法是main方法,在 main中系统会通过Looper.prepareMainLooper()来创建主线程的Looper以及MessageQueue, 并通过Looper.loop()开启主线程消息循环
public static void main(String[] args) { SamplingProfilerIntegration.start(); // CloseGuard defaults to true and can be quite spammy. We // disable it here, but selectively enable it later (via // StrictMode) on debug builds, but using DropBox, not logs. CloseGuard.setEnabled(false); Environment.initForCurrentUser(); // Set the reporter for event logging in libcore EventLogger.setReporter(new EventLoggingReporter()); Security.addProvider(new AndroidKeyStoreProvider()); Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } AsyncTask.init(); if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
主线程开始循环后,ActivityThread还需要一个Handler来和消息队列进行交互,这个Handler就是ActivityThread.H
ApplicationThread通过binder与Ams通信,并将Ams的调用,通过H类(也就是Hnalder)将消息发送到消息队列,然后进行相应的操作,H收到消息后,就会将ApplicationThread中逻辑切换到ActivityThread中执行,也就是主线程中执行,这个过程就是主线程的消息循环。
至此,Handler消息机制就分析完毕,如有错漏,欢迎留言指证。
- 深入解析Android中Handler消息机制
- 从源码来一步一步解析Android中Handler消息机制
- Android 中Handler消息机制
- Android中Handler消息机制
- 深入了解android中的消息机制Handler
- Android开发Handler消息机制深入探究
- 深入理解Android Handler 消息机制
- 深入解析android 消息机制
- Android源码解析Handler系列第(三)篇---深入了解Android的消息机制
- 解析Android消息处理机制:Handler…
- 解析Android的 消息传递机制Handler
- 解析Android的 消息传递机制Handler
- Android 消息机制,Looper、Handler、Message 解析
- 解析Android的消息传递机制Handler
- Android消息机制解析——Handler
- android消息机制 - Handler、Looper原理解析
- Android消息机制Handler源码简单解析
- android消息机制(handler运行机制)解析
- MyBatis的flushCache和useCache的使用
- linux信号总结
- 每天一个linux命令:diff 命令
- Android ANR异常及解决方法
- Linux上的Shebang符号(#!)
- 深入解析Android中Handler消息机制
- Test
- Struts2 ognl表达式调用方法不抛懒加载异常!!!
- 使用 NGINX 流控和 fail2ban 防止 CC 攻击
- 静态库和动态库的使用
- Ubuntu16.04系统设置与优化方案
- 读写锁和两种同步方式的对比
- shell图形化菜单界面
- android webview上传文件