福田手机网站建设,有后台支撑的网站建设合同,南昌seo网站推广,wordpress pcmsAndroid HandlerThread、Looper、MessageQueue 源码分析
简介
在 Android 开发中#xff0c;大家应该对 HandlerThread 有一定了解。顾名思义#xff0c;HandlerThread 是 Thread 的一个子类。与普通的 Thread 不同#xff0c;Thread 通常一次只能执行一个后台任务#x…Android HandlerThread、Looper、MessageQueue 源码分析
简介
在 Android 开发中大家应该对 HandlerThread 有一定了解。顾名思义HandlerThread 是 Thread 的一个子类。与普通的 Thread 不同Thread 通常一次只能执行一个后台任务如果需要执行多个任务就必须创建多个线程这样容易导致资源管理复杂甚至可能出现内存泄漏等问题。而 HandlerThread 有一个显著的特点它能够串行地执行多个任务。每个任务通过消息的形式排队执行线程池中的任务按顺序依次处理无需手动管理线程的生命周期或调度过程。我们只需通过 Handler 将任务发送到 HandlerThread 中它会自动按顺序执行极大简化了开发过程。使用 HandlerThread 的最大优势是它是单线程的因此不需要担心线程安全问题。实际上Android 源码中也有许多地方使用了 HandlerThread它在设计思想上值得我们学习和借鉴。接下来我们将通过源码进一步了解它的实现原理。
源码分析
HandlerThread内部持有一个Looper,Looper中持有一个消息队列MessageQueue
Looper
在了解HandlerThread前我们有必要先认识一下LooperLooper 是一个负责管理消息队列MessageQueue并循环处理其中消息的类。简单来说Looper 通过不断地从消息队列中取出消息并处理它们来实现线程的消息处理机制。每个线程都有自己的消息队列和 Looper通过 Looper线程能够不断地处理任务直到任务队列为空。接下来我们会涉及到它以下几个核心接口。
prepare() 是一个静态方法用来构建一个Looper对象quitAllowed表示这个Looper是否支持退出默认是支持像Android 主线程的Looper是不支持退出的 public static void prepare() {prepare(true);}loopOnce 静态方法顾名思义循环一次它的作用是从当前消息队列MessageQueue中取一条符合条件的消息进行分发操作
private static boolean loopOnce(final Looper me,final long ident, final int thresholdOverride) {Message msg me.mQueue.next(); // 取一条消息if (msg null) {//正常没消息队列会堵塞不会返回null所以这里是null肯定是已经要结束了这里返回false 退出looperreturn false;}//分发消息代码逻辑省略}loop() 静态方法启动循环在循环中不断调用loopOnce来处理消息
//关键代码public static void loop() {final Looper me myLooper();
//...for (;;) {if (!loopOnce(me, ident, thresholdOverride)) {//返回false 意味着循环结束return;}}}quit()和quitSafely() 退出looper前者是立即退出后者是处理完当前队列中所有消息后退出最终是调用消息队列MessageQueue对应的退出方法 public void quit() {mQueue.quit(false);
}public void quitSafely() {mQueue.quit(true);
}HandlerThread
既然它是一个线程那可以先从run方法入手
Overridepublic void run() {mTid Process.myTid();Looper.prepare();synchronized (this) {mLooper Looper.myLooper();notifyAll();}Process.setThreadPriority(mPriority);onLooperPrepared();Looper.loop();mTid -1;}
可以看出线程跑起来后先初始化了一个Looper然后启动死循环,HandlerThread在这里充当的作用是在子线程中开启死循环接受和分发消息 这里有个地方比较有意思在mLooper Looper.myLooper();后面它调用了notifyAll(),它起到了什么作用呢
getLooper()我们应该知道要把任务提交给HandlerThread执行需要借助Handler但是Handler的构造参数是需要传入一个Looper对象所以这里对外公开了获取Looper的接口
public Looper getLooper() {if (!isAlive()) {return null;}boolean wasInterrupted false;// If the thread has been started, wait until the looper has been created.synchronized (this) {while (isAlive() mLooper null) {try {wait();} catch (InterruptedException e) {wasInterrupted true;}}}/** We may need to restore the threads interrupted flag, because it may* have been cleared above since we eat InterruptedExceptions*/if (wasInterrupted) {Thread.currentThread().interrupt();}return mLooper;}这里是直接返回了在run中初始化的mLooper但是呢在非当前线程获取mLooper对象就会引发线程安全问题可能mLooper还没被初始化就调用了getLooper(),这样就有可能返回一个空的数据了所以官方在这里做了while循环并且使用了wait()堵塞等待上面run初始化完成后再notifyAll()这里
getThreadHandler() 就是公开给外面向当前HandlerThread插入消息的接口内部维护着一个Handler对象 NonNullpublic Handler getThreadHandler() {if (mHandler null) {mHandler new Handler(getLooper());}return mHandler;}quit()、quitSafely() 同样HandlerThread也有退出的方法其实现也是调用looper对应的函数退出
MessageQueue
由于关于消息分发逻辑在其他地方讲过这里只要分析退出队列的逻辑。 它实现了退出和安全退出的方法这两个操作有什么区别呢请看源码
next() looper每调用一次loopOnce内部就会调用messageQueue获取一条消息
Message next() {//只放关键代码for (; ; ) {//堵塞等待消息或者时机合适或主动唤醒nativePollOnce(ptr, nextPollTimeoutMillis);//...//符合分发条件的 返回这条消息给looperif (msg ! null) {//...return msg;} else {// No more messages.nextPollTimeoutMillis -1;}//...//退出状态 则释放队列结束循环if (mQuitting) {dispose();return null;}}
}
quit()实际逻辑由下面两个removeAllFutureMessagesLocked和removeAllMessagesLocked函数执行
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();}//把队列的消息标记完成后唤醒上面next的堵塞位置nativePollOnce执行剩下的退出逻辑nativeWake(mPtr);}
}
//移除所有消息
private void removeAllMessagesLocked() {//mMessages 在这里是队列的头Message p mMessages;while (p ! null) {//从头开始把所有消息标记为回收状态Message n p.next;p.recycleUnchecked();p n;}mMessages null;//队头消息置空next()执行时会判断这个为空直接退出队列
}//移除所有未来消息退出这一瞬间之前提交的消息保留继续执行
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);}}
}
从上面可以看出安全退出时队列会把所有未来消息移除掉并且不再接受新的消息队列中剩下的消息会继续被looper取一直到取完为止然后结束队列退出循环而普通退出就会把队列中所有消息移除然后紧接着结束队列退出循环
quit和quitSafely 应用场景有哪些呢
;//把所有未来消息标记为回收状态 } while (n ! null); } } } 从上面可以看出安全退出时队列会把所有未来消息移除掉并且不再接受新的消息队列中剩下的消息会继续被looper取一直到取完为止然后结束队列退出循环而普通退出就会把队列中所有消息移除然后紧接着结束队列退出循环### quit和quitSafely 应用场景有哪些呢
举一个简单的例子在Android 使用Room操作数据库由于操作数据库需要在子线程所以我们可以构造一个HandlerThread专门处理操作数据库的任务如果操作过程非常耗时然后又要关闭数据库