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

Handler 、 Looper 、Message、HandlerThread总结

程序员文章站 2022-07-14 16:46:04
...

Looper主要有prepare()和loop()两个方法,Looper是用来从MessageQueue中抽取Message,发送给Handler进行处理。
prepare()方法:

public static final void prepare() {  
        if (sThreadLocal.get() != null) {  
            throw new RuntimeException("Only one Looper may be created per thread");  
        }  
        sThreadLocal.set(new Looper(true));  
} 

sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。第一次调用prepare()方法,if条件不成立,所以将创建的Looper对象保存到sThreadLocal中。第二次调用就会抛出异常,说明Looper.prepare()方法不能被调用两次,也保证了一个线程中只有一个Looper实例。

Looper的构造方法:

private Looper(boolean quitAllowed) {  
        mQueue = new MessageQueue(quitAllowed);  
        mRun = true;  
        mThread = Thread.currentThread();  
}  

在构造方法中,创建了一个MessageQueue(消息队列)

loop()方法中调用myLooper()方法取出sThreadLocal中保存的Looper实例,如果Looper实例为null则抛出异常,所以looper方法必须在prepare方法之后运行。接着会获取到该looper实例中的mQueue(消息队列)。然后进入了无限循环,从消息队列中取出消息。调用 msg.target.dispatchMessage(msg);把消息交给msg的target的dispatchMessage方法去处理,Msg的target就是handler对象。

final Looper me = myLooper();  
if (me == null) {  
    throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
}  
final MessageQueue queue = me.mQueue

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

Handler的构造方法中会调用Looper.myLooper()获取了当前线程保存的Looper实例,然后又通过mLooper.mQueue获取了Looper实例中保存的MessageQueue(消息队列),这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了Handler的sendMessage(Message msg)方法最后调用了sendMessageAtTime(),在此方法内部直接获取MessageQueue然后调用了enqueueMessage方法

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

enqueueMessage方法中msg.target赋值为this,在Looper的loop方法会取出每个msg然后交给msg.target.dispatchMessage(msg)去处理消息,所以当前的handler就是msg的target。最终调用queue的enqueueMessage的方法,也就是说handler发出的消息,最终会保存到消息队列中去。不断从MessageQueue中读取Handler发来的消息,然后再回调创建这个消息的handler中的dispathMessage方法,在dispatchMessage方法中调用了handleMessage(msg)方法,方法内部是空实现,因为消息的最终回调是由我们控制的,我们在创建handler的时候都是复写handleMessage方法,然后根据msg.what进行消息处理

public void dispatchMessage(Message msg) {  
        if (msg.callback != null) {  
            handleCallback(msg);  
        } else {  
            if (mCallback != null) {  
                if (mCallback.handleMessage(msg)) {  
                    return;  
                }  
            }  
            handleMessage(msg);  
        }  
} 

public void handleMessage(Message msg) {  
} 

注意:在Activity中,我们并没有调用Looper.prepare()和Looper.loop()方法,但是Handler可以成功创建,这是因为在Activity的启动代码中,已经在当前UI线程调用了Looper.prepare()和Looper.loop()方法

HandlerThread是Android API提供的一个便捷的类,使用它我们可以快速的创建一个带有Looper的线程,有了Looper这个线程,我们又可以生成Handler。HandlerThread本质上是一个线程类,它继承了Thread;
HandlerThread有自己的内部Looper对象,可以进行looper循环;
通过获取HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务。
创建HandlerThread后必须先调用HandlerThread.start()方法,Thread会先调用run方法,创建Looper对象

使用普通的Thread来创建一个Handler的过程:

Handler mHandler;
private void createThreadWithHandler() {
  new Thread() {
      @Override
        public void run() {
            super.run();
            Looper.prepare();
            mHandler = new Handler(Looper.myLooper());
            Looper.loop();
        }
    }.start();
}

调用Looper.prepare 创建与当前线程绑定的Looper实例
使用上面创建的Looper生成Handler实例
调用Looper.loop()实现消息循环

HandlerThread使用:
//传入参数的作用主要是标记当前线程的名字,可以任意字符串
HandlerThread workerThread = new HandlerThread("LightTaskThread");
workerThread.start();
mHandler = new Handler(workerThread.getLooper());
注意:上面的workerThread.start()必须要执行。
在activity的onDestory方法中调用workerThread.quit()方法结束当前的Looper循环

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

    /**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
}

从源码可以看出HandlerThread继续自Thread,构造函数的传递参数有两个,一个是name指的是线程的名称,一个是priority指的是线程优先级,我们根据需要调用即可。其中成员变量mLooper就是HandlerThread自己持有的Looper对象。onLooperPrepared()该方法是一个空实现,是留给我们必要时可以去重写的,但是注意重写时机是在Looper循环启动前

看看run方法:

@Override
public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll(); 
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
}

前面我们在HandlerThread的常规使用中分析过,在创建HandlerThread对象后必须调用其start()方法才能进行其他操作,而调用start()方法后相当于启动了线程,也就是run方法将会被调用,而我们从run源码中可以看出其执行了Looper.prepare()代码,这时Looper对象将被创建,当Looper对象被创建后将绑定在当前线程(也就是当前异步线程),这样我们才可以把Looper对象赋值给Handler对象,进而确保Handler对象中的handleMessage方法是在异步线程执行的。

最近看到的一篇详细分析线程池的文章,学习一下:
https://mp.weixin.qq.com/s/rHnzqlzJusIVNO7X7mBDkA