Android 并发二三事之 Handler 机制的妙用 HandlerThread
Posted MyLero
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 并发二三事之 Handler 机制的妙用 HandlerThread相关的知识,希望对你有一定的参考价值。
Android 并发第五篇
本篇会讲解如何利用 HandlerThread 实现异步操作。
HandlerThread 本身其实就是一个 Thread ,但是其内部还利用 Handler 机制。
对于提交的任务(或者说是信息 Message)依次处理。
所以在介绍 HandlerThread 原理以及如果使用之前,会首先说一个 Handler 异步机制。
当然 Handler, Looper, Message 之间的关系相信很多人都已经很熟悉了,这里会只着重介绍和本节相关的内容。
一 、Handler 机制:
1、 我们都知道,在子线程中通知主线程更新UI界面,需要使用Handler。
一般我们就直接在 Activity 中直接 初始化一个Handler 对象,像这样:
Handler uiHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
重写 handlerMessage() 方法,然后利用 uiHandler 对象在子线程中发送消息。
2、 或者我们也可以直接在主线程直接 new 一个 Handler 对象:
Handler handler = new Handler();
但在子线程中 new Handler()需要这样:
Handler handler = new Handler(Context.getMainLooper());
然后在子线程中:
handler.post(new Runnable() {
@Override
public void run() {
//执行在主线程中
Log.d(TAG, "run on UI Thread Id : "+Thread.currentThread().getId());
}
});
Handler 源码:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
在handler 中会将Runnable 对象赋值给 message.callback 属性,封装成Message,调用 sendMessageDelaye() 将消息发送出去。
sendMessageDelaye() 方法最后在辗转几次后最终会调用sendMessageAtTime() 将消息放到消息队列中。
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
而 Handler.sendMessage()的源码为:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
所以本质上,无论是利用Handler.sendMessage(),还是 Handler.post() 都是将消息添加到消息队列之中。
那么为什么在子线程中需要传入 MainLooper , 而主线程却不需要呢?
首先我们是要在子线程中通知主线程,那么我们便需要代码执行在UI 线程中。
如果在子线程中直接:
Handler handler = new Handler();
会抛出异常:
Can’t create handler inside thread that has not called Looper.prepare()
我们可以看一下源码:
Handler 源码:
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can‘t create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
我们能够看到,无参的构造方法,会调用public Handler(Callback callback, boolean async) 。
在这个方法中,调用 Looper.myLooper(); 获取 Looper 对象,之所以抛出异常,一定是其为null了。
那么为什么没有获取到Looper对象呢?
接下来看Looper源码:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
从 ThreadLocal 变量中获取当前线程的值,那么这个值是在哪里设置的呢?
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
在Looper.prepare() 中设置的。
也就是说在子线程中直接 new Handler() 对象,需要先调用Looper.prepare() 方法。
而在主线程中是不需要的,因为在应用初始化时,已经调用 Looper.prepare() 了。
而Looper 中还有一个方法:Looper.loop() 方法
Looper.loop() 内包含一个死循环,不断的从队列中获取消息,如果没有消息时,会阻塞。
Looper.loop() 调用了 Handler.dispatchMessage() 方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
在dispatchMessage() 方法中会调用 handleMessage() 方法,或者调用 handleCallback() 方法处理我们利用Handler post Runnable
所封装的消息。
3 、所以通过以上总结
我们知道如果Looper.loop()是在主线程中调用的,那么我们重写的 handlerMessage() 方法
和封装在消息中的 Runnable 都会在主线程中执行。
反过来说,如果Looper.prepare() 以及 Looper.loop() 是在子线程中调用的,那么基于子线程的Looper,所创建的Handler
所发送的消息都将会执行在子线程中,HandlerThread 便是利用了这个原理。
二 、HandlerThread
1 、我们首先看一下 HandlerThread 如何使用:
private void requestWithHandlerThread() {
//初始化一个 HandlerThread 对象
HandlerThread handlerThread = new HandlerThread("HandlerThread");
//调用start() 方法
handlerThread.start();
Log.d(TAG, "Main : "+Thread.currentThread().getId());
Log.d(TAG, "HandlerThread : "+handlerThread.getId());
//初始化一个Handler 对象,利用 HandlerThread 中的 Looper 对象
Handler handler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//执行在子线程中
Log.d(TAG,"Thread : "+Thread.currentThread().getId() +" "+msg.obj);
}
};
Message message = Message.obtain();
message.obj = "From Message";
handler.sendMessage(message);
handler.post(new Runnable() {
@Override
public void run() {
//执行在子线程中
Log.d(TAG, "post : "+Thread.currentThread().getId());
}
});
}
2 、结果:
11-15 17:20:15.634 12297-12297/com.loader.demo D/Demo: Main : 1
11-15 17:20:15.634 12297-12297/com.loader.demo D/Demo: HandlerThread : 26599
11-15 17:20:15.640 12297-12416/com.loader.demo D/Demo: Thread : 26599 From Message
11-15 17:20:15.640 12297-12416/com.loader.demo D/Demo: post : 26599
在这里 HandlerThread 需要和 Handler 一起配合使用,HandlerThread 提供一个在子线程中创建的 Looper 。
按照之前的推论,Looper.prepare(), 以及 Looper.loop() 都是执行在子线程中,那么在处理消息时也必然执行在子线程中。
所以其实现了异步的效果。
3 、接下来看一下 HandlerThread 的源码:
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
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;
}
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;
}
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
public int getThreadId() {
return mTid;
}
}
4 、总结
可以看到 HandlerThread 本身是一个 Thread 所以其 run() 方法会执行在子线程中。在 run() 方法中首先调用了Looper.prepare(),
用于初始化消息队列以及Looper对象,紧接着调用 Looper.loop() 开始从消息队列中轮询,一旦有消息便将消息取出处理。
因为整个过程都执行在子线程中,所以当我们用在子线程中创建的Looper作为参数传给Handler时,其处理消息的代码就会执行在子线程中了。
以上便是 HandlerThread 的原理,主要还是利用 Handler,Message, Looper 之间的关系。
三 、自定义 HandlerThread:
当我们了解了其原理之后,其实我们也可以自定义自己 HandlerThread , 在线程之中处理消息。
现在我们自定义一个MyHandlerThread 同样继承 Thread。
1 、代码如下:
public class MyHandlerThread extends Thread {
private Handler asyncHandler;
private Looper mLooper;
public MyHandlerThread() {
}
@Override
public void run() {
Looper.prepare();
mLooper = Looper.myLooper();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
asyncHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
handlerThreadMessage(msg);
}
};
Looper.loop();
}
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;
}
//退出
public void quit() {
Looper looper = getLooper();
if(looper != null) {
looper.quit();
}
}
/**
* 发送消息
* @param message
*/
public void sendMessage(Message message) {
if(asyncHandler != null) {
Log.d("test","sendMessage");
asyncHandler.sendMessage(message);
}
}
/**
* 处理消息
* @param message
*/
private void handlerThreadMessage(Message message) {
Log.d("test","Message : "+message.obj+" Thread " +Thread.currentThread().getId());
}
}
2、用法也很简单:
定义变量:
private MyHandlerThread handlerThread;
在onCreate() 中初始化:
handlerThread = new MyHandlerThread();
handlerThread.start();
在需要异步时调用:
private void updateData() {
Message message = Message.obtain();
message.obj = "更新数据";
handlerThread.sendMessage(message);
}
这样我们便实现自定义 HandlerThread ,其中我们还可以根据需求封装不同发送消息的方法。
并且我们还将提交任务的代码和在子线程中处理任务的代码分开了,两块代码利用 MessageQueue 相连接,
那么这是不是也算是一种生产者消费者模式呢? 因为Handler 机制本身也算是一种生产者消费者模式啊。
四、
下一篇会讲解 Android 中另外一个可以实现异步的类: IntentService 。
IntentService 本身当然是一个 Service , 但是它可以做到完成任务后自动退出,下一篇一起看看其是怎么做到的。
以上是关于Android 并发二三事之 Handler 机制的妙用 HandlerThread的主要内容,如果未能解决你的问题,请参考以下文章
Redis二三事一把LOL的时间让你了解Redis的主从复制机制(超详细步骤图解)
Redis二三事一文了解Redis的哨兵机制(超详细步骤图解)