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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容