android 线程 handler loop,Android线程通信模型(Handler and Loop)

66b52468c121889b900d4956032f1009.png

8种机械键盘轴体对比

本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?

今天在看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

22class extends Activity{

Handler mHandler;

proteced void onCreate(){

mHandler = new Hanlder() {

public void handleMessage(Message msg){

//stopSelf(msg.arg1);

}

}

}

new Thread(new Runable(){

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);

}

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

注意: 表头保存在MessageQueue类mPtr中

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

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

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

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

###取与送

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

Looper与线程是一一对应的,也就是说,每条线程有且只能有一个Looper来管理信息。

这种一一对应的关系是由Looper类中的一个静态变量mThreadLocal实现的。

该变量的定义:1

2// sThreadLocal.get() will return null unless you've called prepare().

static final ThreadLocal sThreadLocal = new ThreadLocal();

再看Looper.prepare()方法:1

2

3

4

5

6private 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{

...

}

该类由一个哈希表来实现,由每个线程本身作为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

34public 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); }

public void handleMessage(Message msg){

onHandleIntent((Intent)msg.obj);

stopSelf(msg.arg1);

}

}

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()方法都在子线程中进行。也就实现了异步任务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值