分享

Android之Handler简单介绍

 好汉勃士 2021-10-28

今天我们的主角: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

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多