前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android多线程:一步步带你源码解析HandlerThread

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

作者头像
Carson.Ho
发布2019-02-22 15:38:18
3880
发布2019-02-22 15:38:18
举报
文章被收录于专栏:Android知识分享Android知识分享

前言

  • 多线程的应用在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. 源码分析

代码语言:javascript
复制
// 步骤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的实例对象

代码语言:javascript
复制
/**
  * 具体使用
  * 传入参数 = 线程名字,作用 = 标记该线程
  */ 
   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:启动线程

代码语言:javascript
复制
/**
  * 具体使用
  */ 
   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()

代码语言:javascript
复制
/**
  * 具体使用
  * 作用:将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向工作线程的消息队列发送消息

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

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

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

代码语言:javascript
复制
/**
  * 具体使用
  */ 
  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的安卓开发笔记

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年02月08日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 目录
  • 1. 简介
  • 2. 工作原理
  • 3. 源码分析
    • 步骤1:创建HandlerThread的实例对象
      • 步骤2:启动线程
        • 步骤3:创建工作线程Handler & 复写handleMessage()
          • 步骤4:使用工作线程Handler向工作线程的消息队列发送消息
            • 步骤5:结束线程,即停止线程的消息循环
            • 4. 总结
            • 请帮顶 / 点赞!因为你的鼓励是我写作的最大动力!
            相关产品与服务
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档