论坛首页 移动开发技术论坛

Handler Looper Message源码研究

浏览 4315 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2011-11-22   最后修改:2011-11-22
概述
Looper是消费者,Handler是生产者(同时Looper消费后,也会通知Handler),MessageQueue是消息队列,队列的实现方式是链表,Message是链表的一个节点。 我们的程序基本上之和Handler打交道。

线程Thread的线程变量ThreadLocal中,存放着这个线程的Looper;

Looper在初始化时,会新建一个消息队列MessageQueue,之后Looper进入一个死循环,等待从消息队列MessageQueue取得消息Message(Looper是消费者),没有消息时会阻塞;

我们程序中的Handler,会通过sendMessage或post方法,往MessageQueue中添加消息时,添加的这个Message,会记录他是属于哪个Handler发出的,同时根据message.when,决定新添加的这个Message在Queue中的位置,MessageQueue中只有一个当前的Message,队列关系是通过Message中的prev,next维护的,Message是一个链表的节点;

添加消息后,消费者Looper取得Message,并调用建立Message的Hander的dispatchMessage方法。

咋一看好像Handler即sendMessage,又handlerMessage,事情还是只有一个线程在做事情。
但是后来想想,明白了这样设计的必要性。
因为这个唯一的线程一般而言,都是mainUI线程,如果你有个可以分成多个小任务的任务要处理,你没有使用Handler,直接执行,也许系统忙于处理你这个任务,而无法及时响应用户事件,从而导致ANR的抛出。
如果你把你的任务拆成几个小任务,用Handler来实现,那么系统就可以把你的小任务推到后面来处理,抽出时间来响应用户操作。
如果真的有大任务,一般式需要另外线程去处理,或者开启Service。




一个在新线程中使用handler例子,我们来分析下源码
new Thread(new Runnable() {
     @Override public void run() {
          Handler handler;
          //1、初始化Looper
          Looper.prepare();
          //2、绑定handler到CustomThread实例的Looper对象、定义处理消息的方法
          handler= new Handler() {
               @Override public void handleMessage(Message msg) {
               }
          };
          // 3、发送消息
          handler.sendMessage(new Message());
          handler.post(new Runnable())
          handler.obtainMessage(1, "hello").sendToTarget();
          //4、启动消息循环
          Looper.loop();
     }
}).start();


1
public static final void prepare() {
    if (sThreadLocal.get() != null) {  // 每个线程,只能有一个Looper对象
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    // 如果当前线程没有Looper,新建一个,构造函数是private的
    sThreadLocal.set(new Looper());
}
private Looper() {
    mQueue = new MessageQueue();   // 建立消息队列
    mRun = true;
    mThread = Thread.currentThread();
}


2
public Handler(){
    mLooper = Looper.myLooper(); // 取得当前线程的Looper,如果抛异常
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;  // 取得消息队列
    mCallback = null;
}


3
//不管调用哪个方法,最终执行的是
public boolean sendMessageAtTime(Message msg, long uptimeMillis){
    boolean sent = false;
    // 取得消息队列
    MessageQueue queue = mQueue;
    if (queue != null) {
        msg.target = this;  // 消息发出着是自己
        sent = queue.enqueueMessage(msg, uptimeMillis); // 添加到消息队列中
    }
    else {
        RuntimeException e = new RuntimeException(
            this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
    }
    return sent;
}
final boolean enqueueMessage(Message msg, long when) {
    if (msg.when != 0) {
        throw new AndroidRuntimeException(msg
                + " This message is already in use.");
    }
    if (msg.target == null && !mQuitAllowed) {
        throw new RuntimeException("Main thread not allowed to quit");
    }
    synchronized (this) {
        if (mQuiting) {
            RuntimeException e = new RuntimeException(
                msg.target + " sending message to a Handler on a dead thread");
            Log.w("MessageQueue", e.getMessage(), e);
            return false;
        } else if (msg.target == null) {
            mQuiting = true;
        }

        msg.when = when;
        Message p = mMessages;
        // 之前没有其他消息了,MessageQueue中当前消息mMessages 就是传递进来的msg
        if (p == null || when == 0 || when < p.when) {
            msg.next = p;
            mMessages = msg;
            this.notify(); // 唤醒
        } else {
            // 之前有其他消息了,将传递的msg放到适合的位置,根据when
            Message prev = null;
            while (p != null && p.when <= when) {
                prev = p;
                p = p.next;
            }
            msg.next = prev.next;
            prev.next = msg;
            this.notify();  // 唤醒
        }
    }
    return true;
}


4
public static final void loop() {
    Looper me = myLooper();
    MessageQueue queue = me.mQueue;
    while (true) {                            // 死循环
        Message msg = queue.next(); // 当队列中没有消息时会阻塞
        if (msg != null) {
            if (msg.target == null) {  // 消息没有发送者时,退出消息循环
                // No target is a magic identifier for the quit message.
                return;
            }
            if (me.mLogging!= null) me.mLogging.println(
                    ">>>>> Dispatching to " + msg.target + " "
                    + msg.callback + ": " + msg.what
                    );
            // 调用消息发出者的dispatchMessage,这里msg.target是我们sendMessage的handler
            msg.target.dispatchMessage(msg);
            if (me.mLogging!= null) me.mLogging.println(
                    "<<<<< Finished to    " + msg.target + " "
                    + msg.callback);
            msg.recycle();
        }
    }
}

final Message next() {
    boolean tryIdle = true;

    while (true) {

        synchronized (this) {
            // 没有消息的或,会阻塞
            try {
                if (mMessages != null) {
                    if (mMessages.when-now > 0) {
                        Binder.flushPendingCommands();
                        this.wait(mMessages.when-now);
                    }
                } else {
                    Binder.flushPendingCommands();
                    this.wait();
                }
            }
            catch (InterruptedException e) {
            }
        }
    }
}


总结
Handler作用:
1. 执行计划任务
2. 线程间通信

一个handler,只能接收到自己发出的message。handler实例与消息Message处理是关联的,发送和接受要匹配
Handler操作队列,主要是在子线程操作主线程的消息队列

Handler是实现异步的一种方式,用法是在主线程中建立Handler,(主线程中的Handler不用掉Looper.prepare);
在子线程(耗时操作)任务完成后sendMessage,这个Message会发送到主线程的消息队列中,主线程Handler的重写dispatchMessage方法,做新线程任务完成后的事情,大部分是更新UI。
  • 大小: 14 KB
   发表时间:2011-11-25  
其实就是单开了一个线程  用于数据处理 然后发送到消息队列 然后主线程从消息队列中拿到数据后 做进一步处理  说到底就是为了UI线程和子线程之间的通信
0 请登录后投票
   发表时间:2011-11-25  
以前觉得Handler存在的必要性是为了UI线程通信使用,实际开发时的使用意义不大。实际开发用AsyncTask和Thread都比它容易的多。

之后才发现Handler的通信模式其实还是很不错的。它的postDelay,removeCallbacks等方法都蛮实用的。

感谢楼主的分享
0 请登录后投票
   发表时间:2011-11-29  
研究研究研究5个字
0 请登录后投票
论坛首页 移动开发技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics