Thread、Handler和HandlerThread关系详解

前言

前几天看到一道面试题:Thread、Handler和HandlerThread有什么区别?,这个题目有点意思,对于很多人来说,可能对Thread和Handler很熟悉,主要涉及到Android的消息机制(Handler、Message、Looper、MessageQueue),详见《 从Handler.post(Runnable r)再一次梳理Android的消息机制(以及handler的内存泄露)》

但是这个HandlerThread是拿来做什么的呢?它是Handler还是Thread?我们知道Handler是用来异步更新UI的,更详细的说是用来做线程间的通信的,更新UI时是子线程与UI主线程之间的通信。那么现在我们要是想子线程与子线程之间的通信要怎么做呢?当然说到底也是用Handler+Thread来完成(不推荐,需要自己操作Looper),Google官方很贴心的帮我们封装好了一个类,那就是刚才说到的:HandlerThread。(类似的封装对于多线程的场景还有AsyncTask)

使用方法

还是先来看看HandlerThread的使用方法:
首先新建HandlerThread并且执行start()

private HandlerThread mHandlerThread;
......
mHandlerThread = new HandlerThread("HandlerThread");
handlerThread.start();

创建Handler,使用mHandlerThread.getLooper()生成Looper:

 final Handler handler = new Handler(mHandlerThread.getLooper()){
   @Override
   public void handleMessage(Message msg) {
    System.out.println("收到消息");
   }
  };

然后再新建一个子线程来发送消息:

 new Thread(new Runnable() {
   @Override
   public void run() {
    try {
     Thread.sleep(1000);//模拟耗时操作
     handler.sendEmptyMessage(0);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
  }).start();

最后一定不要忘了在onDestroy释放,避免内存泄漏:

 @Override
 protected void onDestroy() {
  super.onDestroy();
  mHandlerThread.quit();
 }

执行结果很简单,就是在控制台打印字符串:收到消息

原理

整个的使用过程我们根本不用去关心Handler相关的东西,只需要发送消息,处理消息,Looper相关的东西交给它自己去处理,还是来看看源码它是怎么实现的,先看构造方法:

public class HandlerThread extends Thread {}

HandlerThread其实还是一个线程,它跟普通线程有什么不同?

public class HandlerThread extends Thread {
 int mPriority;
 int mTid = -1;
 Looper mLooper;

 public HandlerThread(String name) {
  super(name);
  mPriority = Process.THREAD_PRIORITY_DEFAULT;
 }
 ......
}

答案是多了一个Looper,这个是子线程独有的Looper,用来做消息的取出和处理。继续看看HandlerThread这个线程的run方法:

 protected void onLooperPrepared() {
 }
 @Override
 public void run() {
  mTid = Process.myTid();
  Looper.prepare();
  synchronized (this) {
   mLooper = Looper.myLooper();//生成Looper
   notifyAll();
  }
  Process.setThreadPriority(mPriority);
  onLooperPrepared();//空方法,在Looper创建完成后调用,可以自己重写逻辑
  Looper.loop();//死循环,不断从MessageQueue中取出消息并且交给Handler处理
  mTid = -1;
 }

主要就是做了一些Looper的操作,如果我们自己使用Handler+Thread来实现的话也要进行这个操作,再来看看getLooper()方法:

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

方法很简单,就是加了个同步锁,如果已经创建了(isAlive()返回true)但是mLooper为空的话就继续等待,直到mLooper创建成功,最后看看quit方法,值得一提的是有两个:

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

quitSafely是针对在消息队列中还有消息或者是延迟发送的消息没有处理的情况,调用这个方法后都会被停止掉。

总结

HandlerThread的使用方法还是比较简单的,但是我们要明白一点的是:如果一个线程要处理消息,那么它必须拥有自己的Looper,并不是Handler在哪里创建,就可以在哪里处理消息的。

如果不用HandlerThread的话,需要手动去调用Looper.prepare()和Looper.loop()这些方法。

以上就是对Thread、Handler和HandlerThread关系 的资料整理,后续继续补充相关资料,谢谢大家对本站的支持!

(0)

相关推荐

  • android使用handlerthread创建线程示例

    在android开发中,一说起线程的使用,很多人马上想到new Thread(){...}.start()这种方式.这样使用当然可以,但是多次使用这种方式,会创建多个匿名线程.使得程序运行起来越来越慢.因此,可以考虑使用一个Handler来启动一个线程,当该线程不再使用就删除,保证线程不会重复创建.一般会使用Handler handler = new Handler(){...}创建Handler.这样创建的handler是在主线程即UI线程下的Handler,即这个Handler是与UI线程下

  • Android用HandlerThread模拟AsyncTask功能(ThreadTask)

    前言 AsyncTask是个好东西,能处理绝大多数应用线程和更新UI的任务,由于其内部使用了静态线程池,如果你有一堆异步任务(例如全局定时更新数据.同一个Activity中多个AsyncTask同时执行)其中有不能马上执行完的情况(例如网络请求超时),那就糟了,其他任务都还等着呢,就会出现任务卡住的情况.此时就需要直接上Thread了,这里参考AsyncTask的API封装了一个ThreadTask,便于必要时代码替换,欢迎交流!  正文实例代码: import android.os.Handl

  • Android HandlerThread的使用及原理详解

    一.HandlerThread的含义 HandlerThread能够新建拥有Looper的线程.这个Looper能够用来新建其他的Handler.(线程中的Looper)需要注意的是,新建的时候需要被回调. 二.HandlerThread的用法 一般情况下,我们会经常用Handler在子线程中更新UI线程,那是因为在主线程中有Looper循环,而HandlerThread新建拥有Looper的子线程又有什么用呢? 必然是执行耗时操作.举个例子,数据实时更新,我们每10秒需要切换一下显示的数据,如

  • Thread、Handler和HandlerThread关系详解

    前言 前几天看到一道面试题:Thread.Handler和HandlerThread有什么区别?,这个题目有点意思,对于很多人来说,可能对Thread和Handler很熟悉,主要涉及到Android的消息机制(Handler.Message.Looper.MessageQueue),详见< 从Handler.post(Runnable r)再一次梳理Android的消息机制(以及handler的内存泄露)> 但是这个HandlerThread是拿来做什么的呢?它是Handler还是Thread

  • Android Handler移除Message详解及实例代码

    Android Handler移除Message详解 问题: 1.removeMessage(what)函数是否只能移除对应what值的Message? 2.对于Delayed发送的Message,能否提前remove? 代码测试: package javine.k.testhandler; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Han

  • Android HandlerThread案例详解

    HandlerThread 顾名思义就是一种可以使用 Handler 的 Thread.日常开发中我们经常会通过创建一个 Thread 去执行任务,有多个任务就多创建几个线程实现,这时候可能出现线程同步的问题.不过有时候我们并不需要很强的并发性,只需保证按照顺序地执行各个任务即可,有什么好办法实现呢?第一反应想到的可能是通过 Executors.newSingleThreadExecutor() 方法来创建一个 SingleThreadExecutor,来统一所有的任务到一个线程中,然后按顺序执

  • iOS开发retina屏幕下的点与像素关系详解

    目录 引言 I iOS中点与像素有什么关系? II 图片使用的相关注意事项 2.1 推荐使用png格式 2.2 关于图像的实例化 2.3 动画结束之后清除帧动画数组 III 设置状态栏字体颜色 3.1 方式一 3.2 方式二 see also 引言 提交app store的时候 需要一张1024*1024的 如果不设置这两种的尺寸启动页的话,在4英寸.3.5英寸的设备上展示不了启动页,app 的高度也默认都是矮的960px.** 注意@3x 提供给开发的px 为12422208 ,但真实的px

  • Android中屏幕密度和图片大小的关系详解

    Android中屏幕密度和图片大小的关系详解 前言 Android中支持许多资源,包括图片(Bitmap),对应于bitmap的文件夹是drawable,除了drawable,还有drawable-ldpi.drawable-mdpi.drawable-hdpi.drawable-xhdpi.drawable-xxhdpi等,同一张图片放到上面不同的文件夹中是有区别的,比如一张100 * 100像素大小的图片,分别放在上述各个文件夹中,然后将其设置为ImageView(假设宽高都是wrap_co

  • java多线程Thread的实现方法代码详解

    之前有简单介绍过java多线程的使用,已经Thread类和Runnable类,为了更好地理解多线程,本文就Thread进行详细的分析. start() 我们先来看看API中对于该方法的介绍: 使该线程开始执行:Java 虚拟机调用该线程的 run 方法. 结果是两个线程并发地运行:当前线程(从调用返回给 start 方法)和另一个线程(执行其 run 方法). 多次启动一个线程是非法的.特别是当线程已经结束执行后,不能再重新启动. 用start方法来启动线程,真正实现了多线程运行,这时无需等待r

  • 对python:threading.Thread类的使用方法详解

    Python Thread类表示在单独的控制线程中运行的活动.有两种方法可以指定这种活动: 1.给构造函数传递回调对象 mthread=threading.Thread(target=xxxx,args=(xxxx)) mthread.start() 2.在子类中重写run() 方法 这里举个小例子: import threading, time class MyThread(threading.Thread): def __init__(self): threading.Thread.__in

  • 对Python Class之间函数的调用关系详解

    假设有Class A 和 Class B两个类,Class A中定义了a(),Class B中定义了b(). 现在我想在Class B中调用 Class A中的函数a().此处介绍三种调用方法: 方法一: 在Class B中所定义的fuction()中声明Class A的对象a,然后用对象a来调用Class A的函数a(). 最后在main中声明Class B的对象b,让b调用该类中的fuction(). #!/usr/bin/env python # -*- coding: utf-8 -*-

  • Python for循环与getitem的关系详解

    这篇文章主要介绍了Python for循环与getitem的关系详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一个类里面如果由__iter__for循环就是找它取,没有的话就会找__getitem__ 前面一笔看过没有留心具体的执行情况. In [169]: class Foo: ...: def __getitem__(self, pos): ...: print(pos) ...: return range(10)[pos] ...:

  • python多线程和多进程关系详解

    关于多线程的大概讲解: 在Python的标准库中给出了2个模块:_thread和threading,_thread是低级模块不支持守护线程,当主线程退出了时,全部子线程都会被强制退出了.而threading是高级模块,用作对_thread进行了封装支持守护线程.在大部分状况下人们只需要采用threading这个高级模块即可. 关于多进程的大概讲解: 多进程是multiprocessing模块给出远程与本地的并发,在一个multiprocessing库的采用场景下,全部的子进程全是由一个父进程运行

随机推荐