Android多线程之 HandlerThread 源码分析

Android多线程之 HandlerThread 源码分析

一、前言

这主要是一个讲解 Android 中多线程的系列,全部文章如下:

前面写了 Android 中的消息机制,也就是 Handler 的消息机制。不了解的可以去看下 Handler系列—源码分析

本章就来使用和分析下 HandlerThread

二、什么是 HandlerThread?

我们看下官方的描述:

可以看到 HandlerThread 是继承于 Thread 类的,说明我们可以使用它开启一个子进程去执行一些操作。

再看下官方说明:

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.

这个类可以方便的开启一个带有 Looper 的新线程。这个 looper 可以去创建一个 Handler,但是还是必须得调用 start() 方法。

我们大概知道 HandlerThread 更加方便了我们在 Android 中进行多线程消息传递。

下面我们通过实例了解下:

public class HandlerThreadTestActivity extends AppCompatActivity {

    private static final String TAG = "HandlerThreadTest";

    private TextView mShow;

    private HandlerThread mHandlerThread = new HandlerThread("MyHandlerThread");

    private Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_thread_test);
        mShow = findViewById(R.id.show);
        mHandlerThread.start();
        mHandler = new Handler(mHandlerThread.getLooper()) {
            @Override
            public void handleMessage(Message message) {
                super.handleMessage(message);
                final int what = message.what;
                mShow.setText("得到的 what 为: "+what );
                Log.d(TAG, "handleMessage: " + message.what);
            }
        };
        findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mHandler.sendMessage(mHandler.obtainMessage(1));
            }
        });

        findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mHandler.sendMessage(mHandler.obtainMessage(2));
            }
        });

        findViewById(R.id.button3).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mHandler.sendMessage(mHandler.obtainMessage(3));
            }
        });
    }
}

运行程序,不出意外会得到如下异常:

原因已经很清楚了,我们在子线程中做了更新 UI 的操作。

下面来分析下原因:

由于 HandlerThread 是继承于 Thread 的,所以在实例化的 HandlerThread 对象是一个子线程,我们在创建 Handler 的时候,传入了 mHandlerThread 对象所在的线程的 Looper 对象,一个线程中只能有一个 Looper 对象,进而使 mHandler 和 子线程进行了绑定,所以这个 mHandler 是属于 mHandlerThread 的。

大家都知道只有主线程的 Handler 对象才能够进行更新 UI 的操作,所以在 mHandler 中执行 UI 操作时肯定不行的。

所以我们需要这样写:

runOnUiThread(new Runnable() {
    @Override
    public void run() {
        mShow.setText("得到的 what 为: "+what );
    }
});

代码 才能真正的运行。

通过上面的分析,大家应该能够知道 HandlerThread 是什么了。

  • 由于继承于 Thread ,所以 HandlerThread 就是一个线程类;
  • HandlerThread 内部拥有一个 Looper 循环,可以直接传递给 Handler 使用

其实 HandlerThread 就是一个包含了 LooperThread,可以免去我们在子线程中创建 Handler 的时候写一些多余的代码。

比如我们在上篇文章的 2-1 章节 章节中创建一个子线程中的 Handler :

new Thread(new Runnable() {
    @Override
    public void run() {
        Looper.prepare();
        Handler threadHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
        Looper.loop();
    }
}).start();

使用了 HandlerThread 以后我们只需要:

private HandlerThread mHandlerThread = new HandlerThread("MyHandlerThread");
mHandlerThread.start();
Handler threadHandler = new Handler(mHandlerThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };

这样看上去是不是清晰了许多?就是省去了创建 Looper 和 开启 loop 的操作

三、HandlerThread 解析

由于这个类的代码很少,我们都看下。

1.成员变量和构造方法

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    private @Nullable Handler mHandler;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
}

可以看到,HandlerThread 的成员变量很简单,

  • mPriority 是优先级
  • mTid 是线程的标示符,主要和线程的优先级有关
  • mLooper 是内部持有的 Looper 对象
  • mHandler 是与之绑定的 Handler 对象

构造方法也只有两个:

  1. 第一个构造函数
    第一个只需要传入线程的名称就行了,会把线程的优先级设置了默认的

  2. 第一个构造函数
    第二个则根据自己的需要传入线程的名称和优先级,根据注释可以知道,这个优先级必须是从 android.os.Process 类中获取,不能从 Thread 类中获取

2、onLooperPrepared 方法

继续往下看,会看到一个空实现的方法

/**
 * Call back method that can be explicitly overridden if needed to execute some
 * setup before Looper loops.
 */
protected void onLooperPrepared() {
}

根据注释可以知道,这个方法是给我们在创建 Looper 之后,开启 Looper 循环之前的一些操作使用的。

也就是说在 Looper.prepare() 之后 ,在 Looper.loop() 之前需要做的操作,写在这里,上一篇 Handler 源码解析的文章有写到,这里就不多说了。

3、核心 run 方法

由于继承于 Thread,要重写 Thread 的核心方法 run() 方法。

@Override
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

这里首先会调用 Looper.prepare() 创建一个新的 Looper 对象,关于里面的内容,如果看过上篇文章,应该就知道了。

创建完 Looper 以后,通过 Looper 的 Looper.myLooper() 赋值给其成员变量 mLooper

Process.setThreadPriority(mPriority); 就是设置线程的优先级,
onLooperPrepared(); 就是上面的空方法
Looper.loop(); 开启 Looper 循环,不断的查看 MessageQueue 中有没有新消息
最终执行完以后把 mTid置为 -1。

可能你发现了 notifyAll(); 这个方法,那么是干嘛的呢?其实我在 Java多线程 有讲到:

Object 类中提供了 wait() 方法,导致当前线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法,才会继续执行。

其实这个地方的 notifyAll() 和下面的 getLooper() 代码是有关的。

4. 获取 Looper

public Looper getLooper() {
    if (!isAlive()) {
        return null;
    }
    
    // If the thread has been started, wait until the looper has been created.
    synchronized (this) {
        while (isAlive() && mLooper == null) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
    }
    return mLooper;
}

代码中先判断了是线程是否存活,如果不存活,就直接返回 null.

然后我们就看到了的上面提到的 wait() 方法,也就是说如果这个线程没有执行 run() 方法之前,就去调用 getLooper() 是拿不到的。只有执行了 run() 方法了以后,执行了 notifyAll 才会继续解除这里的 wait 等待,才能正确的获取到 mLooper 对象。

5. 其他方法

@NonNull
public Handler getThreadHandler() {
    if (mHandler == null) {
        mHandler = new Handler(getLooper());
    }
    return mHandler;
}

getThreadHandler 获取一个和当前线程关联的 Handler 对象

public boolean quit() {
    Looper looper = getLooper();
    if (looper != null) {
        looper.quit();
        return true;
    }
    return false;
}

quit 退出 Handler 线程的循环

public boolean quitSafely() {
    Looper looper = getLooper();
    if (looper != null) {
        looper.quitSafely();
        return true;
    }
    return false;
}

quitSafely 安全的退出 Handler 线程的循环

上面的 quitquitSafely 都调用了 MessageQueue 中的方法:

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();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
}
  1. quit 调用了 removeAllMessagesLocked();

    作用是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的需要延迟执行的消息)还是非延迟消息。

  2. quitSafely 调用了 removeAllFutureMessagesLocked()

    该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。

无论调用了 quit 还是 quitSafely,线程中的 Looper 都不会接受新的消息了,消息队列终结,这时候再通过 Handler 调用 sendMessagepost 等方法发送消息时均返回 false ,表示消息没有成功放入消息队列 MessageQueue 中,因为消息队列已经退出了。

四、小结

至此 HandlerThread 的源码已经分析完了,相对来说 HandlerThread 就是做了一些封装,理解和用起来都是不难的。

下篇文章看下 AsyncTask

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值