今天我们的主角:Android消息机制——Handler
Handler是什么:
Android系统中线程间传递消息的一种机制
Handler作为这么重要的一种机制,虽然我们在使用的时候基本上只用到这一个类,但是还是有必要了解一下与之相关的类。
Message:消息的载体
MessageQueue:消息队列,负责存储消息
Handler:负责发送和处理消息(发送和处理不在同一个线程)
Looper:通过死循环来取出消息并且通知Handler处理
我们先通过代码来看看基本用法
class AActivity : AppCompatActivity() { private lateinit var handler: Handler @SuppressLint('HandlerLeak') override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) handler = object : Handler() { override fun handleMessage(msg: Message) { Log.d('ZLog AActivity', '接收到消息: $msg')
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的源码我们可以看到它还有几个带参数的构造方法:
public Handler(@Nullable Callback callback) { public Handler(@NonNull Looper looper) { this(looper, null, false); public Handler(@NonNull Looper looper, @Nullable Callback callback) { this(looper, callback, false); public Handler(boolean async) { public Handler(@Nullable Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, 'The following Handler class should be static or leaks might occur: ' + klass.getCanonicalName()); mLooper = Looper.myLooper(); throw new RuntimeException( 'Can't create handler inside thread ' + Thread.currentThread() + ' that has not called Looper.prepare()'); public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
其中主要的就2个,一个传了Callback,一个传了Looper。Callback为消息的回调接口,只有一个方法:
public interface Callback { * @param msg A {@link android.os.Message Message} object * @return True if no further handling is desired boolean handleMessage(@NonNull Message msg);
我们刚刚创建的handler并没有给任何参数,所以最后会调用传递callbac的构造方法,并且默认为空。

可以看到其中的mLooper默认取Looper.myLooper()
这里我们的Handler已经拿到了looper和messageQueue了。再来看看发送消息,通过刚刚的几个sendMessage方法最后调用的地方:
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, msg.workSourceUid = ThreadLocalWorkSource.getUid(); msg.setAsynchronous(true); return queue.enqueueMessage(msg, uptimeMillis);
到这里我们可以看到,先给msg设置了一个target,就是一个handler对象,设置为了本身,然后就是将消息添加到队列中。
下面我们来看一下Looper:
通过刚刚的Handler构造方法中可以看到Looper有一个静态方法myLooper,我们先进入这个方法看看:
public static @Nullable Looper myLooper() { return sThreadLocal.get();
可以看到它用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方法:
public static void loop() { final Looper me = myLooper(); throw new RuntimeException('No Looper; Looper.prepare() wasn't called on this thread.'); final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); // Allow overriding a threshold with a system prop. e.g. // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start' final int thresholdOverride = SystemProperties.getInt('log.looper.' + Thread.currentThread().getName() boolean slowDeliveryDetected = false; Message msg = queue.next(); // might block // No message indicates that the message queue is quitting. // This must be in a local variable, in case a UI event sets the logger final Printer logging = me.mLogging; logging.println('>>>>> Dispatching to ' + msg.target + ' ' + msg.callback + ': ' + msg.what); // Make sure the observer won't change while processing a transaction. final Observer observer = sObserver; final long traceTag = me.mTraceTag; long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs; if (thresholdOverride > 0) { slowDispatchThresholdMs = thresholdOverride; slowDeliveryThresholdMs = thresholdOverride; final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0); final boolean logSlowDispatch = (slowDispatchThresholdMs > 0); final boolean needStartTime = logSlowDelivery || logSlowDispatch; final boolean needEndTime = logSlowDispatch; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0; token = observer.messageDispatchStarting(); long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid); msg.target.dispatchMessage(msg); observer.messageDispatched(token, msg); dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } catch (Exception exception) { observer.dispatchingThrewException(token, msg, exception); ThreadLocalWorkSource.restore(origWorkSource); Trace.traceEnd(traceTag); if (slowDeliveryDetected) { if ((dispatchStart - msg.when) <= 10) { slowDeliveryDetected = false; if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, 'delivery', // Once we write a slow delivery log, suppress until the queue drains. slowDeliveryDetected = true; showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, 'dispatch', msg); logging.println('<<<<< Finished to ' + msg.target + ' ' + msg.callback); // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); Log.wtf(TAG, 'Thread identity changed from 0x' + Long.toHexString(ident) + ' to 0x' + Long.toHexString(newIdent) + ' while dispatching to ' + msg.target.getClass().getName() + ' ' + msg.callback + ' what=' + msg.what);
代码比较长,其实我们只需要关注其中的for循环,这里的for循环没有设置任何条件,那么就是一个死循环,它就是在这里面通过消息队列取出的消息,还记得handler的发送方法吗?在发送的时候,给每个message都设置了一个target,就是handler本身。所以这里取出消息后调用了target的dispatchMessage来分发消息,最终会调用我们重写的handleMessage将消息给到我们自己处理。
到这里整个消息的传递流程就讲完了,说的比较乱,下面大致整理一下:
首先要了解的就是几个类的作用:
Handler:复制消息的发送和最终的处理;
Message:消息的实体,用来保存我们要传递的数据;
MessageQueue:消息队列,存储消息;
Looper:复制循环遍历取出消息并且分发给各自的Handler处理。相当于邮局分派信件,所以每个线程只能有一个Looper。
|