天天看点

Android中的消息机制Looper与MessageQueue子线程中创建Handler为何会抛出异常 ?总结

参考文章:

http://blog.csdn.net/bboyfeiyu/article/details/38555547

http://www.jianshu.com/p/6f25729ef62a

在Android应用启动时,会默认有一个主线程(UI线程),在这个线程中会关联一个消息队列,所有的操作都会被封装成消息然后交给主线程来处理。为了保证主线程不会主动退出,会将取消息的操作放在一个死循环中,这样程序就相当于一直在执行死循环,因此不会退出。

示例图如下 :

Android中的消息机制Looper与MessageQueue子线程中创建Handler为何会抛出异常 ?总结

Android应用程序的入口为ActivityThread.main方法:

public static void main(String[] args) {  
     SamplingProfilerIntegration.start();  
     CloseGuard.setEnabled(false);  

     Environment.initForCurrentUser();  

     // Set the reporter for event logging in libcore  
     EventLogger.setReporter(new EventLoggingReporter());  

     Process.setArgV0("<pre-initialized>");  

     Looper.prepareMainLooper();// 1、创建消息循环Looper  

     ActivityThread thread = new ActivityThread();  
     thread.attach(false);  

     if (sMainThreadHandler == null) {  
     sMainThreadHandler = thread.getHandler(); // UI线程的Handler  
     }  

     AsyncTask.init();  

     if (false) {  
     Looper.myLooper().setMessageLogging(new  
     LogPrinter(Log.DEBUG, "ActivityThread"));  
     }  

     Looper.loop();   // 2、执行消息循环  

     throw new RuntimeException("Main thread loop unexpectedly exited");  
 }  
           

执行ActivityThread.main方法后,应用程序就启动了,并且会一直从消息队列中取消息,然后处理消息。那么系统是如何将消息投递到消息队列中的?又是如何从消息队列中获取消息并且处理消息的呢? 答案就是Handler。

每个Handler都会关联一个消息队列,消息队列被封装在Lopper中,而每个Looper又会关联一个线程(ThreadLocal),也就是每个消息队列会关联一个线程。Handler就是一个消息处理器,将消息投递给消息队列,然后再由对应的线程从消息队列中挨个取出消息,并且执行。默认情况下,消息队列只有一个,即主线程的消息队列,这个消息队列是在ActivityThread.main方法中创建的,通过Lopper.prepareMainLooper()来创建,然后最后执行Looper.loop()来启动消息循环。那么Handler是如何关联消息队列以及线程的呢?我们看看如下源码 :

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

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

从Handler默认的构造函数中我们可以看到,Handler会在内部通过Looper.getLooper()来获取Looper对象,并且与之关联,最重要的就是消息队列。那么Looper.getLooper()又是如何工作的呢?我们继续往下看.

/** 
 * 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();  
}  

/** 
 * Initialize the current thread as a looper, marking it as an 
 * application's main looper. The main looper for your application 
 * is created by the Android environment, so you should never need 
 * to call this function yourself.  See also: {@link #prepare()} 
 */  
public static void prepareMainLooper() {  
    prepare();  
    setMainLooper(myLooper());  
    myLooper().mQueue.mQuitAllowed = false;  
    }  

    private synchronized static void setMainLooper(Looper looper) {  
    mMainLooper = looper;  
}  

 /** Initialize the current thread as a looper. 
  * This gives you a chance to create handlers that then reference 
  * this looper, before actually starting the loop. Be sure to call 
  * {@link #loop()} after calling this method, and end it by calling 
  * {@link #quit()}. 
  */  
public static void prepare() {  
    if (sThreadLocal.get() != null) {  
    throw new RuntimeException("Only one Looper may be created per thread");  
    }  
    sThreadLocal.set(new Looper());  
}  
           

Looper与MessageQueue

创建了Looper后,如何执行消息循环呢?通过Handler来post消息给消息队列( 链表 ),那么消息是如何被处理的呢?答案就是在消息循环中,消息循环的建立就是通过Looper.loop()方法。源码如下 :

/** 
 * Run the message queue in this thread. Be sure to call 
 * {@link #quit()} to end the loop. 
 */  
public static void loop() {  
    Looper me = myLooper();  
    if (me == null) {  
    throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
}  
MessageQueue queue = me.mQueue;// 1、获取消息队列  

// 代码省略  


while (true) {   // 2、死循环,即消息循环  
    Message msg = queue.next(); // 3、获取消息 (might block )  
    if (msg != null) {  
        if (msg.target == null) {  
                // No target is a magic identifier for the quit message.  
                return;  
            }  

            long wallStart = 0;  
            long threadStart = 0;  

            // This must be in a local variable, in case a UI event sets the logger  
            Printer logging = me.mLogging;  
            if (logging != null) {  
                logging.println(">>>>> Dispatching to " + msg.target + " " +  
                msg.callback + ": " + msg.what);  
                wallStart = SystemClock.currentTimeMicro();  
                threadStart = SystemClock.currentThreadTimeMicro();  
            }  

            msg.target.dispatchMessage(msg);// 4、处理消息  

            // 代码省略  

            msg.recycle();  
        }  
    }  
}  
           

可以看到,loop方法中实质上就是建立一个死循环,然后通过从消息队列中挨个取出消息,最后处理消息的过程。对于Looper我们总结一下 : 通过Looper.prepare()来创建Looper对象(消息队列封装在Looper对象中),并且保存在sThreadLoal中,然后通过Looper.loop()来执行消息循环,这两步通常是成对出现的!! 最后我们看看消息处理机制,我们看到代码中第4步通过msg.target.dispatchMessage(msg)来处理消息。其中msg是Message类型,我们看源码 :

public final class Message implements Parcelable {  

    public int what;  

    public int arg1;   

    public int arg2;  

    public Object obj;  


    int flags;  

    long when;  

    Bundle data;  

    Handler target; // target处理  

    Runnable callback;  // Runnable类型的callback  

    // sometimes we store linked lists of these things  
    Message next;   // 下一条消息,消息队列是链式存储的  


    // 代码省略 ....  
}
           

从源码中可以看到,target是Handler类型。实际上就是转了一圈,通过Handler将消息投递给消息队列,消息队列又将消息分发给Handler来处理。我们继续看:

/** 
 * Subclasses must implement this to receive messages. 
 */  
public void handleMessage(Message msg) {  
}  

private final void handleCallback(Message message) {  
    message.callback.run();  
}  

/** 
 * Handle system messages here. 
 */  
public void dispatchMessage(Message msg) {  
    if (msg.callback != null) {  
        handleCallback(msg);  
    } else {  
        if (mCallback != null) {  
            if (mCallback.handleMessage(msg)) {  
                return;  
            }  
        }  
        handleMessage(msg);  
    }  
}
           

可以看到,dispatchMessage只是一个分发的方法,如果Runnable类型的callback为空则执行handlerMessage来处理消息,该方法为空,我们会将更新UI的代码写在该函数中;如果callback不为空,则执行handleCallback来处理,该方法会调用callback的run方法。其实这是Handler分发的两种类型,比如我们post(Runnable callback)则callback就不为空,当我们使用Handler来sendMessage时通常不会设置callback,因此也就执行handlerMessage这个分支。我们看看两种实现:

public final boolean post(Runnable r)  
{  
   return  sendMessageDelayed(getPostMessage(r), 0);  
}  
private final Message getPostMessage(Runnable r) {  
    Message m = Message.obtain();  
    m.callback = r;  
    return m;  
}  

public final boolean sendMessageDelayed(Message msg, long delayMillis)  
{  
    if (delayMillis < 0) {  
    delayMillis = 0;  
    }  
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
}  

public boolean sendMessageAtTime(Message msg, long uptimeMillis)  
{  
    boolean sent = false;  
    MessageQueue queue = mQueue;  
    if (queue != null) {  
        msg.target = this;  // 设置消息的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;  
}   
           

可以看到,在post(Runnable r)时,会将Runnable包装成Message对象,并且将Runnable对象设置给Message对象的callback字段,最后会将该Message对象插入消息队列。sendMessage也是类似实现 :

public final boolean sendMessage(Message msg)  
{  
    return sendMessageDelayed(msg, 0);  
}
           

子线程中创建Handler为何会抛出异常 ?

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

解决:

new Thread(){  
    Handler handler = null;  
    public void run() {  
        Looper.prepare();    // 1、创建Looper,并且会绑定到ThreadLocal中  
        handler = new Handler();  
        Looper.loop();       // 2、启动消息循环  
    };  
}.start(); 
           

在代码中我们加了2处,第一是通过Looper.prepare()来创建Looper,第二是通过Looper.loop()来启动消息循环。这样该线程就有了自己的Looper,也就是有了自己的消息队列。如果之创建Looper,而不启动消息循环,虽然不会抛出异常,但是你通过handler来post或者sendMessage也不会有效,因为虽然消息被追加到消息队列了,但是并没有启动消息循环,也就不会从消息队列中获取消息并且执行了!

总结

在应用启动时,会开启一个主线程(UI线程),并且启动消息循环,应用不停地从该消息队列中取出、处理消息达到程序运行的效果。Looper对象封装了消息队列,Looper对象是ThreadLocal的,不同线程之间的Looper对象不能共享与访问。而Handler通过与Looper对象绑定来实现与执行线程的绑定,handler会把Runnable(包装成Message)或者Message对象追加到与线程关联的消息队列中,然后在消息循环中挨个取出消息,并且处理消息。当Handler绑定的Looper是主线程的Looper,则该Handler可以在handleMessage中更新UI,否则更新UI则会抛出异常!其实我们可以把Handler、Looper、Thread想象成一个生产线,工人(搬运工)相当于Handler,负责将货物搬到传输带上(Handler将消息传递给消息队列);传送带扮演消息队列的角色,负责传递货物,货物会被挨取出,并且输送到目的地 ( target来处理 );而货物到达某个车间后再被工人处理,车间就扮演了Thread这个角色,每个车间有自己独立的传送带,车间A的货物不能被车间B的拿到,即相当于ThreadLocal( 车间独有 )。