Android开发

016 Android多线程-HandlerThread-源码解

2021-01-09  本文已影响0人  凤邪摩羯

目录

image

1. 简介

image

2. 工作原理

内部原理 = Thread类 + 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;
            }
            ...
     }

总结

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

总结

步骤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. 总结

上一篇下一篇

猜你喜欢

热点阅读