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
Android进阶2:线程和线程池(2)—— HandlerThread原理解析_第1张图片

更多相关文章

  1. android获取当前运行Activity名字的方法
  2. Android Handler ExecutorService(线程池) 缓存模式
  3. android画任意曲线时,去除移动过程中出现莫名直线的方法
  4. Android开发包下载(包括开发所需所有安装包的下载方法与地址)
  5. Android获取系统时间方法的总结
  6. Intent传递对象的两种方法(Serializable,Parcelable)
  7. [置顶] Android之ContextMenu的使用方法以及与OptionMenu的区别
  8. INSTALL_FAILED_CONFLICTING_PROVIDER 错误解决方法

随机推荐

  1. Android Studio 2.0 FileOpUtils Not fou
  2. android 根据Bitmap bitmap 保存图片到手
  3. Android EditText中添加图标的简单方法
  4. 转 Android地图和定位学习总结
  5. 拨号器的实现
  6. Android O, failed to mount /system, /d
  7. Android中dip与px之间单位转换
  8. adb shell 无法启动 (insufficient permis
  9. android gdb ndk so lib
  10. android HTTP 通信, XML 解析, 通过 Hand