Android提供的 Handler
消息收发处理机制,其根本目的就是解决多线程并发的问题,与之关联的 Looper
,Message
,MessageQueue
,无论是日常开发或者面试都是出场率极高,所以无论如何都必须搞清楚
Handler
导包要注意啦,package android.os
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
这是我们通常创建一个 Handler
的方法,Handler
内部创建调用实现:
public Handler() {
this(null, false);
}
再跟进 Handler(Callback callback, boolean async)
方法实现:
public Handler(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 that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
-
FIND_POTENTIAL_LEAKS
是Handler中设置的一个标志,如果将这个值设置为true用于检测扩展继承Handler类是否存在潜在的内存泄漏 -
Looper.myLooper()
由于是默认创建的handler,所以会自动绑定主线程的Looper,并将主线程Looper维护的消息队列(mLooper.mQueue
)作为自己(Handler)的消息走向(往消息队发送消息) -
callback
用于是否拦截发送的消息(后面会解释) -
mAsynchronous
的作用是设置消息是否异步,这意味着发送的消息将不受Looper同步设置的影响
至此,通过 new Handler()
方法创建handler就完成了(关于Looper,后面会详解)
创建
Handler
实例 → 绑定主线程的Looper
(只是默认绑定主线程,也可以调用其他构造方法指定Looper
) → 连接绑定Looper
所维护的MessageQueue
→ 设置消息拦截回调和消息是否异步
那Handler怎么发送消息?
通常情况,我们会使用 handler.sendMessage()
来发送消息,如下:
Message message = handler.obtainMessage();
message.agr1 = 1;
.... //message信息携带操作
handler.sendMessage(message);
这样消息就发送出去了,只要重写了handler的 handleMessage(Message message)
便可以接收到消息进行自己的逻辑处理。接下来看一下细节实现:
-
handler.obtainMessage()
会返回一个Message对象
public final Message obtainMessage()
{
return Message.obtain(this);
}
继续跟进Message的 obtain(this)
方法
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
obtain()
方法,从全局池中返回一个新的 Message
对象实例,可以在很多情况下避免分配新的对象。并给这个消息对象设置了 target
属性,指定这个消息由谁发送并由谁处理。下面是 obtain()
方法:
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
这样,我们就拿了一个指定了消息发送和处理者的 Message
,它的消息携带后面再说。当然,也可以不用 obtainMessage()
方法来获取消息对象,完全可以自己 new Message()
对象来携带信息并发送,但是 obtain()
方法的注释也说明了,有更好的方法为什么不用呐
-
sendMessage(Message msg)
发送消息
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
调用了 sendMessageDelayed(msg, 0)
方法
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
//处理了时间不正确的情况,自己写代码的时候也应该要考虑完善各种情况
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
//默认构造方法中设置了关联主线程Looper的消息队列,所以如果消息队列为空则抛出异常
if (queue == null) {
RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//再次确认消息的发送和处理者,所以即便自己 new Message() 忘了设置target也不用担心
msg.target = this;
//这里用到了默认构造方法中设置的 async 属性,设置消息是否异步
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
queue.enqueueMessage(msg, uptimeMillis)
方法有点复杂,这里就不贴了,它做的事情就是根据 msg
和 uptimeMillis
与消息队列里现有的消息比较,决定消息插入队列的位置
当然,也可以直接调用 sendMessageDelayed(Message msg, long delayMillis)
来延时发送消息
OK,现在消息已经发送到消息队列里面了,先处理完一些遗漏的地方,并先来看看 Handler
是怎么在 handleMessage(Message msg)
方法中处理的,后面再说它怎么拿到消息 (剧透:Looper的死循环)
。
Handler的其他构造方法
Handler
有很多构造方法,在创建 Handler
的时候也可以使用 new Handler(Callback callback)
来创建。由上面的分析可见,new Handler()
默认创建的时候,调用了this(null, false)
方法,默认创建一个空的 callback
,那 callback
到底有什么作用?
首先得知道,Callback
是 Handler
内部的一个接口,它包含一个返回 boolean
值的 handleMessage(Message msg)
方法,区别于 Handler
的 handleMessage(Message msg)
(这个是不带返回值的)
public interface Callback {
public boolean handleMessage(Message msg);
}
来创建一个带 Callback
的 handler
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
}){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
调用的是 Handler(Callback callback)
方法来创建,传入了一个 callback
并重写 handlerMessage(Message msg)
public Handler(Callback callback) {
this(callback, false);
}
还是调用的 Handler(Callback callback, boolean async)
方法来创建 Handler
,不清楚看上面的分析。
好了,到了 callback
的作用了,这个方法其实就是在处理消息的时候用到的,先来看一下 Handler
的 handleMessage(Message msg)
方法(),它负责处理接收到的消息,也是我们经常需要重写的方法,那 Handler
的 handleMessage(Message msg)
是怎么被触发的,答案就在 Looper
的死循环中 --> msg.target.dispatchMessage(msg)
(后面详解)
Message
的 target
上面已经说明了,它指定了消息发送和处理的 Handler
, msg.target.dispatchMessage(msg)
这句话就是触发 Handler
进行消息处理,先上源码
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
//首先检查msg是否设置了回调
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
//重点在这里,callback的handleMessage()方法的返回值决定了是否拦截消息
//重点在这里,callback的handleMessage()方法的返回值决定了是否拦截消息
//重点在这里,callback的handleMessage()方法的返回值决定了是否拦截消息
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
重要的事情说三遍,拦截以后的消息,不会调用 Handler
的 handleMessage(Message msg)
方法,所以如果你需要对某些特定消息进行拦截,可以给一个 callback
,并根据消息携带的信息来决定是否拦截
Handler的其他用法
-
Handler
不仅能发送Message
对象,还能post(Runnable runnable)
到消息队列中
/**
* Causes the Runnable r to be added to the message queue
* The runnable will be run on the thread to which this handler is
* attached.
*
* @param r The Runnable that will be executed.
*
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
注释清晰明了
可能上面 dispatchMessage()
的时候还会有疑惑,为什么 Message
也有 Callback
?为什么 Runnable
能直接扔到消息队列中? 。现在算是明白了。尽管 post()
出去的是一个 Runnable
但 Handler
自己帮我们把它封装成了 Message
Handler post Runnable
后是直接把run()
中的代码送回到了Handler
附属的线程,通常就是创建Handler
的主线程,所以我们经常使用handler.post()
在子线程中更新UI
当然,也可以使用 postDelayed(Runnable r, long delayMillis)
来延时发送,其实它最终还是调用的 sendMessageDelayed(getPostMessage(r), delayMillis)
-
Handler
发送 空的Message
对象
/**
* Sends a Message containing only the what value.
*/
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
最终也会调用我们熟悉的方法
空消息只会附带一个
int
类型的what
值,可以用来进行一些特殊类型或者拦截消息的判断
(Message消息携带后面详解)
//////////////////////////////////////// 华丽的分割线 ////////////////////////////////////////
//////////////////////////////////////// 华丽的分割线 ////////////////////////////////////////
//////////////////////////////////////// 华丽的分割线 ////////////////////////////////////////
Looper
对 Looper
的理解现在还不是很到位,知道多少先记录多少
开始之前要扯一点其他的东西,做Android开发也这么久了,一直都知道Android是基于JAVA的 (当然现在Kotlin才是亲儿子了),也知道每个JAVA程序都是有一个主入口 main()
的,之前却一直不知道Android程序的主入口在哪里,就只知道通过 AndroidMamifest.xml
配置好了 intent-filter
就可以启动 activity
,很尴尬......
那Android的主入口到底在哪里?首先得知道 ActivityThread
,好吧,其实我也不知道,附上传送门
Android ActivityThread(主线程或UI线程)简介
ActivityThread
它管理应用进程的主线程的执行(相当于普通Java程序的main入口函数),并根据AMS的要求(通过IApplicationThread接口,AMS为Client、ActivityThread.ApplicationThread和Server)负责调度和执行activities、broadcasts和其它操作。
主线程既要处理Activity组件的UI事件,又要处理Service后台服务工作,通常会忙不过来。为了解决此问题,主线程可以创建多个子线程来处理后台服务工作,而本身专心处理UI画面的事件。
【主线程】的主要责任:
• 快速处理UI事件。而且只有它才处理UI事件, 其它线程还不能存取UI画面上的对象(如TextView等),此时, 主线程就叫做UI线程。基本上,Android希望UI线程能根据用户的要求做出快速响应,如果UI线程花太多时间处理后台的工作,当UI事件发生时,让用户等待时间超过5秒而未处理,Android系统就会给用户显示ANR提示信息。只有UI线程才能执行View派生类的onDraw()函数。
• 快速处理Broadcast消息。【主线程】除了处理UI事件之外,还要处理Broadcast消息。所以在BroadcastReceiver的onReceive()函数中,不宜占用太长的时间,否则导致【主线程】无法处理其它的Broadcast消息或UI事件。如果占用时间超过10秒, Android系统就会给用户显示ANR提示信息。
注意事项:
• 尽量避免让【主线程】执行耗时的操作,让它能快速处理UI事件和Broadcast消息。
• BroadcastReceiver的子类都是无状态的,即每次启动时,才会创建其对象,然后调用它的onReceive()函数,当执行完onReceive()函数时,就立即删除此对象。由于每次调用其函数时,会重新创建一个新的对象,所以对象里的属性值,是无法让各函数所共享。
跑得有点远,但是这些都是我们应该知道或者熟悉的。之前又一次面试,被问到Framewark与应用层怎么通讯?当时一脸懵逼,看了传送门那篇文章,现在还能说个大概,C/S方式,AMS,Binder等等。下面进入正题,知道了Android管理应用进程的入口在 ActivityThread
中,那我们进去看一下(ActivityThread
的 main()
方法)
public static void main(String[] args) {
...... //自动屏蔽其他不需要或者看不懂的代码
Looper.prepareMainLooper();
......
Looper.loop();
......
}
好像发现了什么的感觉,Looper
出现了,初始化了一个主线程的 Looper
,然后执行了它的 loop()
方法。我们可以这样认为,每个Android程序(进程)启动的时候,会自动创建一个 Looper
对象,并开启循环(调用 loop()
方法)拉取 MessageQueue
(Looper
对象中维护的消息队列)的消息。可能表述不是很准确,但是可以这样的理解。接着跟进里面的方法:
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
注意最后一句注释:应用的主 Looper
会由Android系统自动创建,因此我们不需要手动调用这个方法。现在好像也不明白这个方法干了什么,准备了什么,开启了一个同步锁保证线程安全,最后把 myLooper()
返回值赋给了 sMainLooper
,继续跟进 prepare()
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
这里看到了 new Looper(quitAllowed)
明白是创建了一个 Looper
,再结合 myLooper()
一起解释:
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
到了这里,我们需要搞清楚 sThreadLocal
、sMainLooper
等属性到底是什么,先来看一下 Looper
类中最开始定义的几个变量:
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal sThreadLocal = new ThreadLocal();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
-
sThreadLocal
属性,ThreadLocal
这个东西应该翻译为:线程局部变量,不是线程!!!,它干了什么事?(变量是同一个,但是每个线程都使用同一个初始值,也就是使用同一个变量的一个新的副本。)保证了Looper
的线程安全,并保证每个线程使用的是同一个Looper
的不同副本。(有点晦涩难懂,先大致理解概念就行) -
sMainLooper
属性,应用程序的主Looper
-
mQueue
属性,Looper
内部维护的消息队列 -
mThread
属性,Looper
所在的线程,即我们熟悉的UI线程(主线程)
好了,回到上面的方法流程:
prepareMainLooper()
调用了 prepare(false)
方法创建了一个不允许消息队列退出的 Looper
(主线程 Looper
当然不允许退出,只要应用程序还在执行就会一直拉取消息或者阻塞,消息队列都没了去哪里拉取?),并将这个 Looper
复制给了本地线程局部变量 sThreadLocal
。随后设置主线程的 Looper
为刚才我们创建的 Looper
。
注意:
throw new RuntimeException("Only one Looper may be created per thread")
这个异常,每个线程中只能存在一个Looper
,每个线程中只能存在一个Looper
,每个线程中只能存在一个Looper
(重要的事情说三遍)
Looper.prepareMainLooper()
方法执行完后,主线程的 Looper
就准备就绪了,接下来看看 Looper.loop()
在干什么:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
//从该线程中取出对应的 Looper 对象
final Looper me = myLooper();
if (me == null) {
//这里可以看到,必须先调用 Looper.prepare() 初始化
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 拿到Looper 对象维护的消息队列
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();
//重点 重点 重点
for (;;) {
Message msg = queue.next(); // might block
if (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 logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
//调用消息绑定的Handler进行消息处理
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
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();
}
}
检查和判断后,便开启了一个死循环遍历消息,并调用 Handler
的 dispatchMessage(msg)
方法进行消息处理,这个我们上面追踪 Handler
的时候也提到了。至此 Looper
的工作便是正式开启了,一直到程序运行结束。
之前面试被问到:
Looper.loop()
既然是开启了一个死循环,为什么没有把程序给搞死? 这是因为当MessageQueue
里有消息的时候,Looper
会一直循环取消息,当
MessageQueue
为空的时候,Looper
所在的线程就会阻塞,直到再有消息进入消息队列,Looper
会再次被唤醒
要终止 Looper
循环取消息,可以调用 quit()
方法终止 loop()
//////////////////////////////////////// 华丽的分割线 ////////////////////////////////////////
//////////////////////////////////////// 华丽的分割线 ////////////////////////////////////////
//////////////////////////////////////// 华丽的分割线 ////////////////////////////////////////
Message
所在包:package android.os
Message
的源码相对来说要简单一些,主要包括了 Message
的创建,消息内容和信息的设置以及消息回收,这里简单介绍 Message
消息内容的设置,先来看它的几个属性:
/**
* User-defined message code so that the recipient can identify
* what this message is about. Each {@link Handler} has its own name-space
* for message codes, so you do not need to worry about yours conflicting
* with other handlers.
*/
public int what;
/**
* arg1 and arg2 are lower-cost alternatives to using
* {@link #setData(Bundle) setData()} if you only need to store a
* few integer values.
*/
public int arg1;
/**
* arg1 and arg2 are lower-cost alternatives to using
* {@link #setData(Bundle) setData()} if you only need to store a
* few integer values.
*/
public int arg2;
/**
* An arbitrary object to send to the recipient. When using
* {@link Messenger} to send the message across processes this can only
* be non-null if it contains a Parcelable of a framework class (not one
* implemented by the application). For other data transfer use
* {@link #setData}.
*
* Note that Parcelable objects here are not supported prior to
* the {@link android.os.Build.VERSION_CODES#FROYO} release.
*/
public Object obj;
注释也描述的很清楚了
-
what
int
类型,可以用作消息唯一标识,或自行指定消息类型 -
arg1
,arg2
int
类型,可以保存一些int
型数据信息 -
object
Object
类型,这里就可以保存你想让Message
携带的各种类型信息了
这些都是公共属性,直接访问和复制都可以
除了上面这些公共的属性,Message
还又如下属性:
/*package*/ int flags;
/*package*/ long when;
/*package*/ Bundle data;
/*package*/ Handler target;
/*package*/ Runnable callback;
// sometimes we store linked lists of these things
/*package*/ Message next;
-
flags
int
型 ,用于给消息做一些标记 -
data
Bundle
类型,可以通过getData()
和setData(Bundle data)
来获取和设值 -
when
long
类型,有没有觉得有点熟悉?因为我们在Handler
消息发送的时候说到过,通常用它来记录消息发送的延时时间 -
target
指定消息发送和处理的Handler
,即消息的来源和去向 -
callback
Runnable
自定义消息回调
That`s all 如有遗漏,以后再补充..........