前言
从事开发工作的同仁,提到消息大家应该都不陌生。可以说消息机制在任何系统都有它的身影。而我们日常开发中多多少少都不可以避免会涉及有关消息机制方面的内容。在Android系统中消息机制主要是指Handler的运行机制,而Handler是Android消息机制的上层接口,因此,在Android开发中主要与Handler打交道。
ThreadLocal作用
ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储后,只有在指定线程中可以获取到数据,在其他线程中则无法获取数据。因此ThreadLocal可以在不同的线程中互不干扰地存储并提供数据,通过ThreadLocal可以轻松获取每个线程的Looper。
Handler机制简介
定义:是用来结合线程的消息队列来发送、处理“Message对象”和“Runnable对象”的工具(Handler是android.os包下的一个消息处理的类)。
作用:
1、Handler是Android系统封装的一套消息处理的机制;2、是一套子线程用来更新UI的机制;3、任意两个线程的数据传递;
在此很多人说Handler作用是更新主线程UI,这是不严谨的。更新UI只是Handler一个特有的功能而已。至于为什么主线程UI为什么子线程不能更新呢?这里我们暂不过多去阐述(首先UI控件是非线程安全的,其次如果在多线程高并发操作,容易造成不可预期以意外发生,所以不能在非UI线程中操作UI控件)。
消息机制成员
Android中的消息机制主要分为四个部分:Message、Handler、MessageQueue和Looper。
Message
消息对象,也就是MessageQueue里面存放的对象,简单来说就是传递数据的载体。用于线程之间传递的消息,它可以在内部携带少量的信息,用于不同的线程之间交换数据。
Message消息可以携带的内容:what、arg1、arg2等字段可携带一些整型数据,使用obj字段携带一个Object对象。
注意:我们需要创建Message对象的时候,尽量使用Message.obtain()来获取Message实例,因为在Message类里面定义了一个消息池,当消息池里存在未使用的消息时,便返回,如果没有未使用的消息,则通过new的方式创建返回,所以使用Message.obtain()的方式来获取实例可以大大减少当有大量Message对象而产生的垃圾回收问题。
Handler
消息处理者,主要用于发送和接收消息,发送消息一般在主线程构造一个Handler,在其他线程调用Handler的sendMessage()方法,此时主线程的MessageQueue中会插入一条message,然后被Looper使用。Handler在创建时会通过ThreadLocal来获取当前线程的Looper来构造消息循环系统。
注意:Handler的使用一定要在Looper来构造消息循环系统中进行,在Handler对象实例化前需要调用Looper.prepare()不然系统会报异常提示,之后之后还需调用Looper.loop()启动消息循环系统。
MessageQueue
存放消息的地方,MessageQueue是一个消息队列,用来存放Handler发出的消息。这部分消息会一直存在于消息队列中,直到等待被处理。每个线程中只会有一个MessageQueue对象。MessageQueue是单链表维护,在插入和删除上有优势。
注意:如果存入的消息超出MessageQueue存储长度,那么将导致阻塞状态,直到前面的消息被处理,再进入MessageQueue,如果MessageQueue中没有消息,那么MessageQueue就会进入休眠状态,直到有消息进来。
Looper
消息调度者,每一个线程只有一个Looper,每个线程在初始化Looper之后,Looper会维护好该线程的消息队列,用来管理MessageQueue,并处MessageQueue中的Message。另外它跟它的线程是绑定的,处理消息也是在Looper所在的线程去处理,所以当我们在主线程创建Handler时,它就会跟主线程唯一的Looper绑定,从而我们使用Handler在子线程发消息时,最终也是在主线程处理,达到了异步的效果。
注意:主线程创建时,会创建一个默认的Looper对象,而Looper对象的创建,将自动创建一个MessageQueue。其他非主线程,不会自动创建Looper
疑惑?
看完四类型简介,我们发现在Handler介绍中,在要注意提现“Handler对象实例化前需要调用Looper.prepare(),之后之后还需调用Looper.loop()”,为什么平常在Activity组件或其他组件下使用Handler从来没有执行过Looper.prepare()和Looper.loop()两个方法呢?在这不妨贴出程序运行的main方法看看,应该就会明白为什么在主线程中不需要执行Looper.prepare()和Looper.loop()。
public static void main(String[] args) {
...
//1、prepare
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//2、prepare
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
通过以上代码我们可以发现,在ActivityThread被创建时就会初始化Looper,因此主线程中可以直接使用Handler。所以这就为什么我们在主线程中为什么没有执行过Looper.prepare()和Looper.loop()。
消息机制使用
在介绍消息机制使用前,我们先大致看一下交互流程图,如下:
与Handler有关的几个发送Message的方法
这里简述一下与Handler对象有关的几个方法,这几个方法都是用来发送消息用的。
第一个方法:sendMessage(Message msg),该方法只有一个参数,是个消息对象。立即发送消息。
/**
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in {@link #handleMessage},
* in the thread attached to this handler.
*
* @return Returns true if the message 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 sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
第二个方法:sendMessageDelayed(Message msg, long delayMillis),该方法有两个参数,第一个是一个消息对象,第二个参数是延时时间参数。主要用于从发送时开始延时发送消息。
/**
* Enqueue a message into the message queue after all pending messages
* before (current time + delayMillis). You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
第三个方法:sendMessageAtTime(Message msg, long uptimeMillis),该方法有两个参数,第一个是一个消息对象,第二个参数是时间点参数。主要用于设定未来某个时间点发送消息。
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* Time spent in deep sleep will add an additional delay to execution.
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
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);
}
第四个方法:sendMessageAtFrontOfQueue(Message msg),该方法只有一个参数,是个消息对象。立即发送Message到队列,而且是放在队列的最前面。该方法从文字上理解,是将消息放到MessageQueue最前面,可是我们简单的分析源码发现,从效果来说与第一种方法是一模一样的,但是如果从执行效率来说这个方法少执行了第二和第三方法,更优于第一种。
/**
* Enqueue a message at the front of the message queue, to be processed on
* the next iteration of the message loop. You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
* <b>This method is only for use in very special circumstances -- it
* can easily starve the message queue, cause ordering problems, or have
* other unexpected side-effects.</b>
*
* @return Returns true if the message 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 sendMessageAtFrontOfQueue(Message msg) {
MessageQueue queue = mQueue;
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, 0);
}
第五个方法:sendEmptyMessage(int what)。
/**
* Sends a Message containing only the what value.
*
* @return Returns true if the message 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 sendEmptyMessage(int what) {
return sendEmptyMessageDelayed(what, 0);//此处调用的第六个方法。
}
第六个方法:sendEmptyMessageDelayed(int what, long delayMillis)。
/**
* Sends a Message containing only the what value, to be delivered
* after the specified amount of time elapses.
* @see #sendMessageDelayed(android.os.Message, long)
*
* @return Returns true if the message 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 sendEmptyMessageDelayed(int what, long delayMillis) {
//自定义了Message对象,且重新给what赋值
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);//此处调用的第二个方法。
}
第七个方法:sendEmptyMessageAtTime(int what, long uptimeMillis)。
/**
* Sends a Message containing only the what value, to be delivered
* at a specific time.
* @see #sendMessageAtTime(android.os.Message, long)
*
* @return Returns true if the message 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 sendEmptyMessageAtTime(int what, long uptimeMillis) {
//自定义了Message对象,且重新给what赋值
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);//此处调用的第三个方法。
}
第八个方法:enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis),有三个参数:第一个参数为MessageQueue对象,第二个参数为Message对象,第三个参数为时间参数。
/***
*设置msg的target变量,并将target指向自己
*如果Handler的mAsynchronous值为true(默认为false,即不设置),则设置msg的flags值,让是否异步在Handler和Message达成统一。
*调用MessageQueue的enqueueMessage()方法
*/
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
通过分析以上源码我们可以发现,handler最终调用的发送消息的方法是enqueueMessage;在这个方法中主要了做了三件事,分别为:设置msg的target变量,并将target指向自己;判断mAsynchronous值,如果Handler的mAsynchronous值为true(默认为false,即不设置),则设置msg的flags值,让是否异步在Handler和Message达成统一;调用enqueueMessage()方法。
接收Message的方法
接收Message方法:dispatchMessage(Message msg),该方法将在在Looper类中的消息循环中被调起,用于处理消息。
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
与Looper有关的几个方法
以下简述Looper类的初始化方法和消息循环方法(消息循环主要是消息处理功能)。
第一个方法:public static void 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);//此处调用于创建或取Looper对象并存储
}
第二个方法:public static void prepareMainLooper()
/**
* 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);//此处调用于创建或取Looper对象并存储
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
第三个方法:prepare(boolean quitAllowed),quitAllowed参数是用于判断该MessageQueue是否可退出,通过Looper.prepare()创建的都是可退出的,UI线程的MessageQueue是不可以退出的
/***
*quitAllowed参数 如果quitAllowed为true,则MessageQueue可退出的;
* 如果quitAllowed为false,则MessageQueue不可以退出;
*/
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));
}
第三个方法:loop(),消息循环方法,在该方法中处理消息。
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
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;
.....//此处省略部分代码
for (;;) {
Message msg = queue.next(); // might block
.....//此处省略部分代码
try {
msg.target.dispatchMessage(msg);//此处获取到的消息发送给Handler;
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
.....//此处省略部分代码
}
}
与MessageQueue有关的几个方法
第一个方法:boolean enqueueMessage(Message msg, long when),有两个参数,第一个参数是message对象,第二个参数为时间参数。该方法主要用于将message放入队中
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
.....//此处省略部分代码
//判断头部是否为空,或者入队消息的时间小于当前时间则就将message插到队首
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//寻找队尾或者在自己发送时间之后的位置,然后将message插入队列中
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
.....//此处省略部分代码
}
return true;
}
第二个方法:Message next(),该方法主要用于从对中取出消息。
Message next() {
/.....//此处省略部分代码
//当为-1时代表消息队列中没有消息,则进入等待
int pendingIdleHandlerCount = -1; // -1 only during first iteration
//执行下一条消息时还需要等待的时间
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞操作,或者消息队列被唤醒,都会返回)
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
//当消息触发时间大于当前时间,则设置下一次轮询的超时时长
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 从对中获取一条消息,并返回消息
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
.....//此处省略部分代码
}
.....//此处省略部分代码
}
}
Handler的使用
1、首先创建Handler
Handler TestHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//此处接收从MessageQueue(队)中发出来的消息;
}
};
2、创建子线程发送消息
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message = Message.obtain();
message.what = 0;
TestHandler.sendMessage(message);//向对中发送message;
}
}).start();
在Handler真正的使用中,我通过以上的例子,发现其实handler使用起来很简单,只要定义一个接受的handler和发送的子线程就完事了(发送不一定要在子线程完成,主线程也可以发送message消息)。
消息机制使用需要注意的
到这基本就描述完了Handler机制了,不过在这需要注意的是,Handler的使用例子为了方便起见,是没考虑任何其他情况,最最简单的使用方式,但是这种会引发一个问题,那就是内存泄漏。静态内部类是持有外部类,所以Handler或子线程最好采用弱应用的静态内部类创建。如下:
public class TestClass {
Handler TestHandler = new MyHandle(this);
//静态内部类
static class MyHandle extends Handler {
//弱应用对象
WeakReference<TestClass> weakReference;
public void MyHandle(TestClass cl) {//cl为相应的类名
this.weakReference = new WeakReference<TestClass>(cl);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (weakReference != null && weakReference.get() != null) {
if (msg.what == weakReference.get().HANDLE_DISCOUNT_TIME) {
weakReference.get().setUpdataTime(runTime);
}
}
}
}
private void setUpdataTime(long paramNowTime) {
}
}