在日常开发中,我们经常会碰到一些需要在页面执行耗时操作的需求,一般我们会新建一个子线程来完成这部分的操作,非常的方便,但是大家有没有想过,当一个页面短时间内需要多次执行耗时任务的时候,频繁的去创建/销毁线程,其实是很耗费系统资源的。
如果你了解过Android的消息处理机制,可能你会采取自己构建一个循环线程,在线程中创建Looper对象来进行消息的轮询,耗时任务会以消息的形式放到消息队列,当队列中有任务时,会通过Looper取出交给Handler来处理,没任务时会阻塞线程,等待新任务。这样做,确实可以解决多次创建/销毁线程所带来的系统资源的耗费,不过这样做也会引发另外的问题,比如多线程并发导致的状态不同步,这些都是需要开发者而外去处理的,谷歌公司也考虑到了这点,所以给我们提供了一个很强大的线程类——HandlerThread。
如果不了解消息Android的消息处理机制也不打紧,这里给传送门:从源码的角度理解Android的消息处理机制
什么是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就讲完了,收工~