从源码的角度理解HandlerThread

作者: 李晨玮 | 来源:发表于2018-02-03 19:32 被阅读64次

在日常开发中,我们经常会碰到一些需要在页面执行耗时操作的需求,一般我们会新建一个子线程来完成这部分的操作,非常的方便,但是大家有没有想过,当一个页面短时间内需要多次执行耗时任务的时候,频繁的去创建/销毁线程,其实是很耗费系统资源的。

如果你了解过Android的消息处理机制,可能你会采取自己构建一个循环线程,在线程中创建Looper对象来进行消息的轮询,耗时任务会以消息的形式放到消息队列,当队列中有任务时,会通过Looper取出交给Handler来处理,没任务时会阻塞线程,等待新任务。这样做,确实可以解决多次创建/销毁线程所带来的系统资源的耗费,不过这样做也会引发另外的问题,比如多线程并发导致的状态不同步,这些都是需要开发者而外去处理的,谷歌公司也考虑到了这点,所以给我们提供了一个很强大的线程类——HandlerThread。

如果不了解消息Android的消息处理机制也不打紧,这里给传送门:从源码的角度理解Android的消息处理机制

Handler消息处理机制

什么是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.

上面的引用是来自官方对HandlerThread的介绍,大概意思是HandlerThread可以用来创建一个带有Looper对象的线程,这个Looper对象可以在创建Handler对象时所使用。

HandlerThread源码解读

由于HandlerThread的源码比较短,这里就直接贴出代码了,如果你了解Android的消息处理机制,花个2-3分钟瞅瞅就可以了,哈哈哈~

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

1、 首先我们看出HandlerThread是一个线程类,因为他继承了Thread。
2、在构造方法HandlerThread(String name, int priority)中我们可以定义线程名和优先级(android.os.Process)。
3、既然是线程类,那么肯定有run方法了,我们来看下run方法,在方法内我们可以发现Looper.prepare();Looper.loop();,这是用来创建Looper对象和使Looper对象正常运作的方法,在loop()方法前系统还为我们提供了一个初始化操作的方法onLooperPrepared();,我们可以覆写这个方法做一些我们需要想做的事情。
4、在run方法里,我们还可以看一个同步代码块,这个是用来做什么的呢?在同步代码块中,通过Looper.myLooper();到ThreadLocal中去获取已有的Looper对象,并用notifyAll();通知getLooper()方法中的wait()方法,让其释放同步锁,返回Looper对象,由于我们在主线程中创建Handler的时候需要一个Looper对象,而这个Looper对象是在子线程中产生的,避免因线程同步导致Looper空指针问题 ,我们必须等得到Looper对象创建完毕,才能正确的执行getLooper()方法。
5、关于Looper退出线程,这里提供了quit()quitSafely()2个方法,quit方法执行后,则不再处理消息队列中的任何消息,而quitSafely则是会处理完消息队列中的消息,这2个方法执行后,则消息队列不再接收任何由Handler post出的消息。

HandlerThread的特点

1、HandlerThread是一个线程类,它集成了Thread。
2、HandlerThread有自己内部的Looper对象,可以进行looper循环。
3、通过获取HandlerThread的Looper对象创建Handler,可以在handleMessage中处理异步任务。
4、不会阻塞线程,减少性能的开销,但是不能通行执行异步任务,处理效率较低。
5、HandlerThread是个串行队列,HandlerThread背后只有一个线程。

好了,到这里HandlerThread就讲完了,收工~

相关文章

  • 从源码的角度理解HandlerThread

    在日常开发中,我们经常会碰到一些需要在页面执行耗时操作的需求,一般我们会新建一个子线程来完成这部分的操作,非常的方...

  • IntentService

    先上源码: 自己的理解: 1、新建Thread HandlerThread;2、指定ServiceHandler运...

  • 3.3异步消息处理机制-HandlerThread

    HandlerThread handlerThread是什么 handlerThread源码解析 1.handle...

  • 10 异步3-HandlerThread

    1)handlerThread是什么2)handlerThread源码解析 1、handlerThread产生背景...

  • 从源码角度深入理解Glide(下)

    上两篇文章从源码角度深入理解Glide(上)和从源码角度深入理解Glide(中)中,我们已经把Glide加载图片的...

  • HandlerThread

    在分析HandlerThread的源码之前,可以先大致看一下HandlerThread的源码有哪些东西 通过源码可...

  • Handler从源码角度理解

    上一个文章讲解了Handler的基本使用,同时也有一些问题没有解决,本篇带你从源码的角度理解。首先让我们来看看Ha...

  • Volley

    Android Volley完全解析(四),带你从源码的角度理解Volley

  • Android事件分发机制完全解析

    Android事件分发机制完全解析,带你从源码的角度彻底理解(上)Android事件分发机制完全解析,带你从源码的...

  • Volley源码解析

    原博客地址参考资料:Android Volley完全解析(四),带你从源码的角度理解VolleyVolley 源码...

网友评论

本文标题:从源码的角度理解HandlerThread

本文链接:https://www.haomeiwen.com/subject/spbyzxtx.html