Android进阶2:线程和线程池(2)—— HandlerThread原理解析
HandlerThread是属于Android异步线程模块的一部分,上一篇是AsyncTask的源码:
Android进阶2:线程和线程池(1)—— AsycTask原理解析
如果你没看过handler消息机制,建议先学习下handler消息机制:
Android进阶1:Android的消息机制
记得之前刚接触android的之后,只知道HandlerThread内部原理:Handler + Thread, 始终没看过源码,最近学习到了android线程和线程池模块,自然而然的HandlerThread源码是必须看的。
HandlerThread的简单使用
老规矩,得先会使用HandlerThread再说别的。
需求:使用HandlerThread开启一个子任务,在子任务中,发送消息,在UI主线程接收消息,并显示在TextView上。
public class MainActivity extends AppCompatActivity { private Handler mainHandler = new Handler() { //主线程的handler @Override public void handleMessage(Message msg) { super.handleMessage(msg); String description = msg.getData().getString("description"); textView.setText(description); } }; private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = findViewById(R.id.image_view); HandlerThread handlerThread = new HandlerThread("test-handler-thread"); handlerThread.start(); Handler childHandler = new Handler(handlerThread.getLooper()) { @Override public void handleMessage(Message msg) { super.handleMessage(msg); //运行在子线程 String description = msg.getData().getString("description"); Message message = mainHandler.obtainMessage(); Bundle data = message.getData(); data.putString("description", description); mainHandler.sendMessage(message); //通过主线程的handler发送消息 } }; for (int i = 0; i < 10; i++) { //子线程childHandler 发送消息 Message message = childHandler.obtainMessage(); Bundle data = new Bundle(); data.putString("description","hello world " + i); message.setData(data); childHandler.sendMessageDelayed(message, 1000 * i); } }}
通过上述demo是对handleThread的简单使用,并且我们得到的信息是:
- 通过handlerThread.getLooper()新建了绑定的childHandler对象
- childHandler发送消息,但是在childHandler内部又通过mainHandler发送了消息,真正更新UI是在mainHandler中,为什么不在childHandler中更新UI呢? 原因只有一个:那就是childHandler中的handleMessage是运行在子线程中的,我们知道handleMessage方法运行的线程和创建该handler的Looper的线程是一致的,换句话说:handlerThread.getLooper()获得的是子线程的Looper。 到底是不是子线程的Looper呢?看源码
HandlerThread的
1: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; //当前线程的Looper对象 private @Nullable Handler mHandler; //传递的handler ......}
两个注意点:
- HandlerThread 继承自 Thread类,HandlerThread本身可以说是Thread的子类,也就是可以处理耗时操作。
成员变量含有Looper和Handler对象,也就是处理消息啦。
再看下Google给出的方法描述:创建一个含有Looper的子线程,该Looper用来创建一个Handler对象。无论如何都必须通过start开启。
通过Google的描述,我们使用的HandlerThread的步骤是不是清晰了?
- HandlerThread handlerThread = new HandlerThread(“test-handler-thread”);
- handlerThread.start(); 开启任务
- Handler childHandler = new Handler(handlerThread.getLooper()) ,通过HandlerThread的成员变量Looper创建出关联的handler。
接着往下看,构造函数:
HandlerThread构造方法
/** * @param name the name of the new thread * 线程名称 */ public HandlerThread(String name) { super(name); mPriority = Process.THREAD_PRIORITY_DEFAULT; } public HandlerThread(String name, int priority) { super(name); mPriority = priority; }
HandlerThread只有两个构造函数,我们的demo使用的是第一种创建方式,name参数表示线程的名字;
对象创建好了该运行了,那么就得看下start源码了
HandlerThread启动线程
通过查找,并没有在HandlerThread的源码内找到start()的复写,也就是说执行的是Thread的start方法。再想一下,start()方法调用之后,run()方法就调用了,来看下run()的源码:
@Override public void run() { //此时就运行在子线程了! mTid = Process.myTid(); Looper.prepare(); // 创建子线程的Looper synchronized (this) { mLooper = Looper.myLooper(); //获取子线程的Looper notifyAll(); //注意点1:唤醒所有等待 } Process.setThreadPriority(mPriority); //设置线程优先级 onLooperPrepared(); //注意点2:looper准备工作 Looper.loop(); //开启Looper调度 mTid = -1; }
通过上述代码可以看出,run方法主要做的工作就是:创建Looper,并且开启Looper的消息循环。有两个注意点:
- 注意点1:唤醒等待线程, 唤醒的是谁呢?下文讲述
注意点2:onLooperPrepared()方法,HandlerThread暴露这个方法,我们的知晓,我们可以在消息Looper开始消息调度前,复写此方法多一些事情。
再回过头看demo创建子线程的handler,看到了handlerThread.getLooper(), 看下源码:
public Looper getLooper() { if (!isAlive()) { //线程是否存活 return null; } // If the thread has been started, wait until the looper has been created. //如果改线程已经存活,就等到Looper创建好。 synchronized (this) { while (isAlive() && mLooper == null) { //死循环,等待创建好Looper对象 try { wait(); //等待唤醒 } catch (InterruptedException e) { } } } return mLooper; //返回Looper对象 }
此方法返回的是Looper对象,然后先判断线程是否存活,然后判断mLooper是否为null , 如果looper是null,就休眠等待唤醒,休眠? 那什么时候唤醒呢? 还记得run()方法里面的唤醒等待吧? 就是线程间Looper,然后唤醒等待,确保创建使用Handler之前必须有Looper。由于该Looper对象创建在子线程中,所有通过该Looper为参数创建的Handler内部的handlerMessage也是运行在子线程中。可以这样理解:上述demo,发送消息是在主线程,但是执行消息是在子线程,刚好跟我们常用的handler相反。
HandlerThread退出
退出有两种方式:
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; }
这两种退出方式是不是很熟悉? 没错,就是Looper的两种退出方式,内部调用的是MessageQueue的退出。
至此流程走完了,梳理一下:
创建HandleThread —> start启动 —> 在run方法内创建Looper对象,并唤醒等待 —> 通过run创建的Looper对象,创建一个运行在子线程的handler对象 —> 在子线程的handleMessage方法内,处理消息。
HandlerThread和普通Thread的区别:
Thread: run()中执行耗时操作,执行一次之后就结束,
HandlerThread:run()方法是无限循环的,阻塞等待,通过子线程为参数创建的Handler发送消息,执行任务。因此在明确不需要HandlerThread时,要通过quit或者quitSafely终止线程的执行。
通过上述分析,可以确定的是,HandlerThread原理实际上就是handler + Thread的封装使用
本文感谢《Android开发艺术探索》
如果发现错误,欢迎指正,以便纠正,谢谢哈 = - =
Android开发交流群: 543671130
更多相关文章
- android获取当前运行Activity名字的方法
- Android Handler ExecutorService(线程池) 缓存模式
- android画任意曲线时,去除移动过程中出现莫名直线的方法
- Android开发包下载(包括开发所需所有安装包的下载方法与地址)
- Android获取系统时间方法的总结
- Intent传递对象的两种方法(Serializable,Parcelable)
- [置顶] Android之ContextMenu的使用方法以及与OptionMenu的区别
- INSTALL_FAILED_CONFLICTING_PROVIDER 错误解决方法