Android进阶2:线程和线程池(2)—— HandlerThread原理解析

HandlerThread是属于Android异步线程模块的一部分,上一篇是AsyncTask的源码:
Android进阶2:线程和线程池(1)—— AsycTask原理解析

如果你没看过handler消息机制,建议先学习下handler消息机制:
Android进阶1:Android的消息机制

记得之前刚接触android的之后,只知道HandlerThread内部原理:Handler + Thread, 始终没看过源码,最近学习到了android线程和线程池模块,自然而然的HandlerThread源码是必须看的。

HandlerThread的简单使用

老规矩,得先会使用HandlerThread再说别的。

需求:使用HandlerThread开启一个子任务,在子任务中,发送消息,在UI主线程接收消息,并显示在TextView上。


public class MainActivity extends AppCompatActivity {

    private Handler mainHandler = new Handler() { //主线程的handler
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            String description = msg.getData().getString("description");
            textView.setText(description);
        }
    };

    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.image_view);

        HandlerThread handlerThread = new HandlerThread("test-handler-thread");
        handlerThread.start();

        Handler childHandler = new Handler(handlerThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //运行在子线程
                String description = msg.getData().getString("description");
                Message message = mainHandler.obtainMessage();
                Bundle data = message.getData();
                data.putString("description",  description);
                mainHandler.sendMessage(message); //通过主线程的handler发送消息
            }
        };
        for (int i = 0; i < 10; i++) { //子线程childHandler 发送消息
            Message message = childHandler.obtainMessage();
            Bundle data = new Bundle();
            data.putString("description","hello world  " + i);
            message.setData(data);
            childHandler.sendMessageDelayed(message, 1000 * i);
        }
    }
}

通过上述demo是对handleThread的简单使用,并且我们得到的信息是:

  • 通过handlerThread.getLooper()新建了绑定的childHandler对象
  • childHandler发送消息,但是在childHandler内部又通过mainHandler发送了消息,真正更新UI是在mainHandler中,为什么不在childHandler中更新UI呢? 原因只有一个:那就是childHandler中的handleMessage是运行在子线程中的,我们知道handleMessage方法运行的线程和创建该handler的Looper的线程是一致的,换句话说:handlerThread.getLooper()获得的是子线程的Looper。 到底是不是子线程的Looper呢?看源码

HandlerThread的

1:HandlerThread成员变量


/**
  Handy class for starting a new thread that has a looper. The looper can then be 
  used to create handler classes. Note that start() must still be called.
 */
//注意点
public class HandlerThread extends Thread {
    int mPriority; //线程任务优先级
    int mTid = -1;
    //注意点
    Looper mLooper; //当前线程的Looper对象
    private @Nullable Handler mHandler; //传递的handler
    ......
}

两个注意点:

  1. HandlerThread 继承自 Thread类,HandlerThread本身可以说是Thread的子类,也就是可以处理耗时操作。
  2. 成员变量含有Looper和Handler对象,也就是处理消息啦。

    再看下Google给出的方法描述:创建一个含有Looper的子线程,该Looper用来创建一个Handler对象。无论如何都必须通过start开启。

通过Google的描述,我们使用的HandlerThread的步骤是不是清晰了?

  1. HandlerThread handlerThread = new HandlerThread(“test-handler-thread”);
  2. handlerThread.start(); 开启任务
  3. Handler childHandler = new Handler(handlerThread.getLooper()) ,通过HandlerThread的成员变量Looper创建出关联的handler。

接着往下看,构造函数:

HandlerThread构造方法

   /**
     * @param name the name of the new thread
     * 线程名称
     */
    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

HandlerThread只有两个构造函数,我们的demo使用的是第一种创建方式,name参数表示线程的名字;

对象创建好了该运行了,那么就得看下start源码了

HandlerThread启动线程

通过查找,并没有在HandlerThread的源码内找到start()的复写,也就是说执行的是Thread的start方法。再想一下,start()方法调用之后,run()方法就调用了,来看下run()的源码:


    @Override
    public void run() {
        //此时就运行在子线程了!
        mTid = Process.myTid();
        Looper.prepare(); // 创建子线程的Looper
        synchronized (this) {
            mLooper = Looper.myLooper(); //获取子线程的Looper
            notifyAll(); //注意点1:唤醒所有等待
        }
        Process.setThreadPriority(mPriority); //设置线程优先级
        onLooperPrepared(); //注意点2:looper准备工作
        Looper.loop(); //开启Looper调度 
        mTid = -1;
    }

通过上述代码可以看出,run方法主要做的工作就是:创建Looper,并且开启Looper的消息循环。有两个注意点:

  • 注意点1:唤醒等待线程, 唤醒的是谁呢?下文讲述
  • 注意点2:onLooperPrepared()方法,HandlerThread暴露这个方法,我们的知晓,我们可以在消息Looper开始消息调度前,复写此方法多一些事情。

    再回过头看demo创建子线程的handler,看到了handlerThread.getLooper(), 看下源码:


    public Looper getLooper() {
        if (!isAlive()) { //线程是否存活
            return null;
        }

        // If the thread has been started, wait until the looper has been created.
        //如果改线程已经存活,就等到Looper创建好。
        synchronized (this) {
            while (isAlive() && mLooper == null) { //死循环,等待创建好Looper对象
                try {
                    wait(); //等待唤醒
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper; //返回Looper对象
    }

此方法返回的是Looper对象,然后先判断线程是否存活,然后判断mLooper是否为null , 如果looper是null,就休眠等待唤醒,休眠? 那什么时候唤醒呢? 还记得run()方法里面的唤醒等待吧? 就是线程间Looper,然后唤醒等待,确保创建使用Handler之前必须有Looper。由于该Looper对象创建在子线程中,所有通过该Looper为参数创建的Handler内部的handlerMessage也是运行在子线程中。可以这样理解:上述demo,发送消息是在主线程,但是执行消息是在子线程,刚好跟我们常用的handler相反。

HandlerThread退出

退出有两种方式:

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

这两种退出方式是不是很熟悉? 没错,就是Looper的两种退出方式,内部调用的是MessageQueue的退出。

至此流程走完了,梳理一下:

创建HandleThread —> start启动 —> 在run方法内创建Looper对象,并唤醒等待 —> 通过run创建的Looper对象,创建一个运行在子线程的handler对象 —> 在子线程的handleMessage方法内,处理消息。

HandlerThread和普通Thread的区别:
Thread: run()中执行耗时操作,执行一次之后就结束,
HandlerThread:run()方法是无限循环的,阻塞等待,通过子线程为参数创建的Handler发送消息,执行任务。因此在明确不需要HandlerThread时,要通过quit或者quitSafely终止线程的执行。

通过上述分析,可以确定的是,HandlerThread原理实际上就是handler + Thread的封装使用

本文感谢《Android开发艺术探索》

如果发现错误,欢迎指正,以便纠正,谢谢哈 = - =

Android开发交流群: 543671130
这里写图片描述

猜你喜欢

转载自blog.csdn.net/lmq121210/article/details/81367080