- HandlerThread是什么?⭐⭐⭐⭐⭐
- HandlerThread原理和使用场景?⭐⭐⭐
HandlerThread是什么?
在安卓开发中,如果需要执行耗时操作,则可以开启子线程来完成,然而手动创建销毁线程又麻烦又消耗系统性能,因此可以使用线程池来完成。如果还需要在线程中使用Handler异步消息机制,或者需要实现子线程和子线程之间的通讯(Handler是主线程和子线程之间的通讯),那么就可以用HandlerThreaad。
HandlerThread是Google封装好的一个类,它的内部有自己的Looper对象,可以进行Loop轮询,用于执行多个耗时操作,而不需要多次开启线程,本质是使用Handler和Looper实现的。
HandlerThread的本质: 继承Thread类 & 封装Handler类
HandlerThread怎么使用
如果我们需要使用HandlerThread来读取一个大文件的内容,可以这么写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
HandlerThread mHandlerThread = new HandlerThread("handlerThread");
mHandlerThread.start();
Handler workHandler = new Handler( mHandlerThread.getLooper() ) { @Override public boolean handleMessage(Message msg) { ... return true; } });
Message msg = Message.obtain(); msg.what = 2; msg.obj = "B";
workHandler.sendMessage(msg);
mHandlerThread.quit();
|
使用mHandlerThread的looper创建的mThreadHandler,里面的handleMessage是可以进行耗时操作的,因为它是执行在mHandlerThread所在的子线程。因此其优点是不会阻塞主线程,但是多任务时也需要有序执行,导致执行效率低。因此HandlerThread比较适合耗时不且不会产生较大的阻塞,比如读取文件,操作数据库等,至于网络IO操作这种可能有较大阻塞等,HandlerThread并不适合。
HandlerThread的使用方法:
- 创建HandlerThread实例
- 执行start函数来启动HandlerThread线程
- 将HandlerThread和Handler绑定
- 然后执行startHandlerThread(),最后退出HandlerThread
内存泄漏
如果没有及时退出会造成内存泄漏:
- 当Handler消息队列 还有未处理的消息 / 正在处理消息时,存在引用关系: “未被处理 / 正处理的消息 -> Handler实例 -> 外部类”
- 若出现 Handler的生命周期 > 外部类的生命周期 时(即 Handler消息队列 还有未处理的消息 / 正在处理消息 而 外部类需销毁时),将使得外部类无法被垃圾回收器(GC)回收,从而造成 内存泄露
解决方案
静态内部类
原理:静态内部类不默认持有外部类的引用,从而使得 “未被处理 / 正处理的消息 -> Handler实例 -> 外部类” 的引用关系 不存在。
具体方案:将Handler的子类设置成静态内部类。此外,还可使用WeakReference弱引用持有外部类,保证外部类能被回收。因为:弱引用的对象拥有短暂的生命周期,在垃圾回收器线程扫描时,一旦发现了具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存
解决代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| public class MainActivity extends AppCompatActivity { public static final String TAG = "carson:"; private Handler showhandler;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
showhandler = new FHandler(this);
new Thread() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Message msg = Message.obtain(); msg.what = 1; msg.obj = "AA";
showhandler.sendMessage(msg); } }.start();
}
private static class FHandler extends Handler{
private WeakReference<Activity> reference;
public FHandler(Activity activity) { reference = new WeakReference<Activity>(activity); }
@Override public void handleMessage(Message msg) { switch (msg.what) { case 1: Log.d(TAG, "收到线程1的消息"); break; case 2: Log.d(TAG, " 收到线程2的消息"); break;
} } } }
|
当外部类结束生命周期时,清空Handler内消息队列
原理:不仅使得 “未被处理 / 正处理的消息 -> Handler实例 -> 外部类” 的引用关系 不复存在,同时 使得 Handler的生命周期(即 消息存在的时期) 与 外部类的生命周期 同步
具体方案:当 外部类(此处以Activity为例) 结束生命周期时(此时系统会调用onDestroy()),清除 Handler消息队列里的所有消息(调用removeCallbacksAndMessages(null))
具体代码
1 2 3 4 5 6
| @Override protected void onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null); }
|
为了保证Handler中消息队列中的所有消息都能被执行,此处推荐使用解决方案1解决内存泄露问题,即 静态内部类 + 弱引用的方式
源码分析
步骤1:创建HandlerThread的实例对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
HandlerThread mHandlerThread = new HandlerThread("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; } ... }
|
- HandlerThread类继承自Thread类
- 创建HandlerThread类对象 = 创建Thread类对象 + 设置线程优先级 = 新开1个工作线程 + 设置线程优先级
步骤2:启动线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
mHandlerThread.start();
@Override public void run() { mTid = Process.myTid();
Looper.prepare();
synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); }
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1; }
|
- 为当前工作线程(即步骤1创建的线程)创建1个Looper对象 & MessageQueue对象
- 通过持有锁机制来获得当前线程的Looper对象
- 发出通知:当前线程已经创建mLooper对象成功
- 工作线程进行消息循环,即不断从MessageQueue中取消息 & 派发消息
步骤3:创建工作线程Handler & 复写handleMessage()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
Handler workHandler = new Handler( handlerThread.getLooper() ) { @Override public boolean handleMessage(Message msg) { ... return true; } });
public Looper getLooper() { if (!isAlive()) { return null; } synchronized (this) { while (isAlive() && mLooper == null) { try { wait(); } catch (InterruptedException e) { } } } return mLooper; }
|
- 在获得HandlerThread工作线程的Looper对象时存在一个同步的问题:只有当线程创建成功 & 其对应的Looper对象也创建成功后才能获得Looper的值,才能将创建的Handler 与 工作线程的Looper对象绑定,从而将Handler绑定工作线程
- 解决方案:即保证同步的解决方案 = 同步锁、wait和 notifyAll,即 在run()中成功创建Looper对象后,立即调用notifyAll通知 getLooper()中的wait结束等待&返回run()中成功创建的Looper对象,使得Handler与该Looper对象绑定
步骤4:使用工作线程Handler向工作线程的消息队列发送消息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
Message msg = Message.obtain(); msg.what = 2; msg.obj = "B"; workHandler.sendMessage(msg);
|
步骤5:结束线程,即停止线程的消息循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
|
mHandlerThread.quit();
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; }
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(); } nativeWake(mPtr); } }
private void removeAllMessagesLocked() { Message p = mMessages; while (p != null) { Message n = p.next; p.recycleUnchecked(); p = n; } mMessages = null; }
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); } }
|
总结
- HandlerThread 是内部会创建Looper的线程,因此只能作为子线程使用;
- HandlerThread 需要配合 Handler 使用,HandlerThread 的耗时操作在handleMessage()中执行,因为此时执行的是在HandlerThread所在的子线程执行的,但不能像普通线程一样在run()方法中进行,因为HanderThread的run()方法已经被写死。
- HandlerThread和Handler绑定之前,必须先调用 mHandlerThread.start() 让 run() 方法跑起来。
- 创建HandlerThread的实例对象
- 创建HandlerThread类对象=创建Thread类对象+设置线程优先级
- 即:新开1个工作线程+设置线程优先级
- 启动HandlerThread工作线程
- 为当前工作线程(即步骤1创建的线程)创建1个Looper对象&MessageQueue对象
- 通过持有锁机制来获得当前线程的Looper对象
- 向getLooper()的wait()发出通知:当前线程已经创建mLooper对象成功
- 工作线程进行消息循环,即不断从MessageQueue中取消息 & 派发消息
- 创建工作线程的Handler (需复写handleMessage())
- 将Handler关联到HandlerThread的Looper对象,从而绑定到HandlerThread的工作线程
- 使用工作线程Handler向工作线程的消息队列发送消息
- 在工作线程中,当消息循环时取出对应消息 &在工作线程执行相关操作
- 此处的源码即Handler的源码
- 结束工作线程
- 即停止线程的消息循环
- 可使用安全/不安全的方式结束:quit()/quitSafely()
- (安全与否(quitSafe() 或 quit()),在于该方法移除消息、退出循环时是否在意当前队列是否正在处理消息)
- 停止消息循环的本质:遍历Message链表、移除所有信息的回调&重置为null