今天在看IntentService的时候,发现自己对于线程通信模型并不十分了解。于是把Handler and Loop模型重新学了一遍。总结一下加深记忆。

##模型分析
首先我们来考虑一下线程之间需要哪些通信,我们需要实现哪些功能。

  • 回调功能(处理信息的能力)
    • 在某某事件结束后,执行一个方法。
    • 延迟一段时间后,执行一个方法。
  • 发现信息的能力
  • 把信息送往正确目的地的能力

##主要用法和实现

###用法
主要用法有两种,一种为view.postXXX()系;另一种为直接调用Handler产生。

调用handler时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class myActivity extends Activity{
Handler mHandler;

@Override
proteced void onCreate(){
mHandler = new Hanlder() {
@Override
public void handleMessage(Message msg) {
//onHandleIntent((Intent)msg.obj);
//stopSelf(msg.arg1);
}
}
}

new Thread(new Runable(){
@Override
public void run(){
Message msg = mHandler.obtainMessage();//这里是Message池
mHandler.sendMessage(msg);//内部调用,sendMessageDelayed(msg,0);
}
})
}

###原理
在某一线程中调用mHandler.sendMessage(Message msg)方法,并且由同一个mHanlder引用在另一个特定线程中处理

那么问题来了:
要传递的信息从哪来?
是什么?
准备到哪里去?

####从哪来?

  • mHandler.obtainMessage(){ return Message.obtainMessage(this)}
  • 调用Message类的静态方法Message.obtainMessage(Handler handler)
    • 此处的handler将被传给msg.target变量决定到哪里去问题
  • Message的静态方法有统一调用obtain()方法,从global message pool 中获取一个Message实例
    • globle message pool的实现值得一提
    • 这个pool使用链表来实现,靠msg.next()链接起来。在类中只保存一个引用。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
          /**
      * Return a new Message instance from the global pool. Allows us to
      * avoid allocating new objects in many cases.
      */
      public static Message obtain() {
      synchronized (sPoolSync) {
      if (sPool != null) {
      Message m = sPool;
      sPool = m.next;
      m.next = null;
      m.flags = 0; // clear in-use flag
      sPoolSize--;
      return m;
      }
      }
      return new Message();
      }
      ```
      ###是什么?

      Handler只是传递一些简单的信息
      - `int what` 通常是用来在`handleMessage`时做判断信息是由哪个地方来的
      - `int arg1` 额外变量
      - `int arg2` 额外变量
      - `Object object` 可以传递一些Runable方法

      ###到哪去?
      `sendMessage`会把该信息打上`target`标签,然后入队(这里传入`MessageQueue`,还不是`Loop`)`Handler` 中的`enqueueMessage`会调用`MessageQueue`的`enqueueMessage`
      ```java
      //class Handler
      private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
      msg.target = this;
      if (mAsynchronous) {
      msg.setAsynchronous(true);
      }// lock to make it asynchronous
      return queue.enqueueMessge(msg, uptimeMillis);
      }

此处所入的队列Queuemsg.next的连接起来的链表,队列的顺序受时间的影响,即时间靠前的排在前面。特殊情况:如果比表头还靠前,需要更新表头,此时要多加一句needWake = mBlocked;,以便后面nativeWake(mPtr)唤醒。

注意: 表头保存在MessageQueuemPtr

Handler中的这个MessageQueue实例由Looper实例获得,Looper实例由Looper.myLooper()静态方法获得(或者初始化时传入的Looper),这一动作在构造函数中进行。

所以,信息到底被传递给哪个线程,关键在于Handler在哪里初始化,或者初始化时传入的Looper参数

想获取特定的Looper对象,只要用thread.getLooper就好

LooperMessageQueue是绑定的,一对一的聚合关系。一个线程内,Looper的初始化函数会调用MessageQueue的初始化函数。

###取与送
如何把特定的Message交给特定的Handler?我们需要把MessageMessageQueue中取出来,并且送到特定的Handler中,这个工作由Looper负责。

Looper与线程是一一对应的,也就是说,每条线程有且只能有一个Looper来管理信息。
这种一一对应的关系是由Looper类中的一个静态变量mThreadLocal实现的。

该变量的定义:

1
2
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

再看Looper.prepare()方法:

1
2
3
4
5
6
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.myLooper()方法:

1
2
3
4
5
6
7
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/

public static Looper myLooper() {
return sThreadLocal.get();
}

ThreadLocal是一种特殊的多线程变量结构,在Python等语言中也有不同程度的支持

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* Implements a thread-local storage, that is, a variable for which each thread
* has its own value. All threads share the same {@code ThreadLocal} object,
* but each sees a different value when accessing it, and changes made by one
* thread do not affect the other threads. The implementation supports
* {@code null} values.
*
* @see java.lang.Thread
* @author Bob Lee
*/

public class ThreadLocal<T> {
...
}

该类由一个哈希表来实现,由每个线程本身作为key,取出value

Looper类用死循环来取信息,该动作在Looper.loop()方法实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/

public static void loop() {
final Looper me = myLooper();
...
final MessageQueue queue = me.mQueue;
...
for (;;) {
Message msg = queue.next(); // might block(这里面也有一个死循环)
...
msg.target.dispatchMessage(msg);
...
msg.recycleUnchecked();
}
}

注意: Looper(以及其包含的MessageQueue)都与线程关联,后者与前者同时初始化。二者中皆包含死循环,来不断的读取信息。

再注:在主线程中肯定不能有死循环,该怎么办呢?Looper中专门为mainThread留了一个变量sMainLooper,其实还是由myLoop()方法赋值,也就是说还是保存在mThreadLocal中。
(傻逼了)
主线程这里也是这个死循环,不停的更新UI,读缓存绘制到屏幕上。其它GUI正这种死循环也非常常见,典型的控制反转

##应用范例:IntentService

IntentService 可以在另一个线程中异步的执行一个任务。那么它是怎么在不同的线程中通讯的呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public abstract class IntentService extends Service{
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;

private final class ServiceHandler extends Handler{
public Service(Looper looper){ super(looper); }

@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}

@Override
public void onCreate(){
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();

mServiceLooper = thread.getLooper();
mServiceHandler = new mServiceHandler(mServiceLooper);
}

@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mService.sendMessage(msg);
}

onStartCommand(){ onStart(); }
}

其实说白了,就是Handler在主线程中把信息发给子线程,并且在子线程中执行Handler的handleMessage方法。
这是因为,Handler的初始化时传入了特定的Looper,也就与子线程绑定了。Looper的死循环在子线程中进行,自然dispatchMessage()以及handleMessaged()方法都在子线程中进行。也就实现了异步任务。