前言
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;
}
}