多线程详解——HandlerThread工作原理(源码详解)

目录

一、HandlerThread简介

1. HandlerThread产生背景

2. HandlerThread 的特点

二、HandlerThread用法

1、通过callback创建

2、通过Looper创建

3、举例

三、HandlerThread生命周期管理

四、HandlerThread API及源码分析

run方法

getLooper()方法

quit和quitSafely方法


一、HandlerThread简介

1. HandlerThread产生背景

开启Thread子线程进行耗时操作

多次创建和销毁线程是很耗系统资源的。

(为什么呢,因为java的线程是依托操作系统,线程的创建和销毁以及频繁切换都涉及到用户态和内核态的切换,以及线程上下文的保存和恢复,所以是比较耗资源的。)

既然已经有Handler可以实现线程间通信,为什么又设计了HandlerThread?

HandlerThread通过字面意思我们可以看到,它是Handler+Thread,那么我们猜测它应该实现了Handler和Thread功能,到底是不是呢,我们向下看。

首先,HandlerThread继承了Thread类,也就是说HandlerThread可以创建一个新的线程。

其次,HandlerThread内封装了Handler类,并自动创建了Looper和MessageQueue,也就是说我们使用HandlerThread线程时,不需要手动创建Looper。

优点:

之前我们使用Handler时,必须自己创建线程,并且自己创建Looper等相关的功能,而HandlerThread则提供了一种方便的方式,相当于内部已经集成了这些功能,不需要我们再手动创建Looper。

在Android开发中,通常在主线程(也称为UI线程)中执行耗时操作会导致界面卡顿甚至ANR(Application Not Responding)错误。为了避免这种情况,可以使用HandlerThread。

HandlerThread是一个带有Looper的线程,可以用来处理消息队列中的消息。通过HandlerThread,可以在后台线程执行耗时操作,然后通过Handler将结果发送回主线程更新UI,从而提高应用的性能和流畅度。

使用HandlerThread的好处包括:

  1. 避免在主线程执行耗时操作导致界面卡顿
  2. 可以轻松地在后台线程执行网络请求、数据库操作等耗时任务
  3. 可以方便地进行线程间的通信和数据传递
  4. 可以更好地控制线程的生命周期和执行顺序

总之,使用HandlerThread可以帮助提高应用的性能和用户体验,是Android开发中常用的线程处理方式之一。

2. HandlerThread 的特点

本质上就是封装了Handler的线程

  1. HandlerThread 本质是一个线程类,它继承自Thread
  2. HandlerThread 有自己的内部looper对象,可以进行loop循环
  3. 通过HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务
  4. 不会阻塞,减少了对性能的消耗,缺点是不能同时进行多任务的处理,需要等待进行,效率低。

这里的不会阻塞,指的是非UI线程,又想使用消息机制的那些操作。通过HandlerThread来执行是最合适的。

     5.与线程池并发不同,HandlerThread是一个串行队列,HandlerThread只有一个线程。

HandlerThread是一个轻量级的异步类,可以实现多线程,并且可以实现线程间的通信(HandlerThread主要应用是实现主线程到子线程的通信,子线程到主线程通信可以通过Handler机制)。

HandlerThread继承自Thread,内部实现了初始化了Looper,并创建了消息队列,接着调用了Looper.loop()开启了消息循环,这样HandlerThread就可以处理通过Handler传递过来的Message了,因为HandlerThread中的run方法是无限循环,当有消息过来时处理消息,没有消息时就会阻塞。当明确不需要HandlerThread时,可以调用quit或者quitSafely (API 18以上使用)来尝试终止线程。

二、HandlerThread用法

1、通过callback创建

    1.创建HandlerThread
    HandlerThread handlerThread = new HandlerThread("handlerThread_name");
 
    //2.必须先开启线程
    handlerThread.start();
 
    /**
     * 3.设置callback
     * 该callback运行于子线程
     */
    class ChildCallback implements Handler.Callback {
        @Override
        public boolean handleMessage(Message msg) {
            //在子线程中进行相应的网络请求
 
            //通知主线程去更新UI
            mUIHandler.sendMessage(msg1);
            return false;
        }
    }
 
    //4.子线程Handler
    Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());

2、通过Looper创建

使用步骤:
//1、创建HandlerThread对象,即创建了一个新的线程,参数为线程名,仅仅是标记线程用的
HandlerThread mHandlerThread = new HandlerThread("mHandlerThread");
//2、开启线程,第一步创建了一个新的线程,此处开启线程。
mHandlerThread.start();
//3、创建Handler,并重写handleMessage()方法
    //new Handler(mHandlerThread.getLooper()),即把该Handler绑定到mHandlerThread线程的Looper,
    //进而绑定到了线程mHandlerThread
Handler mHandlerInHandlerThread = new Handler(mHandlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
 	        //对信息的相关处理操作               
                //在子线程mHandlerThread中运行   
                super.handleMessage(msg);
            }
        };
//4、创建消息并发送消息
    //在主线程中
Message msg = Message.obtain();
//主线程向子线程mHandlerThread发送消息通信
mHandlerInHandlerThread.sendMessage(msg);
//5、结束线程。之前开启线程,当工作结束不再使用该线程时,应该结束该线程
    //即停止了线程的消息循环
    mHandlerThread.quit();

通过使用步骤我们可以看到,HandlerThread实现的功能主要就是主线程向子线程通信,另外可以在使用Handler实现子线程到主线程的通信,进而就可以实现主线程到子线程间的双向通信。

3、举例

handlerThread = new HandlerThread("handler_thread");
handlerThread.start();
//在主线程初始化,传入的是HandlerThread中的Looper
workHandler = new Handler(handlerThread.getLooper()) {
     @Override
     public void handleMessage(Message msg) {
         //Handler是在HandlerThread所在的子线程线程中处理Message
         switch (msg.what) {
             case MSG_UPDATE:
                 if (isStop) return;
                 try {
                     String result = String.valueOf(new Random().nextInt(9000) + 1000);
                     //通过uiHandler对象传送消息到主线程,更新UI
                     sendMsg(UI_UPDATE, result, uiHandler);
                     //延迟2秒
                     Thread.sleep(2000);
                     //循环发送消息
                     workHandler.sendEmptyMessage(MSG_UPDATE);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 break;
         }
     }
 };

首先,初始化HandlerThread并通过handlerThread.getLooper()关联一个在UI线程初始化的workHandler,这样就可以通过workHandler在主线程中向HandlerThread中发送消息了,这里要注意一下,workHandler 中的handleMessage()方法是在HandlerThread子线程中执行的,为什么会在子线程中执行呢?

因为在初始化workHandler 时传入的是HandlerThread的Looper,而workHandler 是把消息发送到HandlerThread中去,HandlerThread在执行Looper.loop()方法后会循环取出消息并处理消息,所以workHandler 中的handleMessage()方法是在HandlerThread子线程中执行的,那么处理完消息后怎么更新到UI线程呢?

//在主线程初始化,传入的是主线程中的Looper
 uiHandler = new Handler(Looper.getMainLooper()) {
     @Override
     public void handleMessage(Message msg) {
         //在主线程中处理Message
         switch (msg.what) {
             case UI_UPDATE:
                 //更新验证码
                 String result = (String) msg.obj;
                 tv_random.setText(result);
                 break;
         }
     }
 };

我们在主线程中初始化了另一个uiHandler 并传入了主线程的Looper,所以此时最后处理消息的回调方法handleMessage()是在主线程中执行的,当HandlerThread处理完逻辑后,通过uiHandler把结果发到主线程中,就可以愉快地来更新UI了,最后别忘了关闭线程:

@Override
 protected void onDestroy() {
     if (SDK_INT >= 18) {
         handlerThread.quitSafely();
     } else {
         handlerThread.quit();
     }
     super.onDestroy();
 }

三、HandlerThread生命周期管理

使用HandlerThread完成任务后,要记得调用quit方法退出线程。避免出现内存泄漏问题!!!

四、HandlerThread API及源码分析

run方法

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {//同步代码块,确保同一时刻只有一个线程可以访问mLooper
            mLooper = Looper.myLooper();
            notifyAll();//通知当前等待线程,主要是getLooper()方法里的wait方法
        }
        Process.setThreadPriority(mPriority);//设定线程优先级
        onLooperPrepared();//空方法,留个用户自己重写
        Looper.loop();//初始化数据接收,开启循环。
        mTid = -1;
    }

getLooper()方法

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
 
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {//同步代码块,如果looper没有创建成功,就一直阻塞,直到looper创建完毕。
            while (isAlive() && mLooper == null) {
                try {
                    //这个是没有设置TimeOut的Object:wait方法,无限期等待,必须是其他线程调用notify或者notifyAll才能唤醒。
                    //当run方法的mLooper对象创建完毕时,就会调用notifyAll方法唤醒。
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

quit和quitSafely方法

    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;
    }

quitsafely比quit更安全,各自的实现原理如下。

    //Looper.java
    public void quit() {
        mQueue.quit(false);
    }
    public void quitSafely() {
        mQueue.quit(true);
    }
 
    //MessageQueue.java
    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();
            }
 
            // We can assume mPtr != 0 because mQuitting was previously false.
            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原理、使用实例、源码详细解析-优快云博客

https://zhuanlan.zhihu.com/p/424513321

HandlerThread相关问题总结 - cfdroid - 博客园

为什么要使用handlerthread - 问答 - 亿速云

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闲暇部落

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值