Handler消息机制
来源:互联网 发布:化学博士就业前景 知乎 编辑:程序博客网 时间:2024/06/07 22:21
为什么不允许子线程访问UI?UI线程不是线程安全的,多线程并发访问会出问题。为什么不加锁呢?首先加锁机制会使UI访问逻辑变复杂,其次锁机制降低UI访问效率,因为锁机制会阻塞某些线程的执行
1.Handler无参构造函数
public Handler() { this(null, false); } public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
无参构造函数中生成以下两个重要的对象mLooper和mQueue
mLooper是什么呢?
public static Looper myLooper() { return sThreadLocal.get(); }
mLooper是一个ThreadLocal对象。ThreadLocal是一个线程内部的存储类,通过它可以在指定线程中存储数据(后面详细介绍)
mQueue是一个MessageQueue对象,看名字就知道这是用来存储Message的Queue。不过,真的是队列么?其实不是,查看源码就知道,里面是一个ArrayList
public final class MessageQueue { // True if the message queue can be quit. private final boolean mQuitAllowed; @SuppressWarnings("unused") private long mPtr; // used by native code Message mMessages; //*********************************在这里,看到了么?????????? private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>(); private IdleHandler[] mPendingIdleHandlers; private boolean mQuitting; // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout. private boolean mBlocked; 。。。。。。。。}
2.如何发送消息的
Handler中有好多发送消息的函数,最终都会调用这个函数
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); }
接下来我们看enqueueMessage
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
接下来转到MessageQueue的enqueueMessage
boolean enqueueMessage(Message msg, long when) { 。。。。。。 synchronized (this) { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", 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; }
3.如何接收消息
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 (false) Log.v("MessageQueue", "Returning message: " + msg); //*******************************************如果有消息那就返回并从单链表中移除 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("MessageQueue", "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; } }
4.如何让消息循环
Android主线程是ActivityThread(E:\adt\sdk\sources\android-23\android\app)。主线程入口方法是
public static void main(String[] args) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); 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()); AndroidKeyStoreProvider.install(); // Make sure TrustedCertificateStore looks in the right place for CA certificates final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); TrustedCertificateStore.setDefaultUserDirectory(configDir); Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
注意其中三句话
Looper.prepareMainLooper();if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler();}Looper.loop();
先看prepareMainLooper(就生成一个sMainLooper)
public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
再看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 (;;) { //********************************next寻找是否有消息,如果没有就阻塞。有的话就dispatchMessage 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 Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); 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(); } }
再看一下dispatchMessage
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
看,出现了熟悉的handleMessage了吧
5.ThreadLoacal介绍
ThreadLocal是一个线程内部的存储类,通过它可以在指定线程中存储数据。等取数据的时候就会从各自的线程中取一个数组
先来个小例子
public class MainActivity extends Activity { private ThreadLocal<String> mThreadLocal = new ThreadLocal<String>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mThreadLocal.set("Main"); Log.e("Main", mThreadLocal.get()); new Thread(new Runnable() { @Override public void run() { mThreadLocal.set("Thread1"); Log.e("Thread1", mThreadLocal.get()); } }).start(); new Thread(new Runnable() { @Override public void run() { mThreadLocal.set("Thread2"); Log.e("Thread2", mThreadLocal.get()); } }).start(); }}
结果是
为什么呢?这要从get,set函数说起
public void set(T value) { //*************************关键就在这里的currentThread,这样就保证了维护了数据的副本,彼此互不干扰 Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) { values = initializeValues(currentThread); } values.put(this, value); } public T get() { // Optimized for the fast path. //************************get的时候也是这样,先找到对应的Thread,在找到数据的副本 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); }
6.总结
主线程先通过这三句话开启循环
Looper.prepareMainLooper();if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler();}Looper.loop();
此时因为没有消息导致next阻塞。
然后我们自己new Handler。
以后每次sendMessage的时候就添加进MessageQueue里面。此时由于有Message了,loop中的next不会阻塞就能取到Message,就调用dispatchMessage
然后就调用我们重写的handleMessage方法完成消息的发送
如果不是主线程而是子线程就用如下三句开启循环
Looper.prepare();Handler handler = new Handler();Looper.loop();
- Handler消息传递机制
- Handler 消息传递机制
- handler消息机制
- Handler消息传递机制
- Handler消息传递机制
- Handler消息处理机制
- Handler 消息传递机制
- Android Handler消息机制
- Handler消息机制
- Handler消息机制浅谈
- android handler消息机制
- Handler消息机制
- android handler消息机制
- Handler消息机制
- Handler消息传递机制
- Handler消息传送机制
- handler消息机制
- Handler消息传递机制
- linux awk命令详解
- 谁说ajax不能跨域
- 1.初识Linux
- Android studio快捷键大全+Android studio使用小技巧
- STM32一个Timer输出4路不同频率、可调占空比的PWM
- Handler消息机制
- WEB前端之HTML5~HTML5与HTML4的区别
- 改变父元素的透明度,不影响子元素的透明度—css
- OVM-V1.4 版发布,可针对多磁盘和网卡分别监控
- Math与StrictMath的区别
- Java中的关键字transient
- MySQL日期时间函数大全
- Android学习笔记进阶16之BitmapShader
- 【BS学习】——jQuery总结