欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Looper,MessageQueue,Message,Handler

程序员文章站 2022-05-16 08:39:04
...

 

先看看Looper,MessageQueue,Message,Handler之间的关系.

Looper,MessageQueue,Message,Handler
            
    
    博客分类: Android基础篇 AndroidLooperMessageQueueMessageHandler 

Looper的构造函数是private,因此不能直接new,Looper的prepare方法会先检查当前线程下是否存在Looper,如果不存在则为当前线程new一个Looper,如果发现当前线程下已经存在Looper实例,则会抛出异常。

相关源码如下:

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    public static void prepare() {
        prepare(true);
    }

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

 其中的sThreadLocal可以简单理解为一个Map,一个以当前线程为key的Map。因此这样的初始化代码的意义就是为当前线程创建一个唯一的Looper实例。同样的道理,如果在同一个线程中多次调用Looper.prepare是会报错的。

Looper中还有一个比较常用的内容,先看源码

    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

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

乍一看起来上面这三个方法和prepare没啥区别,无非就是把为当前线程创建的Looper赋值给了sMainLooper类变量。实际上它也就是这个意思,没啥特别之处。唯一不同的是,Android系统在当前Application启动的时候(主线程中,也叫UI线程)自己调用了prepareMainLooper方法,这就导致了sMainLooper与UI线程产生绑定,带来的好处就是我们在自己的方法中(即使处于自定义子线程)可以使用getMainLooper来获得UI线程的Looper,让在这个Looper里的消息能处理UI内容。

 

接着需要看一下Handler的几个重要的构造函数干嘛了,相关源码:

    public Handler() {
        this(null, false);
    }

    public Handler(Callback callback) {
        this(callback, false);
    }

    public Handler(Looper looper) {
        this(looper, null, false);
    }

    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }

    /**
     * @hide
     */
    public Handler(boolean async) {
        this(null, async);
    }

    /**
     * @hide
     */
    public Handler(Callback callback, boolean async) {
        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();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    /**
     * @hide
     */
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

  看得出来,构造函数主要就是指定一个Looper,缺省情况下使用当前线程对应的Looper。

那么Handler中的sendMessage方法做了些什么?看代码:

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

    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) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

 很简单,就是将当前Message派发给当前Looper中指定的MessageQueue.

 

是时候来看一个常用的例子了

package com.iteye.badpie.androidabc.handler;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

public class MainActivity extends Activity {

    private static final String TAG = MainActivity.class.getSimpleName();
    private static final int MSG_HELLO = 100;
    private Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler = new Handler() {

            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                case MSG_HELLO:
                    Log.d(TAG, String.format("handleMessage what:%s", "MSG_HELLO"));
                    break;

                default:
                    Log.d(TAG, String.format("handleMessage what:%s", msg.what));
                    break;
                }
            }

        };

        Message message = mHandler.obtainMessage(MSG_HELLO);
        mHandler.sendMessage(message);
    }

}

 运行结果:

D/MainActivity(  758): handleMessage what:MSG_HELLO

在主线程中运行的,因此可以在handlerMessage中处理UI内容,比如设置TextView的文字等等。

 

接着来看一个在自定义Thread中运行的例子

package com.iteye.badpie.androidabc.handler;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

public class MainActivity extends Activity {

    private static final String TAG = MainActivity.class.getSimpleName();
    private static final int MSG_HELLO = 100;
    private Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        new Thread("My Thread") {

            @Override
            public void run() {
                // 为这个子线程创建一个Looper
                Looper.prepare();
                mHandler = new Handler() {

                    @Override
                    public void handleMessage(Message msg) {
                        switch (msg.what) {
                        case MSG_HELLO:
                            Log.d(TAG, String.format("handleMessage what:%s, Looper:%s", "MSG_HELLO", Looper.myLooper().getThread().getName()));
                            break;

                        default:
                            Log.d(TAG, String.format("handleMessage what:%s, Looper:%s", msg.what, Looper.myLooper().getThread().getName()));
                            break;
                        }
                    }

                };

                Message message = mHandler.obtainMessage(MSG_HELLO);
                mHandler.sendMessage(message);

                // 让这个Looper驱动起来
                Looper.loop();
            }

        }.start();

    }

}

运行结果:

D/MainActivity(  927): handleMessage what:MSG_HELLO, Looper:My Thread
 

这个消息循环有问题,因为它不能结束。Looper.loop方法会一直运行,该方法在这个代码中不会返回。这可能需要一个变量来记录My Thread线程下的Looper,然后在需要时候调用其quit方法来结束消息循环。

可喜的是,针对这种问题Android提供了一个更方便的使用方法,看代码

package com.iteye.badpie.androidabc.handler;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

public class MainActivity extends Activity {

    private static final String TAG = MainActivity.class.getSimpleName();
    private static final int MSG_HELLO = 100;
    private HandlerThread mHandlerThread;
    private Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandlerThread = new HandlerThread("Handler Thread");
        mHandlerThread.start();
        mHandler = new Handler(mHandlerThread.getLooper()) {

            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                case MSG_HELLO:
                    Log.d(TAG, String.format("handleMessage what:%s, Looper:%s", "MSG_HELLO", Looper.myLooper().getThread().getName()));
                    break;

                default:
                    Log.d(TAG, String.format("handleMessage what:%s, Looper:%s", msg.what, Looper.myLooper().getThread().getName()));
                    break;
                }
            }

        };

        Message message = mHandler.obtainMessage(MSG_HELLO);
        mHandler.sendMessage(message);

        // 结束消息循环时调用.最终会调用当前Looper中引用的MessageQueue的quit方法.
        // mHandlerThread.quit();
    }

}

 程序运行结果:

D/MainActivity( 1026): handleMessage what:MSG_HELLO, Looper:Handler Thread

 

附录

MessageQueue如何enqueue消息

    final boolean enqueueMessage(Message msg, long when) {
        if (msg.isInUse()) {
            throw new AndroidRuntimeException(msg + " This message is already in use.");
        }
        if (msg.target == null) {
            throw new AndroidRuntimeException("Message must have a target.");
        }

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

            msg.when = when;
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
        }
        if (needWake) {
            nativeWake(mPtr);
        }
        return true;
    }

看得出来,如果

p == null || when == 0 || when < p.when

会直接将消息放到链表的开始位置.所以在发送消息时使用如下方法会迅速将消息放到消息链表的开始位置:

    Message message = mHandler.obtainMessage(MSG_HELLO);
    mHandler.sendMessageAtTime(message, 0);
 
  • Looper,MessageQueue,Message,Handler
            
    
    博客分类: Android基础篇 AndroidLooperMessageQueueMessageHandler 
  • 大小: 40.9 KB