今天我们的主角:Android消息机制——Handler
Handler是什么:
Android系统中线程间传递消息的一种机制
Handler作为这么重要的一种机制,虽然我们在使用的时候基本上只用到这一个类,但是还是有必要了解一下与之相关的类。
Message:消息的载体
MessageQueue:消息队列,负责存储消息
Handler:负责发送和处理消息(发送和处理不在同一个线程)
Looper:通过死循环来取出消息并且通知Handler处理
我们先通过代码来看看基本用法
[code]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) {super.handleMessage(msg)Log.d(\"ZLog AActivity\", \"接收到消息: $msg\")}}Thread {kotlin.run {val msg = Message()msg.what = 110msg.arg1 = 1msg.arg2 = 2msg.obj = \"obj\"handler.sendMessage(msg)}}.start()}}
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的源码我们可以看到它还有几个带参数的构造方法:
[code] public Handler() {this(null, false);}public Handler(@Nullable Callback callback) {this(callback, false);}public Handler(@NonNull Looper looper) {this(looper, null, false);}public Handler(@NonNull Looper looper, @Nullable Callback callback) {this(looper, callback, false);}@UnsupportedAppUsagepublic Handler(boolean async) {this(null, 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();if (mLooper == null) {throw new RuntimeException(\"Can\'t create handler inside thread \" + Thread.currentThread()+ \" that has not called Looper.prepare()\");}mQueue = mLooper.mQueue;mCallback = callback;mAsynchronous = async;}@UnsupportedAppUsagepublic Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {mLooper = looper;mQueue = looper.mQueue;mCallback = callback;mAsynchronous = async;}
其中主要的就2个,一个传了Callback,一个传了Looper。Callback为消息的回调接口,只有一个方法:
[code]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方法最后调用的地方:
[code] private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {msg.target = this;msg.workSourceUid = ThreadLocalWorkSource.getUid();if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}
到这里我们可以看到,先给msg设置了一个target,就是一个handler对象,设置为了本身,然后就是将消息添加到队列中。
下面我们来看一下Looper:
通过刚刚的Handler构造方法中可以看到Looper有一个静态方法myLooper,我们先进入这个方法看看:
[code]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方法:
[code]public static void loop() {final Looper me = myLooper();if (me == null) {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.\"+ Process.myUid() + \".\"+ Thread.currentThread().getName()+ \".slow\", 0);boolean slowDeliveryDetected = false;for (;;) {Message msg = queue.next(); // might blockif (msg == null) {// No message indicates that the message queue is quitting.return;}// This must be in a local variable, in case a UI event sets the loggerfinal Printer logging = me.mLogging;if (logging != null) {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;final long dispatchEnd;Object token = null;if (observer != null) {token = observer.messageDispatchStarting();}long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);try {msg.target.dispatchMessage(msg);if (observer != null) {observer.messageDispatched(token, msg);}dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;} catch (Exception exception) {if (observer != null) {observer.dispatchingThrewException(token, msg, exception);}throw exception;} finally {ThreadLocalWorkSource.restore(origWorkSource);if (traceTag != 0) {Trace.traceEnd(traceTag);}}if (logSlowDelivery) {if (slowDeliveryDetected) {if ((dispatchStart - msg.when) <= 10) {Slog.w(TAG, \"Drained\");slowDeliveryDetected = false;}} else {if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, \"delivery\",msg)) {// Once we write a slow delivery log, suppress until the queue drains.slowDeliveryDetected = true;}}}if (logSlowDispatch) {showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, \"dispatch\", msg);}if (logging != null) {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();if (ident != newIdent) {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);}msg.recycleUnchecked();}}
代码比较长,其实我们只需要关注其中的for循环,这里的for循环没有设置任何条件,那么就是一个死循环,它就是在这里面通过消息队列取出的消息,还记得handler的发送方法吗?在发送的时候,给每个message都设置了一个target,就是handler本身。所以这里取出消息后调用了target的dispatchMessage来分发消息,最终会调用我们重写的handleMessage将消息给到我们自己处理。
到这里整个消息的传递流程就讲完了,说的比较乱,下面大致整理一下:
首先要了解的就是几个类的作用:
Handler:复制消息的发送和最终的处理;
Message:消息的实体,用来保存我们要传递的数据;
MessageQueue:消息队列,存储消息;
Looper:复制循环遍历取出消息并且分发给各自的Handler处理。相当于邮局分派信件,所以每个线程只能有一个Looper。