HandlerThread(子线程也可以有消息传递机制)
来源:互联网 发布:windows消息机制详解 编辑:程序博客网 时间:2024/03/29 13:51
知识点
HandlerThread,顾名思义,Handler+Thread,就是让子线程也能有一套和MainThread一样的Handler消息机制。
- HandlerThread本质上是一个线程类,它继承了Thread
- HandlerThread有自己的内部Looper对象,可以进行looper循环
- 通过获取HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务
- 创建HandlerThread后必须先调用HandlerThread.start()方法,Thread会先调用run方法,创建Looper对象
MainThread vs HandlerThread
MainThreadHandler的消息传递机制
- 用户启动一个应用,系统内部建立一个进程。
- 进程启动主线程Main Thread。
- Main Thread通过Looper建立一个消息队列Message Queue。
- 消息队列是存在于主线程中的,在主线程中开始无限循环。
- 每当有新的Message进来,消息队列就开始处理,如果没有,就执行return继续等待。
HandlerThread的消息传递机制
- 创建实例对象HandlerThread
- 启动HandlerThread的主线程Thread()
- 创建一个Handler,该Handler使用的是HandlerThread的looper,并传入一个自定义的的消息处理机制来处理
- 处理完毕调用MainThread创建的另外一个Handler去处理结果
区别:
平时使用Handler的时候系统内部已经帮我们创建好主线程和启动主线程了,而使用HandlerThread需要我们手动去创建一个属于HandlerThread的主线程并且去启动它
使用Handler的时候使用的是HandlerThread的looper而不是MainThread的looper
不使用handleMessage来处理,而是使用一个实现了Handler.Callback接口的自定义的处理方法
HandlerThread不能操作UI,所以处理完毕后还是需要使用主线程的Handler去处理UI
这个和直接在MainThread中new Thread的作用是一样的,只不过是HandlerThread比Thread多了一套和MainThread一样的消息处理机制,有利于串行地处理消息,不会造成并发。
如图所示:
一个点击下载图片的例子
package com.bourne.android_common.ServiceDemo;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.Bundle;import android.os.Handler;import android.os.HandlerThread;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.ImageView;import com.bourne.android_common.R;import com.bourne.common_library.utils.Logout;import java.io.BufferedInputStream;import java.io.IOException;import java.net.HttpURLConnection;import java.net.URL;public class HandlerThreadActivity extends AppCompatActivity { class ImageBean { private String url; private Bitmap bitmap; public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public Bitmap getBitmap() { return bitmap; } public void setBitmap(Bitmap bitmap) { this.bitmap = bitmap; } } /** * 图片地址集合 */ private String url[] = { "http://img0.imgtn.bdimg.com/it/u=1597254274,1405139366&fm=23&gp=0.jpg", "http://img0.imgtn.bdimg.com/it/u=3901634069,2243065451&fm=23&gp=0.jpg", "http://img4.imgtn.bdimg.com/it/u=1800624712,2677106110&fm=23&gp=0.jpg", "http://img0.imgtn.bdimg.com/it/u=2456066925,446683653&fm=23&gp=0.jpg", "http://img0.imgtn.bdimg.com/it/u=565155430,1247415230&fm=23&gp=0.jpg", "http://img4.imgtn.bdimg.com/it/u=2845715753,1348257911&fm=23&gp=0.jpg", "http://img3.imgtn.bdimg.com/it/u=3634032659,2514353810&fm=23&gp=0.jpg" }; private ImageView imageView; private HandlerThread handlerThread; private Thread loadImageThread; private int count = 0; /** * 处理UI */ Handler mainThreadHandler = new Handler() { @Override public void handleMessage(Message msg) { Logout.e("次数:" + msg.what); ImageBean imageBean = (ImageBean) msg.obj; imageView.setImageBitmap(imageBean.getBitmap()); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler_thread); imageView = (ImageView) findViewById(R.id.imageView); createHandlerThread(); } /** * 通过HandlerThread的方式加载 * @param view */ public void loadByHandlerThread(View view) { Handler.Callback callBack = new loadImageCallBack(); Handler handlerThreadHandler = new Handler(handlerThread.getLooper(), callBack); for (int i = 0; i < url.length; i++) { handlerThreadHandler.sendEmptyMessageDelayed(i, 1000 * i); } } /** * 通过Thread的方式加载 * @param view */ public void loadByThread(View view) { if (loadImageThread != null && !loadImageThread.isInterrupted()) { loadImageThread.interrupt(); } count = 0; loadImageThread = new Thread(new Runnable() { @Override public void run() { try { while (true) { Thread.sleep(1000); //在子线程中进行网络请求 Bitmap bitmap = downloadUrlBitmap(url[count]); ImageBean imageBean = new ImageBean(); imageBean.setBitmap(bitmap); imageBean.setUrl(url[count]); Message message = new Message(); message.what = count; message.obj = imageBean; count++; mainThreadHandler.sendMessage(message); //最后一张时停止加载 if (count >= url.length) { loadImageThread.interrupt(); } } } catch (InterruptedException e) { e.printStackTrace(); Logout.e("加载完毕,停止线程"); } } }); loadImageThread.start(); } /** * 处理下载图片 */ class loadImageCallBack implements Handler.Callback { @Override public boolean handleMessage(Message msg) { //在子线程中进行网络请求 Bitmap bitmap = downloadUrlBitmap(url[msg.what]); ImageBean imageBean = new ImageBean(); imageBean.setBitmap(bitmap); imageBean.setUrl(url[msg.what]); Message message = new Message(); message.what = msg.what; message.obj = imageBean; mainThreadHandler.sendMessage(message); return false; } } /** * 创建一个HandlerThread */ private void createHandlerThread() { //创建实例对象 handlerThread = new HandlerThread("downloadImage"); handlerThread.start(); } /** * 下载图片的网络请求 * * @param urlString * @return */ private Bitmap downloadUrlBitmap(String urlString) { HttpURLConnection urlConnection = null; BufferedInputStream in = null; Bitmap bitmap = null; try { final URL url = new URL(urlString); urlConnection = (HttpURLConnection) url.openConnection(); in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024); bitmap = BitmapFactory.decodeStream(in); } catch (final IOException e) { e.printStackTrace(); } finally { if (urlConnection != null) { urlConnection.disconnect(); } try { if (in != null) { in.close(); } } catch (final IOException e) { e.printStackTrace(); } } return bitmap; } @Override protected void onDestroy() { super.onDestroy(); //释放资源 handlerThread.quit(); }}
可以看到我们在onCreate的时候创建了一个HandlerThread,并开启它的线程。点击按钮的时候创建一个Handler,取HandlerThread的Looper,并完成线程的操作。操作完成之后,发消息给UI线程并改变UI。
布局文件
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_horizontal" android:orientation="vertical" tools:context="com.bourne.android_common.ServiceDemo.HandlerThreadActivity"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="loadByHandlerThread" android:text="使用HandlerThread下载图片" android:textAllCaps="false"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="loadByThread" android:text="使用Thread下载图片" android:textAllCaps="false"/> <ImageView android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content"/></LinearLayout>
源码解析
完整代码:
import android.os.Handler;import android.os.Looper;import android.os.Message;/** * */public class HandlerThread extends Thread { /** * 线程优先级 */ int mPriority; int mTid = -1; /** * 当前线程持有的Looper对象 */ Looper mLooper; public MyHandlerThread(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 MyHandlerThread(String name, int priority) { super(name); mPriority = priority; } /** *必要时可以自己去重写 */ protected void onLooperPrepared() { } @Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { //Looper对象将被创建 mLooper = Looper.myLooper(); //唤醒等待线程 notifyAll(); } //设置进程优先级 Process.setThreadPriority(mPriority); onLooperPrepared(); //开启looper循环语句 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; } 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; }}
/** * 线程优先级 */ int mPriority; int mTid = -1; /** * 当前线程持有的Looper对象 */ Looper mLooper; public MyHandlerThread(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 MyHandlerThread(String name, int priority) { super(name); mPriority = priority; } /** *必要时可以自己去重写 */ protected void onLooperPrepared() { }
这里初始化了Handler的对象,设置了优先级,mLooper是持有的Looper对象,onLooperPrepared是一个空实现,必要是可以自己重写。
@Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { //Looper对象将被创建 mLooper = Looper.myLooper(); //唤醒等待线程 notifyAll(); } //设置进程优先级 Process.setThreadPriority(mPriority); onLooperPrepared(); //开启looper循环语句 Looper.loop(); mTid = -1; }
run()方法中调用了Looper.prepare(),Loop.loop(),prepare()负责创建了一个Looper对象,并且把该对象放到了该线程范围内的变量中(sThreadLocal),在Looper对象的构造过程中,初始化了一个MessageQueue,作为该Looper对象成员变量。
loop()就开启了,不断的循环从MessageQueue中取消息处理了,当没有消息的时候会阻塞,有消息的到来的时候会唤醒。
/** * 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; } synchronized (this) { while (isAlive() && mLooper == null) { try { //等待唤醒 wait(); } catch (InterruptedException e) { } } } return mLooper; }
Looper的创建是在子线程中执行的,而Handler通过getLooper去获取mLooper的时候又是在主线程,怎么保证它们可以同步呢,原因是通过唤醒机制:
getLooper方法获取looper对象时会先先判断当前线程是否启动了,如果启动了会判断Looper对象有没有被创建,如果都都没有满足则会继续等待,直到Looper对象被创建并通过 notifyAll()方法唤醒等待线程,返回mLooper对象给Handler使用。
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; }
从源码可以看出当我们调用quit方法时,其内部实际上是调用Looper的quit方法而最终执行的则是MessageQueue中的removeAllMessagesLocked方法(Handler消息机制知识点),该方法主要是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送)还是非延迟消息。
当调用quitSafely方法时,其内部调用的是Looper的quitSafely方法而最终执行的是MessageQueue中的removeAllFutureMessagesLocked方法,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理完成后才停止Looper循环,quitSafely相比于quit方法安全的原因在于清空消息之前会派发所有的非延迟消息。最后需要注意的是Looper的quit方法是基于API 1,而Looper的quitSafely方法则是基于API 18的。
参考文章
Android 多线程之HandlerThread 完全详解
Android HandlerThread 完全解析
- HandlerThread(子线程也可以有消息传递机制)
- HandlerThread,子线程中处理Handler消息
- Handler消息传递机制(子线程中传递new Handler和主线程中new Handle传递消息)
- Android中消息机制初探(创建一个可以接收消息的子线程)
- Handler消息传递机制(四)子线程接收主线程发送的消息
- 举例子解释Handler消息传递机制
- 异步消息处理线程----HandlerThread
- 子线程handler与handlerThread
- 线程通信机制---共享内存:消息传递
- 消息机制、子窗口和父窗口的消息传递
- windows 消息机制、窗口过程与线程间消息传递
- windows 消息机制、窗口过程与线程间消息传递
- Android子线程其实也可以刷新UI。。。。
- Android消息通信机制(主线程发消息到子线程)
- 线程(HandlerThread)
- HandlerThread:带消息循环(Looper)的线程
- (4.1.21.1)android 主线程和子线程之间的消息传递
- android 主线程和子线程之间的消息传递
- 链表升序
- mysql_real_connect(): Access denied for user 'root'@'localhost' (using password: YES)
- maven 推荐网址
- JSP页面间传值、前后端传值及EL表达式
- 互联网产品“冷启动”问题及解决办法
- HandlerThread(子线程也可以有消息传递机制)
- 重写与重载的区别
- 二分查找法与链表算法
- ArcGIS平台中PostgreSQL数据连接配置总结(转)
- 反射机制-对象复制转换
- Flex效果
- 微信小程序开发—快速掌握组件及API的方法
- blade中设计中对于HttpResponse的封装处理
- Hibernate 初体验 之 配置Hibernate