Andrid中的Looper,Handler,Message研究

  1. Looper内部有一个消息队列,loop()方法调用后线程开始不断从队列中取出消息执行

  2. Looper使一个线程变成Looper线程。

Handler扮演了向MessageQueue上添加消息和处理消息的角色(只处理由自己发出的消息),即通知MessageQueue它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的。Handler创建时会关联一个Looper,默认的构造方法将关联当前线程的Looper,当然也可以自己设置的。默认的构造方法:

public class handler {

final MessageQueue mQueue;  // 关联的MQ

final Looper mLooper;  // 关联的looper

final Callback mCallback;

// 其他属性

public Handler() {

// 没看懂,直接略过,,,

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

}

}

// 默认将关联当前线程的looper

mLooper = Looper.myLooper();

// looper不能为空,即该默认的构造方法只能在looper线程中使用

if (mLooper == null) {

throw new RuntimeException(

“Can’t create handler inside thread that has not called Looper.prepare()”);

}

// 重要!!!直接把关联looper的MessageQueue作为自己的MessageQueue,因此它的消息将发送到关联looper的 MessageQueue上

mQueue = mLooper.mQueue;

mCallback = null;

}

// 其他方法

}

下面就可以为之前的LooperThread类加入Handler:

public class LooperThread extends Thread {

private Handler handler1;

private Handler handler2;

@Override

public void run() {

// 将当前线程初始化为Looper线程

Looper.prepare();

// 实例化两个handler

handler1 = new Handler();

handler2 = new Handler();

// 开始循环处理消息队列

Looper.loop();

}

}

加入handler后的效果如下图:

一个线程可以有多个Handler,但是只能有一个Looper!

Handler发送消息

创建Handler后可以使用 [post(Runnable)]( ),   [postAtTime(Runnable, long)]( ),  [postDelayed(Runnable, long)]( ),  [sendEmptyMessage(int)]( ),   [sendMessage(Message)]( )[sendMessageAtTime(Message, long)]( ) 和 [sendMessageDelayed(Message, long)]( )这些方法向MessageQueue上发送消息。光看这些API你可能会觉得handler能发两种消息,一种是Runnable对象,一种是message对象,其实发出的Runnable对象最后也被封装成message对象了,见源码:

// 此方法用于向关联的MQ上发送Runnable对象,它的run方法将在handler关联的looper线程中执行

public final boolean post(Runnable r)

{

// 注意getPostMessage®将runnable封装成message

return  sendMessageDelayed(getPostMessage®, 0);

}

private final Message getPostMessage(Runnable r) {

Message m = Message.obtain();  //得到空的message

m.callback = r;  //将runnable设为message的callback,

return m;

}

public boolean sendMessageAtTime(Message msg, long uptimeMillis)

{

boolean sent = false;

MessageQueue queue = mQueue;

if (queue != null) {

msg.target = this;  // message的target必须设为该handler!

sent = queue.enqueueMessage(msg, uptimeMillis);

}

else {

RuntimeException e = new RuntimeException(

this + " sendMessageAtTime() called with no mQueue");

Log.w(“Looper”, e.getMessage(), e);

}

return sent;

}

通过Handler发出的message的特点:

  1. message.target为该handler对象,这确保了looper执行到该message时能找到处理它的handler,即loop()方法中的关键代码

msg.target.dispatchMessage(msg);

  1. post发出的message,其callback为Runnable对象

Handler处理消息

消息的处理是通过核心方法dispatchMessage(Message msg)与handleMessage(Message msg)方法完成的,见源码

// 处理消息,该方法由looper调用

public void dispatchMessage(Message msg) {

if (msg.callback != null) {

// 如果message设置了callback,即runnable消息,处理callback!

handleCallback(msg);

} else {

// 如果handler本身设置了callback,则执行callback

if (mCallback != null) {

/* 这种方法允许让activity等来实现Handler.Callback接口,避免了自己编写handler重写handleMessage方法。见http://alex-yang-xiansoftware-com.iteye.com/blog/850865 */

if (mCallback.handleMessage(msg)) {

return;

}

}

// 如果message没有callback,则调用handler的钩子方法handleMessage

handleMessage(msg);

}

}

// 处理runnable消息

private final void handleCallback(Message message) {

message.callback.run();  //直接调用run方法!

}

// 由子类实现的钩子方法

public void handleMessage(Message msg) {

}

可以看到,除了handleMessage(Message msg)和Runnable对象的run方法由开发者实现外(实现具体逻辑),handler的内部工作机制对开发者是透明的。

Handler的用处

Handler拥有下面两个重要的特点:

1.handler可以在任意线程发送消息,这些消息会被添加到关联的MessageQueue上。

2.handler是在它关联的looper线程中处理消息的。

Android的主线程也是一个Looper线程,在其中创建的handler默认将关联主线程MessageQueue。因此,利用handler的常用场景就是在activity中创建handler并将其引用传递给你的线程,你的线程执行完任务后使用handler发送消息通知Activity更新UI。(过程如图)

下面给出sample代码,仅供参考:

public class TestDriverActivity extends Activity {

private TextView textview;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

textview = (TextView) findViewById(R.id.textview);

// 创建并启动工作线程

Thread workerThread = new Thread(new SampleTask(new MyHandler()));

workerThread.start();

}

public void appendText(String msg) {

textview.setText(textview.getText() + “\n” + msg);

}

class MyHandler extends Handler {

@Override

public void handleMessage(Message msg) {

String result = msg.getData().getString(“message”);

// 更新UI

appendText(result);

}

}

}

public class SampleTask implements Runnable {

private static final String TAG = SampleTask.class.getSimpleName();

Handler handler;

public SampleTask(Handler handler) {

super();

this.handler = handler;

}

@Override

public void run() {

try {  // 模拟执行某项任务,下载等

Thread.sleep(5000);

// 任务完成后通知activity更新UI

Message msg = prepareMessage(“task completed!”);

// message将被添加到主线程的MQ中

handler.sendMessage(msg);

} catch (InterruptedException e) {

Log.d(TAG, “interrupted!”);

}

}

private Message prepareMessage(String str) {

Message result = handler.obtainMessage();

Bundle data = new Bundle();

data.putString(“message”, str);

result.setData(data);

return result;

}

}

当然,handler能做的远远不仅如此,由于它能post Runnable对象,它还能与Looper配合实现经典的Pipeline Thread(流水线线程)模式。

整个消息处理机制中,message封装了任务携带的信息和处理该任务的handler。message的用法比较简单,有几点需要注意:

  1. 尽管Message有public的默认构造方法,但是应该通过Message.obtain()来从消息池中获得空消息对象,以节省资源。

  2. 如果你的message只需要携带简单的int信息,请优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存;

  3. 擅用message.what来标识信息,以便用不同方式处理message。

=============================================================================

下面是转载的一篇介绍消息机制的整个流程:

每一个handler其实都绑定了一个线程(Thread)和消息队列(MessageQueue),消息队列中存放的是一堆待处理的消息,Looper通过一个loop方法不断从消息队列中获取到消息(Message)(先进先出的方式),执行消息队列的出队方法,然后该消息会通过自身绑定target(其实是一个handler对象),分发处理携带的消息(dispatchMessage)。消息处理完成之后,该消息会被回收(recycle),回到消息池中。而handler的sendMessage方法其实会调用消息队列的入队方法(enqueueMessage),将包装好的消息加到消息队列中,再次循环。这其实就是android下的消息机制。本文将试图从源码的角度分析这一流程。

首先介绍Handler,Looper,MessageQueue,Message四个概念:

Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。

Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。

MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。

Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。

-------------------------------------

注:一个线程默认是没有消息队列的,如果我们要使用handler,必须指定Looper,Looper内部会创建MessageQueue。其中比较特殊的是UI线程提供了默认的Looper,我们不需要为其指定Looper。如果是其他线程,我们必须显式调用Looper.prepare和Looper.loop方法创建Looper并使其运转起来。

[java]  view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. class LooperThread extends Thread {

  2. public Handler mHandler;

  3. public void run() {

  4. Looper.prepare();

  5. mHandler = new Handler() {

  6. public void handleMessage(Message msg) {

  7. // process incoming messages here

  8. }

  9. };

  10. Looper.loop();

  11. }

  12. }

----------------------

下面我们将从Handler调用post或者sendMessage方法开始,跟踪源码进行分析,从而了解整个消息机制的运作过程。

我们在调用sendMessage方法时,需要提供一个Message对象,Message对象用于封装一个消息,我们通过查看源码观察下该类的成员变量:

[java]  view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. public final class Message implements Parcelable {

  2. public int what;

  3. public int arg1;

  4. public int arg2;

  5. public Object obj;

  6. long when;

  7. Bundle data;

  8. Handler target;

  9. Runnable callback;

  10. // sometimes we store linked lists of these things

  11. Message next;

  12. private static final Object sPoolSync = new Object();

  13. private static Message sPool;

  14. private static int sPoolSize = 0;

  15. private static final int MAX_POOL_SIZE = 50;

  16. … …

四个公有变量相信大家都不陌生,这里就不再介绍了。我们看到Message还有一些成员变量,比如说target,这是个Handler对象,target可以通过set方法或者obtainMessage等方式设置,一旦设置了T arget,这个message就被绑定到一个具体的handler上了,然后message内部即可调用sendToTarget方法将message交给绑定的Handler进行处理,内部也调用的是sendMessage:

[java]  view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. public void sendToTarget() {

  2. target.sendMessage(this);

  3. }

callback对象是Runnable类型的,这个也比较好理解,因为我们的handler除了可以处理message之外,还可以通过Post方式将Runnable加入队列,进行处理。这个执行流程我们放在后面介绍。

我们还看到了一个Message类型的next,这个成员变量持有了下一个Message的引用,众多Message就是通过这种方式形成一个链表,也就是所谓的消息池,我们看到消息池默认的容量为0(sPoolSize ​),池的最大容量为50。Message的obtain及其重载形式每次调用都会从池中取出一个Message,如果不存在就new一个出来。

[java]  view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  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. }

recycle方法又会将用完的Message回收:

[java]  view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. public void recycle() {

  2. clearForRecycle();

  3. synchronized (sPoolSync) {

  4. if (sPoolSize < MAX_POOL_SIZE) {

  5. next = sPool;

  6. sPool = this;

  7. sPoolSize++;

  8. }

  9. }

  10. }

现在假设我们的消息已经封装好了,下一步必然就是发送消息了。通过调用Handler的sendMessage方法可以发送一条消息。

[java]  view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. public final boolean sendMessage(Message msg)

  2. {

  3. return sendMessageDelayed(msg, 0);

  4. }

sendMessage方法调用了sendMessageDelayed方法,延时设为0,继续跟源码:

[java]  view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. public final boolean sendMessageDelayed(Message msg, long delayMillis)

  2. {

  3. if (delayMillis < 0) {

  4. delayMillis = 0;

  5. }

  6. return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);

  7. }

先是判断延时参数的合法性,然后调用了sendMessageAtTime方法,果断跟进去看看:

[java]  view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. public boolean sendMessageAtTime(Message msg, long uptimeMillis) {

  2. MessageQueue queue = mQueue;

  3. if (queue == null) {

  4. RuntimeException e = new RuntimeException(

  5. this + " sendMessageAtTime() called with no mQueue");

  6. Log.w(“Looper”, e.getMessage(), e);

  7. return false;

  8. }

  9. return enqueueMessage(queue, msg, uptimeMillis);

  10. }

跟到这一步,我们发现了一个没见过的变量,mQueue,查看该变量定义:

[java]  view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. final MessageQueue mQueue;

  2. final Looper mLooper;

  3. final Callback mCallback;

原来mQueue是一个消息队列,那么消息队列是怎么初始化的呢?我们找到Handler的一个构造器:

[java]  view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. public Handler(Callback callback, boolean async) {

  2. … …

  3. mLooper = Looper.myLooper();

  4. if (mLooper == null) {

  5. throw new RuntimeException(

  6. “Can’t create handler inside thread that has not called Looper.prepare()”);

  7. }

  8. mQueue = mLooper.mQueue;

  9. mCallback = callback;

  10. mAsynchronous = async;

  11. }

原来mQueue是Looper的成员变量,这个构造器通过Looper.myLooper方法返回了一个与当前线程绑定的Looper对象,进而获取到与Looper绑定的MessageQueue。这是Looper的构造器:

[java]  view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. private Looper(boolean quitAllowed) {

  2. mQueue = new MessageQueue(quitAllowed);

  3. mRun = true;

  4. mThread = Thread.currentThread();

  5. }

既然我们知道了mQueue的来历,那么我们继续随着上面的思路跟进吧!刚才我们通过跟踪sendMessage源码发现内部最终调用了sendMessageAtTime方法,而这个方法又调用了enqueueMessage方法,从名字上能看出,该方法是将消息出队的方法,等等。。消息出队不应该是消息队列的方法么?怎么Handler也有?别急,我们查看enqueueMessage方法源码:

[java]  view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

  2. msg.target = this;//我觉得这里应该判断target是否已经存在,如果存在就没必要设置了吧?

  3. if (mAsynchronous) {

  4. msg.setAsynchronous(true);

  5. }

  6. return queue.enqueueMessage(msg, uptimeMillis);

  7. }

这下原因显而易见了,handler的入队方法其实调用的是消息队列的入队方法。

下面我们来到MessageQueue源码,查找到enqueueMessage方法:

[java]  view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. final boolean enqueueMessage(Message msg, long when) {

  2. if (msg.isInUse()) {//消息是否正在使用

  3. throw new AndroidRuntimeException(msg + " This message is already in use.");

  4. }

  5. if (msg.target == null) {//是否绑定handler

  6. throw new AndroidRuntimeException(“Message must have a target.”);

  7. }

  8. boolean needWake;

  9. synchronized (this) {

  10. if (mQuiting) {

  11. RuntimeException e = new RuntimeException(

  12. msg.target + " sending message to a Handler on a dead thread");

  13. Log.w(“MessageQueue”, e.getMessage(), e);

  14. return false;

  15. }

  16. msg.when = when;

  17. Message p = mMessages;

  18. if (p == null || when == 0 || when < p.when) {

  19. // New head, wake up the event queue if blocked.

  20. msg.next = p;

  21. mMessages = msg;

  22. needWake = mBlocked;

  23. } else {

  24. needWake = mBlocked && p.target == null && msg.isAsynchronous();

  25. Message prev;

  26. for (;😉 {

  27. prev = p;

  28. p = p.next;

  29. if (p == null || when < p.when) {

  30. break;

  31. }

  32. if (needWake && p.isAsynchronous()) {

  33. needWake = false;

  34. }

  35. }

  36. msg.next = p; // invariant: p == prev.next

  37. prev.next = msg;

  38. }

  39. }

  40. if (needWake) {

  41. nativeWake(mPtr);

  42. }

  43. return true;

  44. }

跟到这里终于明白了,这个MessageQueue是通过mMessages变量来记录当前待处理的消息,通过Mesage的发送的时间进行排序(调用msg.next)。

到这里整个入队操作就分析完了,通过这一系列的操作,handler将消息发送给了消息队列。到这里消息只是不断被加到消息队列中,我们并没有取出消息。那么Handler的handMessage方法又是怎么被调用的呢??带着这个疑问,我们来研究下Looper,消息泵,既然是泵,之所以叫泵,是因为它可以从消息队列中取出消息!之前说过,要在线程中使用Handler,必须调用Looper的prepare方法创建Looper对象,然后调用loop方法让消息队列运作起来。那么,这个loop方法是关键,我们查看该方法源码:

[java]  view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. public static void loop() {

  2. final Looper me = myLooper();

  3. if (me == null) {

  4. throw new RuntimeException(“No Looper; Looper.prepare() wasn’t called on this thread.”);

  5. }

  6. final MessageQueue queue = me.mQueue;

  7. Binder.clearCallingIdentity();

  8. … …

  9. for (;😉 {

  10. Message msg = queue.next(); // might block

  11. if (msg == null) {

  12. return;

  13. }

  14. … …

  15. msg.target.dispatchMessage(msg);

  16. … …

  17. msg.recycle();

  18. }

  19. }

这个方法首先获取到消息队列的一个引用,然后在一个死循环中反复调用消息队列的出队方法(next)获取到下一个待处理的消息的引用,然后调用该消息所绑定的handler的dispatchMessage方法分发消息,最后将该消息回收。我们跟踪Handler的 dispatchMessage方法:

[java]  view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. public void dispatchMessage(Message msg) {

  2. if (msg.callback != null) {

  3. handleCallback(msg);

  4. } else {

  5. if (mCallback != null) {

  6. if (mCallback.handleMessage(msg)) {

  7. return;

  8. }

  9. }

  10. handleMessage(msg);

  11. }

  12. }

既然是分发消息的方法,那必然会根据消息的类型做出不同的处理,这个方法正是根据Message对象是否携带了callback,如果携带了callback那就执行handleCallback方法,callback之前分析Message类的时候已经知道是一个Runnable的对象了。下面我们看看handleCallback是什么实现的:

[java]  view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. private static void handleCallback(Message message) {

  2. message.callback.run();

  3. }

一目了然!直接调用callback的run方法。之前我们用post方法发送消息时携带的Runnable对象在Handle内部被转化为一个Message:

[java]  view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. private static Message getPostMessage(Runnable r) {

  2. Message m = Message.obtain();

  3. m.callback = r;

  4. return m;

  5. }

post方法中的Runnable对象被转成Message之后也会进入消息队列,最终被Looper抽取出来,让handler进行处理。

普通的sendMessage方法是不携带callback的,这时便进入dispatchMessage方法的else分支,进入else分支后,首先判断handler是否有一个CallBack的回调函数,如果有,直接调用回调函数的handMessage方法,这个CallBack是一个接口,它的好处是我们不用在继承Handler重写handleMessage方法了,CallBack接口定义:

[java]  view plain copy 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. public interface Callback {

  2. public boolean handleMessage(Message msg);

  3. }

如果没有实现回调接口,那么便调用Handle的handMessage方法处理消息。

-------------------------------------------

通过上面的一番分析,我们终于明白了android下的消息机制的运作原理了,为了更形象的说明整个流程,这里贴一张示意图:

==================================================================================================================

下面源码分析篇也是转载:

Android中消息传递模块差不多看了好几次,虽然每次看的方式都差不多但是还是发觉的到每次看了之后,理解的更清晰一点。

关于这个模块的文章数不胜数,但是最重要的还是自己动手理解一遍更好。

会牵扯到的几个类: Handler.java  , Looper.java , MessageQueue.java , Message.java

源代码路径:

xxx/frameworks/base/core/java/android/os  看的过程中你会发现高版本和低版本系统代码有些地方比较大的差距。从中我们可以分析为什么要做这样的修改,这样的修改的优点。

先看看在Looper类的注释中的一段代码。

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. This is a typical example of the implementation of a Looper thread,

  2. * using the separation of {@link #prepare} and {@link #loop} to create an

  3. * initial Handler to communicate with the Looper.

  4. *

  5. *  class LooperThread extends Thread {

  6. *      public Handler mHandler;

  7. *

  8. *      public void run() {

  9. *          Looper.prepare();

  10. *

  11. *          mHandler = new Handler() {

  12. *              public void handleMessage(Message msg) {

  13. *                  // process incoming messages here

  14. *              }

  15. *          };

  16. *

  17. *          Looper.loop();

  18. *      }

  19. *  }

  20. */

Looper.java

这里Looper.prepare() ; 和 Looper.loop() ;这两个方法做了什么?

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. /** Initialize the current thread as a looper.

  2. * This gives you a chance to create handlers that then reference

  3. * this looper, before actually starting the loop. Be sure to call

  4. * {@link #loop()} after calling this method, and end it by calling

  5. * {@link #quit()}.

  6. */

  7. public static void prepare() {

  8. prepare(true);

  9. }

  10. private static void prepare(boolean quitAllowed) {

  11. if (sThreadLocal.get() != null) {

  12. throw new RuntimeException(“Only one Looper may be created per thread”);

  13. }

  14. //如果是main线程,则不允许退出。

  15. sThreadLocal.set(new Looper(quitAllowed));

  16. }

当我们手动调用Looper.prepare()方法的时候,内部调用prepare(boolean quitAllowd)方法,参数quitAllowd表示是否允许退出。

Android中刷新UI都在main线程中完成的,因此prepare(false)的时候是在启动main线程的时候会用到。我们可以看到Looper中另外一个方法:

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. /**

  2. * Initialize the current thread as a looper, marking it as an

  3. * application’s main looper. The main looper for your application

  4. * is created by the Android environment, so you should never need

  5. * to call this function yourself.  See also: {@link #prepare()}

  6. */

  7. //查询该方法有两个位置用到。一是在SystemServer中,另外一个是在ActivityThread中。

  8. //vi services/java/com/android/server/SystemServer.java +97

  9. //vi core/java/android/app/ActivityThread.java +4985

  10. public static void prepareMainLooper() {

  11. prepare(false);

  12. synchronized (Looper.class) {

  13. if (sMainLooper != null) {

  14. throw new IllegalStateException(“The main Looper has already been prepared.”);

  15. }

  16. sMainLooper = myLooper();

  17. }

  18. }

Looper.prepare()方法中,完成了Looper的创建并将起存储至ThreadLocal变量中。

从上面代码中可以看到:

sThreadLocal.set(new Looper(quitAllowed));

因此下面我们跟踪一下Looper的构造方法、

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. private Looper(boolean quitAllowed) {

  2. mQueue = new MessageQueue(quitAllowed);

  3. mThread = Thread.currentThread();

  4. }

构造方法中看到创建了MessageQueue和得到当前线程的Thread对象。这里的MessageQueue是后面消息循环以及消息的传递重要的部分。

因此,从这里我们可以看出,Looper中持有:MessageQueue,currentThread,ThreadLocal。

接下来看下,Looper.loop()方法。注:方法中部分打印Log相关的代码此处已经删除。

Android Source Code 4.4

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. /**

  2. * Run the message queue in this thread. Be sure to call

  3. * {@link #quit()} to end the loop.

  4. */

  5. public static void loop() {

  6. //获取当前looper

  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. //Looper中持有消息队列。获取当前Looper的消息队列

  12. final MessageQueue queue = me.mQueue;

  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. //无限循环

  17. for (;😉 {

  18. //取出消息

  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. //++++++++++++++++++++这一句是重点,获取到消息后会回调到Handler方法中的handleMessage()方法中。

  25. //在低版本的系统代码中,会判断msg.target==null,这里去掉了。

  26. msg.target.dispatchMessage(msg);

  27. //回收当前消息对象所占内存

  28. msg.recycle();

  29. }

  30. }

对比Android Source Code 2.3

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. /**

  2. *  Run the message queue in this thread. Be sure to call

  3. * {@link #quit()} to end the loop.

  4. */

  5. public static final void loop() {

  6. Looper me = myLooper();

  7. MessageQueue queue = me.mQueue;

  8. // Make sure the identity of this thread is that of the local process,

  9. // and keep track of what that identity token actually is.

  10. Binder.clearCallingIdentity();

  11. while (true) {

  12. Message msg = queue.next(); // might block

  13. //if (!me.mRun) {

  14. //    break;

  15. //}

  16. if (msg != null) {

  17. if (msg.target == null) {

  18. // No target is a magic identifier for the quit message.

  19. return;

  20. }

  21. msg.recycle();

  22. }

  23. }

  24. }

从上面的对比可以看出4.4的代码明显比2.3的代码变化。

1.while(true){…}循环修改为for(,){…} 貌似这里没啥区别

2.Looper me = myLooper();获取到当前looper,4.4加了非空判断。因为在调用了Looper.prepare()后才会有Looper产生。可以看上面的分析。

3.去掉了message中target判断空的if语句。

分析为何这里去掉。

先看当我们调用Handler的handler.obtainMessage() ;

------->in Handler.java

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. /**

  2. * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than

  3. * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).

  4. *  If you don’t want that facility, just call Message.obtain() instead.

  5. */

  6. public final Message obtainMessage()

  7. {

  8. return Message.obtain(this);

  9. }

此时Handler会去调用Message的obtain方法,该方法将this作为参数传递进去,也就是将当前的Handler对象引用传递进去。

----->in Message.java

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. /**

  2. * Same as {@link #obtain()}, but sets the values of the targetwhat,

  3. arg1arg2, and obj members.

  4. *

  5. * @param h  The target value to set.

  6. * @param what  The what value to set.

  7. * @param arg1  The arg1 value to set.

  8. * @param arg2  The arg2 value to set.

  9. * @param obj  The obj value to set.

  10. * @return  A Message object from the global pool.

  11. */

  12. public static Message obtain(Handler h, int what,

  13. int arg1, int arg2, Object obj) {

  14. Message m = obtain();

  15. m.target = h;

  16. m.what = what;

  17. m.arg1 = arg1;

  18. m.arg2 = arg2;

  19. m.obj = obj;

  20. return m;

  21. }

而且再看看当msg被添加到队列的时候调用的方法

boolean enqueueMessage(Message msg,long when)其中首先就是判断将被添加的target是否为null,否则跑出nullPointerException。

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. boolean enqueueMessage(Message msg, long when) {

  2. if (msg.target == null) {

  3. throw new IllegalArgumentException(“Message must have a target.”);

  4. } …

因此msg.target不可能为空,也就没有判断的必要。

4.将可能的情况都先放到前面过滤,减少了if语句的嵌套。是代码更加清晰明了。

刚好手上也有5.0的代码,干脆也贴上来看下。

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. /**

  2. * Run the message queue in this thread. Be sure to call

  3. * {@link #quit()} to end the loop.

  4. */

  5. public static void loop() {

  6. final Looper me = myLooper();

  7. if (me == null) {

  8. throw new RuntimeException(“No Looper; Looper.prepare() wasn’t called on this thread.”);

  9. }

  10. final MessageQueue queue = me.mQueue;

  11. // Make sure the identity of this thread is that of the local process,

  12. // and keep track of what that identity token actually is.

  13. Binder.clearCallingIdentity();

  14. for (;😉 {

  15. Message msg = queue.next(); // might block

  16. if (msg == null) {

  17. // No message indicates that the message queue is quitting.

  18. return;

  19. }

  20. msg.target.dispatchMessage(msg);

  21. msg.recycleUnchecked();

  22. }

  23. }

对比可以发现5.0的和4.4的差不多只是msg的回收方法变了。编程recycleUnchecked()。

因此这里需要对比4.4的Message和5.0的Message。这里简单看下不同点。

-------->5.0 Message.java  添加了当前msg是否正在使用的校验。当然,这里添加了,说明很多地方也会相应的增加判断校验,差别就出来了。

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. /**

  2. * Return a Message instance to the global pool.

  3. * You MUST NOT touch the Message after calling this function because it has

  4. * effectively been freed.  It is an error to recycle a message that is currently

  5. * enqueued or that is in the process of being delivered to a Handler.

  6. */

  7. public void recycle() {

  8. if (isInUse()) {

  9. if (gCheckRecycle) {

  10. throw new IllegalStateException("This message cannot be recycled because it "

  11. + “is still in use.”);

  12. }

  13. return;

  14. }

  15. recycleUnchecked();

  16. }

Handler.java

平时我们在代码中是直接创建Handler的派生类或者创建匿名内部类。类似于这样

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. private static class MyHandler extends Handler{

  2. @Override

  3. public void handleMessage(Message msg) {

  4. // TODO Auto-generated method stub

  5. super.handleMessage(msg);

  6. }

  7. }

或者

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. Handler mHandler = new Handler(){

  2. @Override

  3. public void handleMessage(Message msg) {

  4. // TODO Auto-generated method stub

  5. super.handleMessage(msg);

  6. }

  7. } ;

————————————————————————————————————————————————————————

这里我们最先看看Handler的创建。构造方法中最终会调用到这个:

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. public Handler(Callback callback, boolean async) {

  2. if (FIND_POTENTIAL_LEAKS) {

  3. //这里判断派生出来的Handler子类是否为static,不产生致命危机,但是可能会导致内存泄露。

  4. final Class<? extends Handler> klass = getClass();

  5. if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&

  6. (klass.getModifiers() & Modifier.STATIC) == 0) {

  7. Log.w(TAG, "The following Handler class should be static or leaks might occur: " +

  8. klass.getCanonicalName());

  9. }

  10. }

  11. //取出与该Handler相连接的Looper,该Looper与Thread相连接。

  12. mLooper = Looper.myLooper();

  13. if (mLooper == null) {

  14. throw new RuntimeException(

  15. “Can’t create handler inside thread that has not called Looper.prepare()”);

  16. }

  17. //在Handler里面也持有MessageQueue,这个MessageQueue和Looper的MessageQueue相同

  18. //所以在Handler的其他处理方法中。removeMessages,hasMessages,enqueueMessage这些操作都相当于操作Looper中的MessageQueue

  19. mQueue = mLooper.mQueue;

  20. mCallback = callback;

  21. mAsynchronous = async;

  22. }

上面代码中,第一个if语句判断当前Handler是否为static,警示Handler最好为static这样可以防止内存泄露,至于为什么,这里就不讲了。

可以看到, mLooper = Looper.myLooper(); mQueue = mLooper.mQueue;与thread绑定的Looper对象的东西也分享给了Handler,这样MessageQueue,Message,Handler,Looper就连接起来了。Handler中移除msg,msg入队列等都是在Looper的MessageQueue中操作。

我们发送消息:

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. Message msg = handler.obtainMessage() ;

  2. msg.what = 1 ;

  3. msg.obj = “Hello World” ;

  4. handler.sendMessage(msg) ;

这样就能在hangleMessage(Message msg)方法中接收到消息了。该消息是如何传递的?

先看sendMessage(msg)做了什么。

---------->code in Handler.java

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. /**

  2. * Pushes a message onto the end of the message queue after all pending messages

  3. * before the current time. It will be received in {@link #handleMessage},

  4. * in the thread attached to this handler.

  5. *

  6. * @return Returns true if the message was successfully placed in to the

  7. *         message queue.  Returns false on failure, usually because the

  8. *         looper processing the message queue is exiting.

  9. */

  10. public final boolean sendMessage(Message msg)

  11. {

  12. return sendMessageDelayed(msg, 0);

  13. }

------->下一步

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. public final boolean sendMessageDelayed(Message msg, long delayMillis)

  2. {

  3. if (delayMillis < 0) {

  4. delayMillis = 0;

  5. }

  6. return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);

  7. }

------->下一步

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. public boolean sendMessageAtTime(Message msg, long uptimeMillis) {

  2. MessageQueue queue = mQueue;

  3. if (queue == null) {

  4. RuntimeException e = new RuntimeException(

  5. this + " sendMessageAtTime() called with no mQueue");

  6. Log.w(“Looper”, e.getMessage(), e);

  7. return false;

  8. }

  9. return enqueueMessage(queue, msg, uptimeMillis);

  10. }

先看下这个注释,返回true则表示msg成功添加进了queue中,false则表示该msg正在被执行或者looper异常退出了。

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. * @return Returns true if the message was successfully placed in to the

  2. *         message queue.  Returns false on failure, usually because the

  3. *         looper processing the message queue is exiting.  Note that a

  4. *         result of true does not mean the message will be processed – if

  5. *         the looper is quit before the delivery time of the message

  6. *         occurs then the message will be dropped.

  7. */

  8. ublic boolean sendMessageAtTime(Message msg, long uptimeMillis)

------>最后

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

  2. msg.target = this;

  3. if (mAsynchronous) {

  4. msg.setAsynchronous(true);

  5. }

  6. //将消息放入队列中去。

  7. return queue.enqueueMessage(msg, uptimeMillis);

  8. }

有个使用,我们将优先级搞得消息添加到队列头部

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. public final boolean sendMessageAtFrontOfQueue(Message msg) {

  2. MessageQueue queue = mQueue;

  3. if (queue == null) {

  4. RuntimeException e = new RuntimeException(

  5. this + " sendMessageAtTime() called with no mQueue");

  6. Log.w(“Looper”, e.getMessage(), e);

  7. return false;

  8. }

  9. return enqueueMessage(queue, msg, 0);

  10. }

注意它直接调用了enqueueMessage,第三个参数是0。因此我们从其他方法调用可以看到这里的第三个参数应该是和消息的先后有关系。

——————————————————————————————————————

这里先跟一下,sendMessageAtFrontOfQueue方法的流程。

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. boolean enqueueMessage(Message msg, long when) {

  2. if (msg.target == null) {

  3. throw new IllegalArgumentException(“Message must have a target.”);

  4. }

  5. if (msg.isInUse()) {

  6. throw new IllegalStateException(msg + " This message is already in use.");

  7. }

  8. synchronized (this) {

  9. if (mQuitting) {

  10. IllegalStateException e = new IllegalStateException(

  11. msg.target + " sending message to a Handler on a dead thread");

  12. Log.w(“MessageQueue”, e.getMessage(), e);

  13. msg.recycle();

  14. return false;

  15. }

  16. msg.markInUse();

  17. msg.when = when;

  18. Message p = mMessages;

  19. boolean needWake;

  20. //sendMessageAtFrontOfQueue()—>enqueueMessage(queue, msg, 0); 则此处会走第一个分支。

  21. if (p == null || when == 0 || when < p.when) {

  22. // New head, wake up the event queue if blocked.

  23. msg.next = p;

  24. mMessages = msg;

  25. needWake = mBlocked;

  26. } else {

  27. // Inserted within the middle of the queue.  Usually we don’t have to wake

  28. // up the event queue unless there is a barrier at the head of the queue

  29. // and the message is the earliest asynchronous message in the queue.

  30. needWake = mBlocked && p.target == null && msg.isAsynchronous();

  31. Message prev;

  32. for (;😉 {

  33. prev = p;

  34. p = p.next;

  35. if (p == null || when < p.when) {

  36. break;

  37. }

  38. if (needWake && p.isAsynchronous()) {

  39. needWake = false;

  40. }

  41. }

  42. msg.next = p; // invariant: p == prev.next

  43. prev.next = msg;

  44. }

  45. // We can assume mPtr != 0 because mQuitting is false.

  46. if (needWake) {

  47. nativeWake(mPtr);

  48. }

  49. }

  50. return true;

  51. }

链表。。。。呵呵呵。。。。。看的有点绕。

——————————————————————————————————————

回到Looper.loop()方法中,可以看到这一句,msg.target.dispatchMessage(msg) ,将msg给msg.target去处理,而这个target就是发送消息的Handler自身

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. /**

  2. * Handle system messages here.

  3. */

  4. //在Looper.loop()方法中,msg会调用target的dispatchMessage方法。

  5. public void dispatchMessage(Message msg) {

  6. if (msg.callback != null) {

  7. handleCallback(msg);

  8. } else {

  9. if (mCallback != null) {

  10. if (mCallback.handleMessage(msg)) {

  11. return;

  12. }

  13. }

  14. handleMessage(msg);

  15. }

  16. }

这里的几个判断我就不讲了。

MessageQueue.java

上面Looper.loop()方法中queue.next()

[java]  view plain copy print ? 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. Message next() {

  2. int pendingIdleHandlerCount = -1; // -1 only during first iteration

  1. Message prev;

  2. for (;😉 {

  3. prev = p;

  4. p = p.next;

  5. if (p == null || when < p.when) {

  6. break;

  7. }

  8. if (needWake && p.isAsynchronous()) {

  9. needWake = false;

  10. }

  11. }

  12. msg.next = p; // invariant: p == prev.next

  13. prev.next = msg;

  14. }

  15. // We can assume mPtr != 0 because mQuitting is false.

  16. if (needWake) {

  17. nativeWake(mPtr);

  18. }

  19. }

  20. return true;

  21. }

链表。。。。呵呵呵。。。。。看的有点绕。

——————————————————————————————————————

回到Looper.loop()方法中,可以看到这一句,msg.target.dispatchMessage(msg) ,将msg给msg.target去处理,而这个target就是发送消息的Handler自身

[java]  view plain copy print ? [外链图片转存中…(img-hpnjZtc9-1734372081970)] [外链图片转存中…(img-icBkdHsC-1734372081971)]

  1. /**

  2. * Handle system messages here.

  3. */

  4. //在Looper.loop()方法中,msg会调用target的dispatchMessage方法。

  5. public void dispatchMessage(Message msg) {

  6. if (msg.callback != null) {

  7. handleCallback(msg);

  8. } else {

  9. if (mCallback != null) {

  10. if (mCallback.handleMessage(msg)) {

  11. return;

  12. }

  13. }

  14. handleMessage(msg);

  15. }

  16. }

这里的几个判断我就不讲了。

MessageQueue.java

上面Looper.loop()方法中queue.next()

[java]  view plain copy print ? [外链图片转存中…(img-b9n8Yjo8-1734372081971)] [外链图片转存中…(img-QjnND3rK-1734372081971)]

  1. Message next() {

  2. int pendingIdleHandlerCount = -1; // -1 only during first iteration

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值