专注分享 Android开发 干货

CSDN签约作者、稀土掘金专栏作者 & 简书推荐作者

Android多线程:一步步带你源码解析HandlerThread

前言

  • 多线程的应用在Android开发中是非常常见的,常用方法主要有:

    1. 继承Thread类
    2. 实现Runnable接口
    3. Handler
    4. AsyncTask
    5. HandlerThread
    6. IntentService
  • 今天,我将全面解析多线程中 HandlerThread的源码

由于本文涉及多线程知识和Handler源码解析,所以阅读本文前建议先看:
Android开发:Handler异步通信机制全面解析(包含Looper、Message Queue)


目录

示意图


1. 简介

示意图


2. 工作原理

内部原理 = Thread类 + Handler类机制,即:

  • 通过继承Thread类,快速地创建1个带有Looper对象的新工作线程
  • 通过封装Handler类,快速创建Handler & 与其他线程进行通信

3. 源码分析

// 步骤1:创建HandlerThread实例对象
// 传入参数 = 线程名字,作用 = 标记该线程
   HandlerThread mHandlerThread = new HandlerThread("handlerThread");

// 步骤2:启动线程
   mHandlerThread.start();

// 步骤3:创建工作线程Handler & 复写handleMessage()
// 作用:关联HandlerThread的Looper对象、实现消息处理操作 & 与其他线程进行通信
// 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
  Handler workHandler = new Handler( handlerThread.getLooper() ) {
            @Override
            public boolean handleMessage(Message msg) {
                ...//消息处理
                return true;
            }
        });

// 步骤4:使用工作线程Handler向工作线程的消息队列发送消息
// 在工作线程中,当消息循环时取出对应消息 & 在工作线程执行相关操作
  // a. 定义要发送的消息
  Message msg = Message.obtain();
  msg.what = 2; //消息的标识
  msg.obj = "B"; // 消息的存放
  // b. 通过Handler发送消息到其绑定的消息队列
  workHandler.sendMessage(msg);

// 步骤5:结束线程,即停止线程的消息循环
  mHandlerThread.quit();
  • 下面,我将根据上述使用步骤进行源码分析

步骤1:创建HandlerThread的实例对象

/**
  * 具体使用
  * 传入参数 = 线程名字,作用 = 标记该线程
  */ 
   HandlerThread mHandlerThread = new HandlerThread("handlerThread");

/**
  * 源码分析:HandlerThread类的构造方法
  */ 
    public class HandlerThread extends Thread {
    // 继承自Thread类

        int mPriority; // 线程优先级
        int mTid = -1; // 当前线程id
        Looper mLooper; // 当前线程持有的Looper对象

       // HandlerThread类有2个构造方法
       // 区别在于:设置当前线程的优先级参数,即可自定义设置 or 使用默认优先级

            // 方式1. 默认优先级
            public HandlerThread(String name) {
                // 通过调用父类默认的方法创建线程
                super(name);
                mPriority = Process.THREAD_PRIORITY_DEFAULT;
            }

            // 方法2. 自定义设置优先级
            public HandlerThread(String name, int priority) {
                super(name);
                mPriority = priority;
            }
            ...
     }

总结

  • HandlerThread类继承自Thread
  • 创建HandlerThread类对象 = 创建Thread类对象 + 设置线程优先级 = 新开1个工作线程 + 设置线程优先级

步骤2:启动线程

/**
  * 具体使用
  */ 
   mHandlerThread.start();

/**
  * 源码分析:此处调用的是父类(Thread类)的start(),最终回调HandlerThread的run()
  */ 
  @Override
    public void run() {
        // 1. 获得当前线程的id
        mTid = Process.myTid();

        // 2. 创建1个Looper对象 & MessageQueue对象
        Looper.prepare();

        // 3. 通过持有锁机制来获得当前线程的Looper对象
        synchronized (this) {
            mLooper = Looper.myLooper();

            // 发出通知:当前线程已经创建mLooper对象成功
            // 此处主要是通知getLooper()中的wait()
            notifyAll();

            // 此处使用持有锁机制 + notifyAll() 是为了保证后面获得Looper对象前就已创建好Looper对象
        }

        // 4. 设置当前线程的优先级
        Process.setThreadPriority(mPriority);

        // 5. 在线程循环前做一些准备工作 ->>分析1
        // 该方法实现体是空的,子类可实现 / 不实现该方法
        onLooperPrepared();

        // 6. 进行消息循环,即不断从MessageQueue中取消息 & 派发消息
        Looper.loop();

        mTid = -1;
    }
}

/**
  * 分析1:onLooperPrepared();
  * 说明:该方法实现体是空的,子类可实现 / 不实现该方法
  */ 
    protected void onLooperPrepared() {

    }

总结
1. 为当前工作线程(即步骤1创建的线程)创建1个Looper对象 & MessageQueue对象
2. 通过持有锁机制来获得当前线程的Looper对象
3. 发出通知:当前线程已经创建mLooper对象成功
4. 工作线程进行消息循环,即不断从MessageQueue中取消息 & 派发消息

步骤3:创建工作线程Handler & 复写handleMessage()

/**
  * 具体使用
  * 作用:将Handler关联HandlerThread的Looper对象、实现消息处理操作 & 与其他线程进行通信
  * 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
  */ 
   Handler workHandler = new Handler( handlerThread.getLooper() ) {
            @Override
            public boolean handleMessage(Message msg) {
                ...//消息处理
                return true;
            }
        });

/**
  * 源码分析:handlerThread.getLooper()
  * 作用:获得当前HandlerThread线程中的Looper对象
  */ 
    public Looper getLooper() {
        // 若线程不是存活的,则直接返回null
        if (!isAlive()) {
            return null;
        } 
        // 若当前线程存活,再判断线程的成员变量mLooper是否为null
        // 直到线程创建完Looper对象后才能获得Looper对象,若Looper对象未创建成功,则阻塞
        synchronized (this) {


            while (isAlive() && mLooper == null) {
                try {
                    // 此处会调用wait方法去等待
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        // 上述步骤run()使用 持有锁机制 + notifyAll()  获得Looper对象后
        // 则通知当前线程的wait()结束等待 & 跳出循环
        // 最终getLooper()返回的是在run()中创建的mLooper对象
        return mLooper;
    }

总结

  • 在获得HandlerThread工作线程的Looper对象时存在一个同步的问题:只有当线程创建成功 & 其对应的Looper对象也创建成功后才能获得Looper的值,才能将创建的Handler 与 工作线程的Looper对象绑定,从而将Handler绑定工作线程
  • 解决方案:即保证同步的解决方案 = 同步锁、wait()notifyAll(),即 在run()中成功创建Looper对象后,立即调用notifyAll()通知 getLooper()中的wait()结束等待 & 返回run()中成功创建的Looper对象,使得Handler与该Looper对象绑定

步骤4:使用工作线程Handler向工作线程的消息队列发送消息

/**
  * 具体使用
  * 作用:在工作线程中,当消息循环时取出对应消息 & 在工作线程执行相关操作
  * 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
  */ 
  // a. 定义要发送的消息
  Message msg = Message.obtain();
  msg.what = 2; //消息的标识
  msg.obj = "B"; // 消息的存放
  // b. 通过Handler发送消息到其绑定的消息队列
  workHandler.sendMessage(msg);

/**
  * 源码分析:workHandler.sendMessage(msg)
  * 此处的源码即Handler的源码,故不作过多描述
  */ 

步骤5:结束线程,即停止线程的消息循环

/**
  * 具体使用
  */ 
  mHandlerThread.quit();

/**
  * 源码分析:mHandlerThread.quit()
  * 说明:
  *     a. 该方法属于HandlerThread类
  *     b. HandlerThread有2种让当前线程退出消息循环的方法:quit() 、quitSafely()
  */ 

  // 方式1:quit() 
  // 特点:效率高,但线程不安全
  public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit(); 
            return true;
        }
        return false;
    }

  // 方式2:quitSafely()
  // 特点:效率低,但线程安全
  public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

  // 注:上述2个方法最终都会调用MessageQueue.quit(boolean safe)->>分析1

/**
  * 分析1:MessageQueue.quit(boolean safe)
  */ 
    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(); // 方式1(不安全)会调用该方法 ->>分析2
            } else {
                removeAllMessagesLocked(); // 方式2(安全)会调用该方法 ->>分析3
            }
            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }
/**
  * 分析2:removeAllMessagesLocked()
  * 原理:遍历Message链表、移除所有信息的回调 & 重置为null
  */ 
  private void removeAllMessagesLocked() {
    Message p = mMessages;
    while (p != null) {
        Message n = p.next;
        p.recycleUnchecked();
        p = n;
    }
    mMessages = null;
}
/**
  * 分析3:removeAllFutureMessagesLocked() 
  * 原理:先判断当前消息队列是否正在处理消息
  *      a. 若不是,则类似分析2移除消息
  *      b. 若是,则等待该消息处理处理完毕再使用分析2中的方式移除消息退出循环
  * 结论:退出方法安全与否(quitSafe() 或 quit()),在于该方法移除消息、退出循环时是否在意当前队列是否正在处理消息
  */ 
  private void removeAllFutureMessagesLocked() {

    final long now = SystemClock.uptimeMillis();
    Message p = mMessages;

    if (p != null) {
        // 判断当前消息队列是否正在处理消息
        // a. 若不是,则直接移除所有回调
        if (p.when > now) {
            removeAllMessagesLocked();
        } else {
        // b. 若是正在处理,则等待该消息处理处理完毕再退出该循环
            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源码的分析完毕。


4. 总结

  • 本文全面分析了多线程中HandlerThread的源码,总结如下
    示意图

  • 接下来,我会继续讲解Android开发中关于多线程的知识,包括继承Thread类、实现Runnable接口、Handler等等,有兴趣可以继续关注Carson_Ho的安卓开发笔记


请帮顶 / 点赞!因为你的鼓励是我写作的最大动力!

阅读更多

扫码向博主提问

去开通我的Chat快问

carson_ho

博客专家

专注分享Android领域
  • 擅长领域:
  • Android
  • 自定义View
  • 开源库源码分析
  • RxJava
  • 性能优化
版权声明:本文为博主原创文章,未经博主允许不得转载,更多请继续关注Carson_Ho https://blog.csdn.net/carson_ho/article/details/52693418
所属专栏: 多线程
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭