Android之Handler简单介绍

今天我们的主角:Android消息机制——Handler

Handler是什么:

Android系统中线程间传递消息的一种机制

Handler作为这么重要的一种机制,虽然我们在使用的时候基本上只用到这一个类,但是还是有必要了解一下与之相关的类。

Message:消息的载体

MessageQueue:消息队列,负责存储消息

Handler:负责发送和处理消息(发送和处理不在同一个线程)

Looper:通过死循环来取出消息并且通知Handler处理

我们先通过代码来看看基本用法

  1. class AActivity : AppCompatActivity() {
  2. private lateinit var handler: Handler
  3. @SuppressLint('HandlerLeak')
  4. override fun onCreate(savedInstanceState: Bundle?) {
  5. super.onCreate(savedInstanceState)
  6. setContentView(R.layout.activity_main)
  7. handler = object : Handler() {
  8. override fun handleMessage(msg: Message) {
  9. super.handleMessage(msg)
  10. Log.d('ZLog AActivity', '接收到消息: $msg')
  11. }
  12. }
  13. Thread {
  14. kotlin.run {
  15. val msg = Message()
  16. msg.what = 110
  17. msg.arg1 = 1
  18. msg.arg2 = 2
  19. msg.obj = 'obj'
  20. handler.sendMessage(msg)
  21. }
  22. }.start()
  23. }
  24. }

AActivity: 接收到消息: { when=-57ms what=110 arg1=1 arg2=2 obj=obj target=com.example.androidteach.AActivity$onCreate$1 }

我们这里创建了一个handler并且重写了handleMessage方法,然后新建了一个线程,在线程中构建了一个消息并且发送。

Message有几个参数是我们可以直接使用的:

what:一般用来区分消息类型

arg1、arg2:这2个是int型,可以携带简单的参数

obj、setData:可以设置复杂的数据。

Handler的发送方法有几个:

sendEmptyMessage(int what):系统帮我们构建一个message并且设置what参数然后发送

sendEmptyMessageAtTime(int what, long uptimeMillis) 在指定的时间发送消息

sendEmptyMessageDelayed(int what, long delayMillis):延时发送消息

sendMessage(@NonNull Message msg):发送普通消息

sendMessageAtTime(@NonNull Message msg, long uptimeMillis):指定时间发送消息

sendMessageDelayed(@NonNull Message msg, long delayMillis):延时发送消息

obtainMessage():返回一个Message

基本的使用就这样就可以了,下面我们就来看看它的运行机制:

先来看看Handler:

查看handler的源码我们可以看到它还有几个带参数的构造方法:

  1. public Handler() {
  2. this(null, false);
  3. }
  4. public Handler(@Nullable Callback callback) {
  5. this(callback, false);
  6. }
  7. public Handler(@NonNull Looper looper) {
  8. this(looper, null, false);
  9. }
  10. public Handler(@NonNull Looper looper, @Nullable Callback callback) {
  11. this(looper, callback, false);
  12. }
  13. @UnsupportedAppUsage
  14. public Handler(boolean async) {
  15. this(null, async);
  16. }
  17. public Handler(@Nullable Callback callback, boolean async) {
  18. if (FIND_POTENTIAL_LEAKS) {
  19. final Class<? extends Handler> klass = getClass();
  20. if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
  21. (klass.getModifiers() & Modifier.STATIC) == 0) {
  22. Log.w(TAG, 'The following Handler class should be static or leaks might occur: ' +
  23. klass.getCanonicalName());
  24. }
  25. }
  26. mLooper = Looper.myLooper();
  27. if (mLooper == null) {
  28. throw new RuntimeException(
  29. 'Can't create handler inside thread ' + Thread.currentThread()
  30. + ' that has not called Looper.prepare()');
  31. }
  32. mQueue = mLooper.mQueue;
  33. mCallback = callback;
  34. mAsynchronous = async;
  35. }
  36. @UnsupportedAppUsage
  37. public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
  38. mLooper = looper;
  39. mQueue = looper.mQueue;
  40. mCallback = callback;
  41. mAsynchronous = async;
  42. }

其中主要的就2个,一个传了Callback,一个传了Looper。Callback为消息的回调接口,只有一个方法:

  1. public interface Callback {
  2. /**
  3. * @param msg A {@link android.os.Message Message} object
  4. * @return True if no further handling is desired
  5. */
  6. boolean handleMessage(@NonNull Message msg);
  7. }

我们刚刚创建的handler并没有给任何参数,所以最后会调用传递callbac的构造方法,并且默认为空。

可以看到其中的mLooper默认取Looper.myLooper()

这里我们的Handler已经拿到了looper和messageQueue了。再来看看发送消息,通过刚刚的几个sendMessage方法最后调用的地方:

  1. private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
  2. long uptimeMillis) {
  3. msg.target = this;
  4. msg.workSourceUid = ThreadLocalWorkSource.getUid();
  5. if (mAsynchronous) {
  6. msg.setAsynchronous(true);
  7. }
  8. return queue.enqueueMessage(msg, uptimeMillis);
  9. }

到这里我们可以看到,先给msg设置了一个target,就是一个handler对象,设置为了本身,然后就是将消息添加到队列中。

下面我们来看一下Looper:

通过刚刚的Handler构造方法中可以看到Looper有一个静态方法myLooper,我们先进入这个方法看看:

  1. public static @Nullable Looper myLooper() {
  2. return sThreadLocal.get();
  3. }

可以看到它用sThreadLocal.get返回的,sThreadLocal是一个ThreadLocal<Looper>的静态成员,ThreadLocal类简单说就是以线程为key来存储数据的结构,每个线程保存的数据互不影响,调用的get和set方法只对当前线程有效。

所以如果Looper在调用myLooper()之前没有设置过的话获取的会是空值。那为什么我们直接创建的Handler里面Looper并不是空?我们来找一下相关的设置方法:

我们看到一个prepare方法,调用了一个私有的prepare方法,在这里面设置Looper,并且在设置之前判断了当前线程是不是已经设置过了,如果设置过了就会抛出异常,提示一个线程只能创建一个Looper。

我们刚刚创建handler的时候并没有调用prepare方法,那我们就再调用一下看看,是不是当前的线程已经设置过了。

我们可以看到这里就报了刚刚看到的异常,提示第20行。所以在这之前这个方法已经被调用过了。那么是谁调用的呢?肯定是系统了,因为我们是直接在activity的onCreate方法中调用的,所以这里 是main线程,系统在启动apk的时候已经帮我们调用过了,这样我们就可以在这里直接创建Handler而不用再去单独调用一次Looper.prepare方法。

其实系统调用的应该不是Looper.prepare方法,应该是prepareMainLooper方法,大家看看下面的截图:

系统启动的时候就已经帮我们初始化好了主线程的Looper。如果是在子线程呢?就需要我们自己调用Looper.prepare方法了。

那么消息是在那里被取出来的呢?还是在Looper中,我们来看一个loop方法:

  1. public static void loop() {
  2. final Looper me = myLooper();
  3. if (me == null) {
  4. throw new RuntimeException('No Looper; Looper.prepare() wasn't called on this thread.');
  5. }
  6. final MessageQueue queue = me.mQueue;
  7. // Make sure the identity of this thread is that of the local process,
  8. // and keep track of what that identity token actually is.
  9. Binder.clearCallingIdentity();
  10. final long ident = Binder.clearCallingIdentity();
  11. // Allow overriding a threshold with a system prop. e.g.
  12. // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
  13. final int thresholdOverride =
  14. SystemProperties.getInt('log.looper.'
  15. + Process.myUid() + '.'
  16. + Thread.currentThread().getName()
  17. + '.slow', 0);
  18. boolean slowDeliveryDetected = false;
  19. for (;;) {
  20. Message msg = queue.next(); // might block
  21. if (msg == null) {
  22. // No message indicates that the message queue is quitting.
  23. return;
  24. }
  25. // This must be in a local variable, in case a UI event sets the logger
  26. final Printer logging = me.mLogging;
  27. if (logging != null) {
  28. logging.println('>>>>> Dispatching to ' + msg.target + ' ' +
  29. msg.callback + ': ' + msg.what);
  30. }
  31. // Make sure the observer won't change while processing a transaction.
  32. final Observer observer = sObserver;
  33. final long traceTag = me.mTraceTag;
  34. long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
  35. long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
  36. if (thresholdOverride > 0) {
  37. slowDispatchThresholdMs = thresholdOverride;
  38. slowDeliveryThresholdMs = thresholdOverride;
  39. }
  40. final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
  41. final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
  42. final boolean needStartTime = logSlowDelivery || logSlowDispatch;
  43. final boolean needEndTime = logSlowDispatch;
  44. if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
  45. Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
  46. }
  47. final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
  48. final long dispatchEnd;
  49. Object token = null;
  50. if (observer != null) {
  51. token = observer.messageDispatchStarting();
  52. }
  53. long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
  54. try {
  55. msg.target.dispatchMessage(msg);
  56. if (observer != null) {
  57. observer.messageDispatched(token, msg);
  58. }
  59. dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
  60. } catch (Exception exception) {
  61. if (observer != null) {
  62. observer.dispatchingThrewException(token, msg, exception);
  63. }
  64. throw exception;
  65. } finally {
  66. ThreadLocalWorkSource.restore(origWorkSource);
  67. if (traceTag != 0) {
  68. Trace.traceEnd(traceTag);
  69. }
  70. }
  71. if (logSlowDelivery) {
  72. if (slowDeliveryDetected) {
  73. if ((dispatchStart - msg.when) <= 10) {
  74. Slog.w(TAG, 'Drained');
  75. slowDeliveryDetected = false;
  76. }
  77. } else {
  78. if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, 'delivery',
  79. msg)) {
  80. // Once we write a slow delivery log, suppress until the queue drains.
  81. slowDeliveryDetected = true;
  82. }
  83. }
  84. }
  85. if (logSlowDispatch) {
  86. showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, 'dispatch', msg);
  87. }
  88. if (logging != null) {
  89. logging.println('<<<<< Finished to ' + msg.target + ' ' + msg.callback);
  90. }
  91. // Make sure that during the course of dispatching the
  92. // identity of the thread wasn't corrupted.
  93. final long newIdent = Binder.clearCallingIdentity();
  94. if (ident != newIdent) {
  95. Log.wtf(TAG, 'Thread identity changed from 0x'
  96. + Long.toHexString(ident) + ' to 0x'
  97. + Long.toHexString(newIdent) + ' while dispatching to '
  98. + msg.target.getClass().getName() + ' '
  99. + msg.callback + ' what=' + msg.what);
  100. }
  101. msg.recycleUnchecked();
  102. }
  103. }

代码比较长,其实我们只需要关注其中的for循环,这里的for循环没有设置任何条件,那么就是一个死循环,它就是在这里面通过消息队列取出的消息,还记得handler的发送方法吗?在发送的时候,给每个message都设置了一个target,就是handler本身。所以这里取出消息后调用了target的dispatchMessage来分发消息,最终会调用我们重写的handleMessage将消息给到我们自己处理。

到这里整个消息的传递流程就讲完了,说的比较乱,下面大致整理一下:

首先要了解的就是几个类的作用:

Handler:复制消息的发送和最终的处理;

Message:消息的实体,用来保存我们要传递的数据;

MessageQueue:消息队列,存储消息;

Looper:复制循环遍历取出消息并且分发给各自的Handler处理。相当于邮局分派信件,所以每个线程只能有一个Looper。

(0)

相关推荐

  • Android Handler消息机制(源码解析)

    Handler 的设计初衷 是为了解决 多线程状态下控件状态混乱问题,通过Handler 让主线程在更新UI控件的状态,而Handler 是面试中常被问起的问题,然后本文就带大家走一遍Handler ...

  • Android之Handler和Loooper源码分析

    Android之Handler和Loooper源码分析

  • Android 消息机制(Looper Handler MessageQueue Message)

    前言 上一篇我们介绍了LeakCanary工具用来分析内存泄漏以及谈了下几种常见内存泄漏的表现和解决方法.本篇内容我们来分析Android的消息机制.我们为什么要介绍Android的消息机制呢,因为A ...

  • 简单介绍各类化肥的不同特性

    时间:2018/3/13 16:27:57       作者:翻堆机 翻堆机: 常用的肥料有很多种,不同的肥料性状有别,用法也各不相同,有的适宜作底肥.有的适宜作追肥,有的适宜作种肥,不同的作物对化肥 ...

  • 简单介绍珍珠的挑选方法和鉴别

    珍珠有着"珠宝皇后"的美誉,其制成的珍珠项链拥有着无可比拟的高雅气质,是众多女性朋友出息重要场合的必备之物,近年来,随着各种设计理念的创新,珍珠开始与黄金.铂金等各种材质搭配,珍珠 ...

  • 简单介绍重晶石

    重晶石是以硫酸钡(BaSO4)为主要成分的非金属矿产品,纯重晶石显白色.有光泽,由于杂质及混入物的影响也常呈灰色.浅红色.浅黄色等,结晶情况相当好的重晶石还可呈透明晶体出现.重晶石系硫酸盐矿物.成分为 ...

  • 天津除了南开大学、天津大学之外,还有其他好大学吗?简单介绍其他8所大学,低调而实力强

    有人问,除了天大和南开,天津还有什么好大学? 这个问题很好,天津大学和南开大学名头太响,在它们的光芒之下,很多天津的大学都是默默无闻,其实还是有不少好大学的,否则大家也不会羡慕天津考生全国最高的一本录 ...

  • 简单介绍长江大学:长江大学的来历怎样?王牌专业和就业是什么?

    前几天,我写了一篇三峡大学的文章,有人说,三峡大学叫长江大学,就更有气魄了. 我不知道三峡大学有没有想过长江大学这么名字,不过2003年之后,它就没法想了,因为有了长江大学了. 三峡大学主要是水电.电 ...

  • 关于德尚太赫兹光波理疗仪的简单介绍

    ​关于德尚太赫兹光波理疗仪的简单介绍: 一.什么是太赫兹? 1,是介于微波和红外波之间的电磁波段,波长0.3um~3000um,波长在2-17之间有益于人体健康,小于2对没有作用,大于17对人体有辐射 ...

  • 电子产品 篇三:首次使用大疆osmo mobile3的简单介绍

    2019-08-25 16:17:15 42点赞 36收藏 28评论 一代比一代便宜,转向轴和二代有了不同,并且能折叠了,体积变小,对于拍vlog的朋友很好,我也定了一个,天猫买的,没几天就发货了,因 ...

  • 简单介绍几种税务筹划方法

    简单介绍几种税务筹划方法 税收优惠政策是指税法对某些纳税人和征税对象给予鼓励和照顾的一种特殊规定.国家为了扶持某些特定产业.行业.地 区.企业和产品的发展,在税法中做出某些特殊规定,比如,免除其应缴的 ...

  • 反射片支架简单介绍和应用领域

    反射片支架广泛应用在各大测绘监测领域,是因为全站仪的应用,以及社会逐渐倾向于快捷高效.低成本.少人员的普遍诉求. 常规测量中,需要专人在后视点架设棱镜,花费许多人力和时间不说,很多测点环境不允许架设棱 ...