概述
前面分析了很多并发编程方面的东西,但是都是Java层面的,其实Google原生也提供了一些类方便我们进行并发编程,比较常见的有HandlerThread,IntentService,AsyncTask,除此之外还有一些第三方框架Volley,Picasso等。
研究这些类以及开源框架的实现,可以让我们更好地理解并发编程,甚至是自己也可以写一个异步框架也不是什么难事,下面就来从源码的角度按照顺序来先分析一下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.
一个好用的类用于创建一个自带Looper的线程。这个Looper可以用来创建Handler。注意start()方法必须首先被调用。
看过Handler的源码都应该比较熟悉,Handler的消息是需要Looper来进行轮询的,也就是每个Handler创建的时候都需要传入一个Looper,不过我们平时创建Handler的时候之所以不需要传入Looper,是因为主线程默认为我们创建了一个looper,当然我们也可以传入自己的Looper.所以为了避免每次在子线程中创建Handler都需要创建Looper,Google为我们提供了HandlerThread这个类。
成员变量
int mPriority;//线程优先级
int mTid = -1;//线程ID
Looper mLooper;//创建线程的Looper
继承关系
继承关系比较简单,仅仅继承自Thread,在内部做了一些封装。
构造方法
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
这个方法Google都没有注释,太简单了,就是传入一个线程名称,然后优先级是默认的优先级0
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
构造方法中新加了一个线程优先级
run方法
@Override
public void run() {
//获取进程ID
mTid = Process.myTid();
//Loopr准备
Looper.prepare();
//创建Looper
synchronized (this) {
mLooper = Looper.myLooper();
//唤醒所有等待的线程
notifyAll();
}
//设置线程优先级
Process.setThreadPriority(mPriority);
//在Looper循环时做一些准备工作
onLooperPrepared();
//开启循环
Looper.loop();
mTid = -1;
}
getLooper
获取子线程的Looper
public Looper getLooper() {
//如果线程已经消亡,就返回null
if (!isAlive()) {
return null;
}
//如果线程已经创建了,就在此处停留等待Looper创建完成之后
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
//等待线程被创建
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
quit方法
public boolean quit()
//获取looper
Looper looper = getLooper();
//退出looper
if (looper != null) {
looper.quit();
return true;
}
return false;
}
quitSafely
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
跟quit方法的唯一区别在于looper.quit()变成了looper.quitSafely(),现在具体分析一下这两个方法的区别
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
一个传入了false,一个传入了true,继续追踪
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
//移除消息队列中延迟的消息
removeAllFutureMessagesLocked();
} else {
//移除消息队列中所有的消息
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
removeAllFutureMessagesLocked方法
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) {
//当消息队列中中的消息的发送时间大于当前时间
//就移除该消息
removeAllMessagesLocked();
} else {
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
所以看到这里,quit跟quidSafely的区别就在于是否移除消息队列中还未发送也就是延迟的消息。
使用
//创建mHandlerThread
mHandlerThread = new HandlerThread("main");
//获取HandlerThead中的Looper
Looper looper = mHandlerThread.getLooper();
//创建子线程中的Looper
Handler handler = new Handler(looper);
//执行耗时操作
handler.post(new Runnable() {
@Override
public void run() {
//子线程中执行耗时操作
}
});
//界面销毁的时候需要销毁Looper
@Override
protected void onDestroy() {
super.onDestroy();
mHandlerThread.quit();
}
总结
如果没有HandlerThread,我们需要手动去创建一个线程,现在HandlerThread可以帮我们简化这个操作,但是有一点需要注意的是,由于我们的异步操作是存放在Handler的消息队列中的,所以是串行的,所以只适合并发量较少的耗时操作。