Android 多线程之HandlerThread 详解
本文给大家介绍一下Android HandlerThread,这个类在线程间通信是非常有用的。

我开发了Android这么久居然没有用过HandlerThread这个类,后面别人提醒才知道。没有用过的人确实值得一看。这个类和Handler有很大的关联。

1、一.HandlerThread相关介绍
1.HandlerThread有那些特点:
HandlerThread本质上是一个线程类,它继承了Thread;
HandlerThread有自己的内部Looper对象,可以进行looper循环;
通过获取HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务。
创建HandlerThread后必须先调用HandlerThread.start()方法,Thread会先调用run方法,创建Looper对象。

上面都是比较官方的说法。

其实我感觉HandlerThread最实用的功能是不同线程之间的数据传递处理,并且处理的方式是队列的形式。

HandlerThread其实就是一个线程Thread,一个类或工程可以有一个主线程和多个子线程。
Handler默认是主线程的,但是你添加了HandlerThread,这个Handler就是子线程的啦。
所以Handler在同一个类中也是可以有多个的,一个主线程的和多个子线程的,这些不同线程的Handler可以传递数据。

2.Handler运行在哪一个线程

一般来说在主线程new出来的Handler,回调方法handlerMessage的处理操作就是在主线程。
在子线程new出来的Handler,回调方法handlerMessage的处理操作就是在子线程。
其实真正决定Handler执行在哪一个线程是由,Handler里面的looper决定的,
默认的情况,我们创建Handler的时候,不会传入Looper对象,所以获取的是自己线程的Looper对象。
但是,其实Handler是有七个构造方法的,其中有方法可以传入Looper对象,
线程对象是可以直接使用getLooper()方法来获取looper的。
所以,我们是可以在主线程创建的Handler传入Looper对象,
来决定里面的回调方法handlerMessage处理的事务在哪一个线程。
同样的,我们在子线程创建的Handler也是可以控制的。

3.HandlerThread的应用场景
(1)多个线程之间通信(不限于主线程和子线程,可以多个子线程)
(2)在一个线程中处理事务后,马上通知另一个线程,并且可能需要传递数据

如果是两个Thread要相互通知,并且传递数据,就要定义外部变量,这时可能出现数据异步问题。
HandlerThread创建的Thread传入Handler中,就能解决这个问题。

这里我说一下我在一个项目中的场景,需求是在子线程进行录音,
但是录音过程中有很多其他的数据处理,比如获取音量大小,对音频数据进行压缩,发送音频数据给服务器。
这里数据压缩发送到服务器都是可以写在另外一个子线程中进行操作的,
但是需要在原来的子线程传递音频数据过去。

二.HandlerThread的一个示例程序‘

代码实现的功能比较简单:在主线程发送数据给子线程,子线程处理完后,在发送数据给主线程。
页面也比较简单,一个按钮和一个TextView,点击按钮后给子线程发送数据,子线程处理后给主线程更新TextView内容。

package com.liwenzhi.chunked.handlerthreaddemo;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    HandlerThread mHandlerThread;
    Handler mHandlerChild;  //创建一个子线程处理事务的handler
    String TAG = "MainActivity";
    TextView tv_info;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initData();

    }

    private void initView() {
        tv_info = findViewById(R.id.tv_info);
    }

    private void initData() {
        mHandlerThread = new HandlerThread("myThread"); //创建子线程
        mHandlerThread.start();    //必须先执行子线程,才能给handler

        //创建一个子线程处理事务的handler,传入Looper对象
        mHandlerChild = new Handler(mHandlerThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                //处理在子线程的事务
                Log.e(TAG, "mHandlerChild Thread name:" + Thread.currentThread().getName());
                if (msg.what == 11) {
                    int time = (int) msg.obj;
                    for (int i = 0; i < time; i++) {

                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        if (i % 10 == 0) {
                            //给主线程Handler发送数据
                            Message message = Message.obtain();
                            message.what = 22;
                            message.obj = i;
                            mUIhandler.sendMessage(message);
                        }
                    }
                }
            }
        };

    }

    //使用按钮,在主线程发送数据给子线程
    public void startThread(View view) {
        //让子线程的Handler处理事务,也可以发送数据给子线程
        Message msg = Message.obtain();
        msg.what = 11;
        msg.obj = 100;
        mHandlerChild.sendMessage(msg);

    }


    //创建一个在主线程处理事务的Handler
    Handler mUIhandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //处理主线程的事务
            Log.e(TAG, "mUIhandler Thread name:" + Thread.currentThread().getName());
            if (msg.what == 22) {
                tv_info.setText("" + msg.obj);
            }
        }
    };

    @Override
    protected void onDestroy() {
        mHandlerThread.quit();//停止子线程
        super.onDestroy();
    }
}

程序运行后,点击按钮,页面的TextView会每隔一秒文本的数字会加10;

三.Handler的几个构造方法
1.Handler()
2.Handler(Callback callback)
3.Handler(Looper looper)
4.Handler(Looper looper, Callback callback)
5.Handler(boolean async)
6.Handler(Callback callback, boolean async)
7.Handler(Looper looper, Callback callback, boolean async)

其中第一种构造方法是我们常用的,
后面的Callback对象,其实是一个用来回调的类,里面有handlerMessage方法
Looper对象就是我们控制Handler中回调方法handlerMessage要在哪个线程中处理事务
async是表示handlerMessage处理的事务是否异步,默认值false,表示按照队列顺序执行。

关于Callback的简单实用,这里可以给大家展示下:

//普通用法:
 Handler mHandler=new Handler(){
     @Override
     public void handleMessage(Message msg) {
    //处理事务
    }
 };


//使用Callback
 Handler mHandler=new Handler(mHandlerCall); 
 Handler.Callback mHandlerCall=new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            //处理事务

            return false;
        }
    };

有的人会问,如果我再第一种方法使用了,然后也传入Callback对象,这个时候两个地方的handleMessage,哪个会回调。
这个问题,你只要看Handler源码就很容易懂了。
Handler源码片段:

/**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

从上面可以看到如果Callback不为空,那么就是这个回调类中回调,并且return,后面的方法不会执行。
所有Callback回调是优先,如果没有这个对象,才会回调第一种方法的handlerMessage。

关于Handler,如果想了解更多,其实可以自己看看其中的源码,代码并不多几百行代码,其中大半都是注解。

但是其中的思想,没有那么容易理解,看看一些简单的过程和参数是可以的。