android笔记--android中的多线程--Handler, Looper, MessageQueue, Message类
什么时候使用多线程:
1. 耗时操作使用多线程, 耗时操作放在UI线程中会导致用户的操作无法得到响应.
2. 阻塞操作使用多线程, 理由同上.
3. 多核CUP的设备使用多线程, 可以有效提高CPU的利用率.
4. 并行操作使用多线程.
android中的多线程模型主要涉及的类有:Looper, Handler, MessageQueue, Message等.
一:Looper类:
1 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); 2 private static Looper sMainLooper; // guarded by Looper.class 3 4 final MessageQueue mQueue; 5 final Thread mThread; 6 public static void loop() { 7 final Looper me = myLooper(); 8 if (me == null) { 9 throw new RuntimeException("No Looper; Looper.prepare() wasn‘t called on this thread."); 10 } 11 final MessageQueue queue = me.mQueue; 12 13 // Make sure the identity of this thread is that of the local process, 14 // and keep track of what that identity token actually is. 15 Binder.clearCallingIdentity(); 16 final long ident = Binder.clearCallingIdentity(); 17 18 for (;;) { 19 Message msg = queue.next(); // might block 20 if (msg == null) { 21 // No message indicates that the message queue is quitting. 22 return; 23 } 24 25 // This must be in a local variable, in case a UI event sets the logger 26 Printer logging = me.mLogging; 27 if (logging != null) { 28 logging.println(">>>>> Dispatching to " + msg.target + " " + 29 msg.callback + ": " + msg.what); 30 } 31 32 msg.target.dispatchMessage(msg);//超级重要 33 34 if (logging != null) { 35 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); 36 } 37 38 // Make sure that during the course of dispatching the 39 // identity of the thread wasn‘t corrupted. 40 final long newIdent = Binder.clearCallingIdentity(); 41 if (ident != newIdent) { 42 Log.wtf(TAG, "Thread identity changed from 0x" 43 + Long.toHexString(ident) + " to 0x" 44 + Long.toHexString(newIdent) + " while dispatching to " 45 + msg.target.getClass().getName() + " " 46 + msg.callback + " what=" + msg.what); 47 } 48 49 msg.recycle(); 50 } 51 } 52 public void quit() { 53 mQueue.quit(false); 54 } 55 public void quitSafely() {//最好用此方法 56 mQueue.quit(true); 57 }
Looper类用来创建消息队列. 每个线程最多只能有一个消息队列:Looper唯一的构造方法如下
1 private Looper(boolean quitAllowed) { 2 mQueue = new MessageQueue(quitAllowed); 3 mThread = Thread.currentThread(); 4 }
android中UI线程默认具有消息队列, 但非UI线程在默认情况下是不具备消息队列的. 如果需要在非UI线程中开启消息队列, 需要调用Looper.prepare()方法, 在该方法的执行过程中会创建一个Looper对象
1 public static void prepare() { 2 prepare(true); 3 } 4 5 private static void prepare(boolean quitAllowed) { 6 if (sThreadLocal.get() != null) { 7 throw new RuntimeException("Only one Looper may be created per thread"); 8 } 9 sThreadLocal.set(new Looper(quitAllowed)); 10 }
而Looper的构造函数中会创建一个MessageQueue instance(Looper的构造函数是私有的, 在Looper类之外无法创建其对象).
此后再为该线程绑定一个Handler instance, 然后调用Looper.loop()方法, 就可以不断的从消息队列中取出消息和处理消息了.
1 public Handler() { 2 this(null, false); 3 } 4 public Handler(Callback callback) { 5 this(callback, false); 6 } 7 public Handler(Looper looper) { 8 this(looper, null, false); 9 } 10 public Handler(Looper looper, Callback callback) { 11 this(looper, callback, false); 12 } 13 public Handler(Callback callback, boolean async) { 14 if (FIND_POTENTIAL_LEAKS) { 15 final Class<? extends Handler> klass = getClass(); 16 if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && 17 (klass.getModifiers() & Modifier.STATIC) == 0) { 18 Log.w(TAG, "The following Handler class should be static or leaks might occur: " + 19 klass.getCanonicalName()); 20 } 21 } 22 23 mLooper = Looper.myLooper(); 24 if (mLooper == null) { 25 throw new RuntimeException( 26 "Can‘t create handler inside thread that has not called Looper.prepare()"); 27 } 28 mQueue = mLooper.mQueue; 29 mCallback = callback; 30 mAsynchronous = async; 31 } 32 public Handler(Looper looper, Callback callback, boolean async) { 33 mLooper = looper; 34 mQueue = looper.mQueue; 35 mCallback = callback; 36 mAsynchronous = async; 37 }
(创建handler的时候必须关联一个looper)
Looper.myLoop()方法可以得到线程的Looper对象, 如果为null, 说明此时该线程尚未开启消息队列.
1 public static Looper myLooper() { 2 return sThreadLocal.get(); 3 }
二:Handler:
Handler类用于处理消息. 该类具有四个构造函数:(如上)
1. public Handler(). 创建好的Handler instance将绑定在代码所在的线程的消息队列上, 因此一定要确定该线程开启了消息队列, 否则程序将发生错误. 使用这个构造函数创建Handler instance, 一般来说, 我们需要重写Hanler类的handleMessage()方法, 以便在之后的消息处理时调用.
2. public Handler(Callback callback). Callback是Handler内部定义的一个接口, 因此想要使用这个构造函数创建Handler对象, 需要自定义一个类实现Callback接口, 并重写接口中定义的handleMessage()方法. 这个构造函数其实与无参的构造函数类似, 也要确保代码所在的线程开启了消息队列. 不同的是在之后处理消息时, 将优先调用callback的handleMessage()方法,返回true,则消息处理结束;返回false。则会调用Handler对象的handleMssage()方法.
1 final MessageQueue mQueue; 2 final Looper mLooper; 3 final Callback mCallback; 4 public interface Callback { 5 public boolean handleMessage(Message msg); 6 } 7 private static void handleCallback(Message message) { 8 message.callback.run(); 9 }
1 /** 2 * Handle system messages here. 3 */ 4 public void dispatchMessage(Message msg) { 5 if (msg.callback != null) { 6 handleCallback(msg);//如上的方法 7 } else { 8 if (mCallback != null) { 9 if (mCallback.handleMessage(msg)) { 10 return; 11 } 12 } 13 handleMessage(msg); 14 } 15 }
3. public Handler(Looper looper). 这个构造函数表示创建一个Handler instance, 并将其绑定在looper所在的线程上. 此时looper不能为null. 此时一般也需要重写Hanler类的handleMessage()方法
4. public Handler(Looper looper, Callback callback). 可以结合2和3理解.
1 public final Message obtainMessage() 2 { 3 return Message.obtain(this); 4 } 5 private static Message getPostMessage(Runnable r) { 6 Message m = Message.obtain(); 7 m.callback = r; 8 return m; 9 } 10 public final boolean post(Runnable r) 11 { 12 return sendMessageDelayed(getPostMessage(r), 0); 13 } 14 public final boolean postAtTime(Runnable r, long uptimeMillis) 15 { 16 return sendMessageAtTime(getPostMessage(r), uptimeMillis); 17 } 18 public final boolean postDelayed(Runnable r, long delayMillis) 19 { 20 return sendMessageDelayed(getPostMessage(r), delayMillis); 21 } 22 public final boolean postAtFrontOfQueue(Runnable r) 23 { 24 return sendMessageAtFrontOfQueue(getPostMessage(r)); 25 } 26 public final boolean sendMessage(Message msg) 27 { 28 return sendMessageDelayed(msg, 0); 29 } 30 public final boolean sendEmptyMessage(int what) 31 { 32 return sendEmptyMessageDelayed(what, 0); 33 } 34 public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { 35 Message msg = Message.obtain(); 36 msg.what = what; 37 return sendMessageDelayed(msg, delayMillis); 38 } 39 public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) { 40 Message msg = Message.obtain(); 41 msg.what = what; 42 return sendMessageAtTime(msg, uptimeMillis); 43 } 44 //Handler所有post、send方法,最后调的都是此方法... 45 public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 46 MessageQueue queue = mQueue; 47 if (queue == null) { 48 RuntimeException e = new RuntimeException( 49 this + " sendMessageAtTime() called with no mQueue"); 50 Log.w("Looper", e.getMessage(), e); 51 return false; 52 } 53 return enqueueMessage(queue, msg, uptimeMillis); 54 } 55 //入队,其实调的是MessageQueue的入队方法 56 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { 57 msg.target = this; 58 if (mAsynchronous) { 59 msg.setAsynchronous(true); 60 } 61 return queue.enqueueMessage(msg, uptimeMillis); 62 } 63 //Handler的hasMessage、removeCallback是MessageQueue的方法 64 public final void removeMessages(int what) { 65 mQueue.removeMessages(this, what, null); 66 } 67 public final void removeCallbacksAndMessages(Object token) { 68 mQueue.removeCallbacksAndMessages(this, token); 69 } 70 public final boolean hasMessages(int what) { 71 return mQueue.hasMessages(this, what, null); 72 } 73 public final boolean hasCallbacks(Runnable r) { 74 return mQueue.hasMessages(this, r, null); 75 }
三,MessageQueue:
MessageQueue类用于表示消息队列. 队列中的每一个Message都有一个when字段, 这个字段用来决定Message应该何时出对处理. 消息队列中的每一个Message根据when字段的大小由小到大排列, 排在最前面的消息会首先得到处理, 因此可以说消息队列并不是一个严格的先进先出的队列.
方法主要有:
Message next() {...} void quit(boolean safe) { if (!mQuitAllowed) { throw new RuntimeException("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); } } //入队 boolean enqueueMessage(Message msg, long when) {...} //判断消息队列中是否包含某个消息 boolean hasMessages(Handler h, int what, Object object) {...} //将某个消息从队列中移出去,该消息被回收到消息池中了 void removeMessages(Handler h, int what, Object object) {...}
四:Message:
Message类用于表示消息. Message对象可以通过arg1, arg2, obj字段和setData()携带数据, 此外还具有很多字段. when字段决定Message应该何时出对处理, target字段用来表示将由哪个Handler对象处理这个消息, next字段表示在消息队列中排在这个Message之后的下一个Message, callback字段如果不为null表示这个Message包装了一个runnable对象, what字段表示code, 即这个消息具体是什么类型的消息. 每个what都在其handler的namespace中, 我们只需要确保将由同一个handler处理的消息的what属性不重复就可以.
1 public int what; 2 public int arg1; 3 public int arg2; 4 public Object obj; 5 public Messenger replyTo; 6 /** If set message is in use */ 7 static final int FLAG_IN_USE = 1 << 0; 8 /** If set message is asynchronous */ 9 static final int FLAG_ASYNCHRONOUS = 1 << 1; 10 /** Flags to clear in the copyFrom method */ 11 static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE; 12 int flags; 13 long when; 14 Bundle data; 15 Handler target; 16 Runnable callback; 17 // sometimes we store linked lists of these things 18 Message next; 19 20 private static final Object sPoolSync = new Object(); 21 private static Message sPool; 22 private static int sPoolSize = 0; 23 24 private static final int MAX_POOL_SIZE = 50;
1 public static Message obtain() { 2 synchronized (sPoolSync) { 3 if (sPool != null) { 4 Message m = sPool; 5 sPool = m.next; 6 m.next = null; 7 sPoolSize--; 8 return m; 9 } 10 } 11 return new Message(); 12 } 13 public static Message obtain(Message orig) { 14 Message m = obtain(); 15 m.what = orig.what; 16 m.arg1 = orig.arg1; 17 m.arg2 = orig.arg2; 18 m.obj = orig.obj; 19 m.replyTo = orig.replyTo; 20 if (orig.data != null) { 21 m.data = new Bundle(orig.data); 22 } 23 m.target = orig.target; 24 m.callback = orig.callback; 25 26 return m; 27 } 28 public static Message obtain(Handler h) { 29 Message m = obtain(); 30 m.target = h; 31 32 return m; 33 } 34 ...... 35 void clearForRecycle() { 36 flags = 0; 37 what = 0; 38 arg1 = 0; 39 arg2 = 0; 40 obj = null; 41 replyTo = null; 42 when = 0; 43 target = null; 44 callback = null; 45 data = null; 46 } 47 public void recycle() { 48 clearForRecycle(); 49 50 synchronized (sPoolSync) { 51 if (sPoolSize < MAX_POOL_SIZE) { 52 next = sPool; 53 sPool = this; 54 sPoolSize++; 55 } 56 } 57 } 58 public void copyFrom(Message o) 59 public long getWhen() 60 public void setTarget(Handler target) 61 public Handler getTarget() 62 public Runnable getCallback() 63 public Bundle getData() { 64 if (data == null) { 65 data = new Bundle(); 66 } 68 return data; 69 } 70 public Bundle peekData() { 71 return data; 72 } 73 public void setData(Bundle data) 74 public void sendToTarget()
将消息压入消息队列: Message对象的target字段关联了哪个线程的消息队列, 这个消息就会被压入哪个线程的消息队列中.
1. 调用Handler类中以send开头的方法可以将Message对象压入消息队列中, 调用Handler类中以post开头的方法可以将一个runnable对象包装在一个Message对象中, 然后再压入消息队列, 此时入队的Message其callback字段不为null, 值就是这个runnable对象. 调用Handler对象的这些方法入队的Message, 其target属性会被赋值为这个handler对象.
2. 调用Message对象的sendToTarget()方法可以将其本身压入与其target字段(即handler对象)所关联的消息队列中.
将未来得及处理的消息从消息队列中删除:
调用Handler对象中以remove开头的方法就可以.
从消息队列中取出消息并处理消息: 所有在消息队列中的消息, 都具有target字段. 消息是在target所关联的线程上被取出和处理的.
1 /** 2 * Handle system messages here. 3 */ 4 public void dispatchMessage(Message msg) { 5 if (msg.callback != null) { 6 handleCallback(msg); 7 } else { 8 if (mCallback != null) { 9 if (mCallback.handleMessage(msg)) { 10 return; 11 } 12 } 13 handleMessage(msg); 14 } 15 }
1. 如果取出的Message对象的callback字段不为null, 那么就调用callback字段的run()方法(callback字段的类型是runnable). 注意此时并不开启一个新的线程运行run()方法, 而是直接在handler对象(即Message的target字段)所关联的线程上运行.
2. 如果取出的Message对象的callback字段为null, 且Handler对象中的callback字段也为null, 那么这个消息将由Handler对象的handleMessage(msg)方法处理. 注意Message对象的callback字段是Runnable类型的而Handler对象的callback字段是Callback类型的, Handler对象的callback字段是在创建Handler instance的时候指定的, 如果没有指定则这个字段为null, 详见Handler类的四个构造方法.
3. 如果取出的Message对象的callback字段为null, 且Handler对象中的callback字段不为null, 那么这个消息将由Handler对象中的callback字段的handleMessage方法优先处理.
线程间通信: 有了以上的叙述, 线程间的通信也就好理解了. 假如一个handler关联了A线程上的消息队列, 那么我们可以在B线程上调用handler的相关方法向A线程上的消息队列压入一个Message, 这个Message将在A线程上得到处理.
转载:android笔记--android中的多线程--Handler, Looper, MessageQueue, Message类,,5-wow.com
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。