HandlerThread的使用以及其原理解析

前言

HandlerThread有什么作用呢?能干什么?
比如说我们现在有个需求必须要Handler创建在子线程进行工作
我们需要手动创建子线程然后在子线程中创建Looper一些操作:
new Thread(new Runnable() {
            @Override
            public void run() {
                // 在当前线程创建Looper对象
                Looper.prepare();
                // 创建Handler对象传入当前线程的Looper
                Handler handler = new Handler(Looper.myLooper());
                // 开始循环消息队列
                Looper.loop();
            }
}).start();

然而谷歌提供的HandlerThread类可以简化如上代码

        HandlerThread handlerThread = new HandlerThread("hello");
        handlerThread.start();
        Handler handler = new Handler(handlerThread.getLooper());

就是这么简单,Handler就创建并工作在线程了

OK 我们来看下这个HandlerThread到底是个什么东东

/**
 * Handy class for starting a new thread that has a looper. The looper can then be 
 * used to create handler classes. Note that start() must still be called.
 */
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;
    }

哟呵,这特么不是个Thread吗!
可以看出来HandlerThread本身就是继承Thread的子类
我们看下他的几个参数以及构造方法

int mPriority;//线程优先级
int mTid = -1;// 线程id
Looper mLooper;// 与当前线程绑定的Looper对象

// name线程名称
public HandlerThread(String name) {
        super(name);
        // 优先级为默认优先级,也就是0
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    // name线程名称,priority优先级,接受自定义的
    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() {
    }

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

onLooperPrepared方法,这个方法可以在Looper调用loop方法之前做的完成一些准备工作
接下来看run方法

// 毫无疑问,是用来获取当前线程id的
mTid = Process.myTid();
// 这个嘛是在当前线程创建一个Looper
// 在当前线程这个方法只能被调用一次
// 至于为什么呢,给你看看prepare();的代码
Looper.prepare();
// 在下面,哈哈

在这

Looper.preare();
我们调用了prepare方法,prepare内部调用了prepare(true)方法
public static void prepare() {
        prepare(true);
    }
private static void prepare(boolean quitAllowed) {
        // 看这里,取出当前线程的Looper对象如果不为null直接抛出异常信息
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 是null的话才会新建一个Looper对象
        sThreadLocal.set(new Looper(quitAllowed));
}

继续刚才的分析

@Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
         // 这里使用了线程锁,获取了当前线程的Looper对象
       synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        // 设置了线程的优先级
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        // 开始消息队列循环
        // 内部开启了死循环进行工作
        Looper.loop();
        mTid = -1;
    }

看了上面的是不是有疑惑为什么有个notifyAll()方法;
为什么会调用个notifyAll()呢,很疑惑把,一开始我也疑惑,接下来就明白了。

// 这个方法是获取当前线程创建的Looper方法
// 主要功能就是检查当前线程状态是否启动,如果未启动就返回null,如果是启动状态并且Looper为空的话
// 则进入阻塞状态等待,直到run方法中的 mLooper = Looper.myLooper();赋值之后调用notityAll方法才开始继续往下执行,发会Looper对象
public Looper getLooper() {
        // isAlive();方法判断当前线程是否开始工作
        if (!isAlive()) {
            return null;
        }
        
        // 如果线程已经启动,等到Looper对象创建完毕
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    // 进入阻塞状态
                   // 等待run方法执行到notifiyAll将其唤醒
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

是不是明白了点!OK我们继续,还有三个方法

// 停止消息循环队列,Looper不再接收消息
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
    // 安全停止消息循环队列,Looper不再接收消息
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

    // 获取当前线程id
    public int getThreadId() {
        return mTid;
    }

关于quit方法和quitSafely方法的区别:

  • quit退出时会清空MessageQueue消息池中的所有消息,包含非延时消息和延时消息
  • quitSafely退出时会清空延时消息,并把所有非延时消息全部发送给Handler的handleMessage方法去处理

OK 到这我们就分析完毕了,HandlerThread已经为我们封装好了Looper

这样我们在子线程使用Handler的话就会简洁一些!

如有分析错误的地方欢迎指出,谢谢

下面贴出HandlerThread的所有源码

/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.os;

/**
 * Handy class for starting a new thread that has a looper. The looper can then be 
 * used to create handler classes. Note that start() must still be called.
 */
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() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    
    /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason is isAlive() returns false, this method will return null. If this thread 
     * has been started, this method will block until the looper has been initialized.  
     * @return The looper.
     */
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

    /**
     * Quits the handler thread's looper.
     * <p>
     * Causes the handler thread's looper to terminate without processing any
     * more messages in the message queue.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p class="note">
     * Using this method may be unsafe because some messages may not be delivered
     * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
     * that all pending work is completed in an orderly manner.
     * </p>
     *
     * @return True if the looper looper has been asked to quit or false if the
     * thread had not yet started running.
     *
     * @see #quitSafely
     */
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }

    /**
     * Quits the handler thread's looper safely.
     * <p>
     * Causes the handler thread's looper to terminate as soon as all remaining messages
     * in the message queue that are already due to be delivered have been handled.
     * Pending delayed messages with due times in the future will not be delivered.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p>
     * If the thread has not been started or has finished (that is if
     * {@link #getLooper} returns null), then false is returned.
     * Otherwise the looper is asked to quit and true is returned.
     * </p>
     *
     * @return True if the looper looper has been asked to quit or false if the
     * thread had not yet started running.
     */
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

    /**
     * Returns the identifier of this thread. See Process.myTid().
     */
    public int getThreadId() {
        return mTid;
    }
}

Paste_Image.png

推荐阅读更多精彩内容