Android 并发二三事之 Handler 机制的妙用 HandlerThread
2016-11-17 11:25
525 查看
Android 并发第五篇
本篇会讲解如何利用 HandlerThread 实现异步操作。
HandlerThread 本身其实就是一个 Thread ,但是其内部还利用 Handler 机制。
对于提交的任务(或者说是信息 Message)依次处理。
所以在介绍 HandlerThread 原理以及如果使用之前,会首先说一个 Handler 异步机制。
当然 Handler, Looper, Message 之间的关系相信很多人都已经很熟悉了,这里会只着重介绍和本节相关的内容。
一 、Handler 机制:
1、 我们都知道,在子线程中通知主线程更新UI界面,需要使用Handler。
一般我们就直接在 Activity 中直接 初始化一个Handler 对象,像这样:
重写 handlerMessage() 方法,然后利用 uiHandler 对象在子线程中发送消息。
2、 或者我们也可以直接在主线程直接 new 一个 Handler 对象:
但在子线程中 new Handler()需要这样:
然后在子线程中:
Handler 源码:
在handler 中会将Runnable 对象赋值给 message.callback 属性,封装成Message,调用 sendMessageDelaye() 将消息发送出去。
sendMessageDelaye() 方法最后在辗转几次后最终会调用sendMessageAtTime() 将消息放到消息队列中。
而 Handler.sendMessage()的源码为:
所以本质上,无论是利用Handler.sendMessage(),还是 Handler.post() 都是将消息添加到消息队列之中。
那么为什么在子线程中需要传入 MainLooper , 而主线程却不需要呢?
首先我们是要在子线程中通知主线程,那么我们便需要代码执行在UI 线程中。
如果在子线程中直接:
会抛出异常:
Can’t create handler inside thread that has not called Looper.prepare()
我们可以看一下源码:
Handler 源码:
我们能够看到,无参的构造方法,会调用public Handler(Callback callback, boolean async) 。
在这个方法中,调用 Looper.myLooper(); 获取 Looper 对象,之所以抛出异常,一定是其为null了。
那么为什么没有获取到Looper对象呢?
接下来看Looper源码:
从 ThreadLocal 变量中获取当前线程的值,那么这个值是在哪里设置的呢?
在Looper.prepare() 中设置的。
也就是说在子线程中直接 new Handler() 对象,需要先调用Looper.prepare() 方法。
而在主线程中是不需要的,因为在应用初始化时,已经调用 Looper.prepare() 了。
而Looper 中还有一个方法:Looper.loop() 方法
Looper.loop() 内包含一个死循环,不断的从队列中获取消息,如果没有消息时,会阻塞。
Looper.loop() 调用了 Handler.dispatchMessage() 方法:
在dispatchMessage() 方法中会调用 handleMessage() 方法,或者调用 handleCallback() 方法处理我们利用Handler post Runnable
所封装的消息。
3 、所以通过以上总结
我们知道如果Looper.loop()是在主线程中调用的,那么我们重写的 handlerMessage() 方法
和封装在消息中的 Runnable 都会在主线程中执行。
反过来说,如果Looper.prepare() 以及 Looper.loop() 是在子线程中调用的,那么基于子线程的Looper,所创建的Handler
所发送的消息都将会执行在子线程中,HandlerThread 便是利用了这个原理。
二 、HandlerThread
1 、我们首先看一下 HandlerThread 如何使用:
2 、结果:
在这里 HandlerThread 需要和 Handler 一起配合使用,HandlerThread 提供一个在子线程中创建的 Looper 。
按照之前的推论,Looper.prepare(), 以及 Looper.loop() 都是执行在子线程中,那么在处理消息时也必然执行在子线程中。
所以其实现了异步的效果。
3 、接下来看一下 HandlerThread 的源码:
4 、总结
可以看到 HandlerThread 本身是一个 Thread 所以其 run() 方法会执行在子线程中。在 run() 方法中首先调用了Looper.prepare(),
用于初始化消息队列以及Looper对象,紧接着调用 Looper.loop() 开始从消息队列中轮询,一旦有消息便将消息取出处理。
因为整个过程都执行在子线程中,所以当我们用在子线程中创建的Looper作为参数传给Handler时,其处理消息的代码就会执行在子线程中了。
以上便是 HandlerThread 的原理,主要还是利用 Handler,Message, Looper 之间的关系。
三 、自定义 HandlerThread:
当我们了解了其原理之后,其实我们也可以自定义自己 HandlerThread , 在线程之中处理消息。
现在我们自定义一个MyHandlerThread 同样继承 Thread。
1 、代码如下:
2、用法也很简单:
定义变量:
在onCreate() 中初始化:
在需要异步时调用:
这样我们便实现自定义 HandlerThread ,其中我们还可以根据需求封装不同发送消息的方法。
并且我们还将提交任务的代码和在子线程中处理任务的代码分开了,两块代码利用 MessageQueue 相连接,
那么这是不是也算是一种生产者消费者模式呢? 因为Handler 机制本身也算是一种生产者消费者模式啊。
四、
下一篇会讲解 Android 中另外一个可以实现异步的类: IntentService 。
IntentService 本身当然是一个 Service , 但是它可以做到完成任务后自动退出,下一篇一起看看其是怎么做到的。
本篇会讲解如何利用 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
- Android 并发二三事之 IntentService Handler 机制再次来袭
- Android Handler 异步消息处理机制的妙用 创建强大的图片加载类
- Android Handler 异步消息处理机制的妙用 创建强大的图片加载类
- Android Handler 异步消息处理机制的妙用 创建强大的图片加载类
- Android Handler 异步消息处理机制的妙用 创建强大的图片加载类
- Android Handler 异步消息处理机制的妙用 创建强大的图片加载类
- Android Handler 异步消息处理机制的妙用 创建强大的图片加载类
- Android Handler 异步消息处理机制的妙用 创建强大的图片加载类
- Android Handler 异步消息处理机制的妙用 创建强大的图片加载类
- Android Handler 异步消息处理机制的妙用 创建强大的图片加载类【转】
- Android 异步消息处理机制的妙用(二) Handler 创建强大的图片加载类
- Android Handler 异步消息处理机制的妙用 创建强大的图片加载类
- Android Handler 异步消息处理机制的妙用 创建强大的图片加载类
- HandlerThread、Loop、MessageQueue、Handler、Message组成的Android的消息处理机制
- Android Handler 异步消息处理机制的妙用 创建强大的图片加载类
- Android Handler 异步消息处理机制的妙用 创建强大的图片加载类
- Android Handler 异步消息处理机制的妙用 创建强大的图片加载类
- Android Handler 异步消息处理机制的妙用 创建强大的图片加载类
- Android Handler 异步消息处理机制三:妙用手法 创建强大的图片加载类