一、什么是Handler
1.Android SDK中用来处理异步消息的核心类
2.子线程可以通过Handler来通知主线程进行UI更新
二、Handler机制的核心类
Handler:mLooper,mQueue(mLooper是从Looper中的ThreadLocal对象中获取的,整个app中,所有的Looper对象都是保存在Looper类中的ThreadLocal静态对象中,mQueue是根据mLooper中的mQueue赋值的)
Message
Looper:mQueue(是一个MessageQueue对象)
MessageQueue:采用单链表的方式存储Handler需要处理的Message信息
Message:
在整个消息机制中,Message又叫Task,封装了任务携带的信息和处理该任务的Handler:
1.尽管Message有public默认的构造方法,但是应该通过Message.obtain()来从消息池中获取空消息对象,以节省资源。
2.Message如果只需要携带简单的int信息,请优先使用arg1和arg2来传递消息,这比使用Bundle更节省内存。
Looper:轮循器
在Looper内部有一个Thread对象,存放Looper对应的当前线程对象。Looper内部有一个MessageQueue队列。用来存放消息。Looper轮循的时候,都是从当前线程的Looper对象中取出MessageQueue,而当前线程的Looper对象从ThreadLocal中获得。而ThreadLocal对象则是在Looper中的一个静态对象,是一个全局的。
三、原理分析
1.Looper实例创建和保存原理分析
MessageQueue 就是设计模式中的缓冲区,它负责接收生产者发送过来的数据先进先出的队列形式,保存着所有消息。在UI Thread中通过looper 不断从MessageQueue 取出消息在执行任务。
Handler中获取Looper对象:
public Handler(Callback callback, boolean async) {
//省略一些无关代码
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;
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
在Android中,所有的Looper对象都是保存在一个全局的ThreadLocal对象中,Handler在线程中获取Looper对象的时候,是调用ThreadLocal的get方法,ThreadLocal的get方法中,会优先获取当前线程对象。
ThreadLocal的get方法的实现:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//this是ThreadLocal对象
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;//value即为Looper对象
return result;
}
}
return setInitialValue();
}
//ThreadLocal的getMap的源码:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
其实每个Thread线程中都会有一个ThreadLocalMap对象。每个线程对应的Looper实例,其实就是保存在每个线程实例中的ThreadLocalMap实例中
Looper 的主要工作就是维护MessageQueque中的消息队列,它负责从MessageQueue中取出要执行的消息任务,先判断Looper是否为null,不为null就循环状态不断从MessageQueue中取出消息,然后通过dispatchMessage派发出去就行处理。
看下Looper的创建过程:
public static void loop() {
// 调用Looper.myLooper()方法,从ThreadLocal中取出当前线程的Looper对象
final Looper me = myLooper();
// 如果当前线程中的Looper对象为null,则会抛出异常
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
.....
for (;;) {
// 通过一个死循环,从MessageQueue中不停的取出消息
Message msg = queue.next(); // might block
if (msg == null) {
// 由于刚创建的MessageQueue就开始轮询,队列里没有消息
// 等到Handler调用send或者post发送消息,进而调用MessageQueue.enqueueMessage后
// 队列里才有消息
return;
}
........
try {
// 分发信息,target是一个Handler对象,这里就是调用Handler来分发信息的。
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
.........
msg.recycleUnchecked();
}
}
每个线程只能有一个Looper对象,而且是通过ThreadLocal来存放,其他线程无法访问当前的Looper。
static final ThreadLocal sThreadLocal = new ThreadLocal();
Looper可以让一个普通线程具有消息循环的能力,这是源码给出的一段示例。
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
每个application都默认拥有一个Looper对象注释可以看到。在启动应用程序进程过程中,会在最后调用ActivityThread.main方法,在ActivityThread.main方法中会调用Looper.prepareMainLooper()方法初始化主线程的Looper实例,所以说UI线程默认就有一个Looper对象,可通过Looper.myLooper()方法获取到。因为Looper中用来保存Looper实例的ThreadLocal是一个静态常量,内存中只有这一个。又根据ThreadLocal的set和get的特性,所以可以根据不同的线程取出不同的值
/**
* 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() {
// 这里传入false,代表MessageQueue是不允许退出的
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
/**
* Returns the application's main looper, which lives in the main thread of the application.
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
常用的方法 prepare()创建一个looper对象,在其他线程中想具有消息循环功能这个方法就必须调用。
在Looper.prepare()方法中,调用了重载方法参数传的是true,表示是允许退出,在创建Looper对象的时候,会将Looper所属的当前线程是哪个缓存在Looper实例中
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在Looper实例中,通过调用其loop()方法,开始循环取出消息
public static void loop() {}
2.Handler的消息发送和处理过程分析
Handler 是负责把Message压入Queue中,还负责处理Message。Handler工作必须依赖Looper才行,没有Looper对象会抛出RuntimeException异常,不管是post sendMessage 还是延时的消息发送,最终都会到enqueueMessage方法中把消息传到缓冲区等待Looper处理。
创建Handler
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());
}
}
// 获取当前线程中的Looper实例
mLooper = Looper.myLooper();
// 如果当前线程中的Looper对象为null,则抛出异常,从这里可以看出创建Handler对象
// 当前线程就必须要先有Looper对象
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
// 每个Handler中使用的MessageQueue对象,都是从Looper对象中取出的
// 一个线程只有一个Looper对象,可以有多少Handler对象
// 所以一个线程中的多个Handler对象是共用一个MessageQueue的
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
创建Message
可以直接new Message,但是有更好的方式Message.obtain。因为可以检查是否可以有复用的Message用过复用避免过多的创建,销毁Message对象达到优化内存和性能的目的
public static Message obtain(Handler h) {
// 调用重载的obtain方法
Message m = obtain();
// 并绑定创建Message对象的Handler对象
m.target = h;
return m;
}
public static Message obtain() {
// sPoolSync是一个Object对象,用来同步保证线程安全的
synchronized (sPoolSync) {
// sPool其实就是Handler.dispatchMessage后通过recycleUnchecked回收用以复用的Message
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();
}
Handler内部发送消息方法源码解析:
(1)sendMessage方法,内部调用的是sendMessageDelayed,只不过时间传0
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
(2)sendMessageDelayed
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//SystemClock.uptimeMillis()表示从开机到现在的毫秒数
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);
}
// 将Message与发送Message的Handler做绑定
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 这部分,是对Message进行赋值,将当前的Handler对象赋值给Message.target
// 但是如果Message是采用obtain方法创建的,则在这个方法内部会对target赋值
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在Handler的enqueueMessage方法中可以看出,每个Message实例,在这里会对其target赋值Handler实例,赋值的目的就是为了在Looper.loop()方法调用的时候用来在Looper.loop()方法中取出消息并且通过消息的target来将消息分发给对应的Handler。这里的Handler是在发送消息的时候,将Message与Handler先做个绑定,即将Handler实例保存在Message中的target属性中
注意:
System.currentTimeMillis()获取的时间,可以通过System.setCurrentTimeMillis(long)方法,进行修改,那么在某些情况下,一旦被修改,时间间隔就不准了,所以推荐使用SystemClock.uptimeMillis(),这样就可以保证消息队列中每个消息的时间顺序是准确的
MessageQueue内部消息入队处理
而MessageQueue的enqueueMessage,就是将消息入队的操作,MessageQueue是单向链表结构,是采用先入先出的操作来处理消息
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) {
// 判断Looper是否有退出,这是在Looper.quit()方法中调用mQueue.quit(false);设置mQuitting为true的
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
// 回收消息,但是如果消息正在使用,则会抛异常,不会回收
msg.recycle();
return false;
}
// 设置消息为正在使用
msg.markInUse();
// 获取当前时间
msg.when = when;
Message p = mMessages;
boolean needWake;
// p相当于当前Message的head
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 {
// 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循环寻找插入点,直到找到为null的时候,因为这个时候p为当前节点,而prev为前一个节点,找到为空的当前节点,然后在这个位置插入
// for循环遍历Message队列,如果当前要插入的msg的时间
// 大于当前遍历到的Message队列中的节点的时间
// 则继续遍历,如果当前要插入的msg的时间when小于当前遍历到
// 的节点p的时间when,则将消息插入到p节点之前
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
// 设置需要插入的Message的下一个节点为null
// 设置前一个节点的下一个节点为Message
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
// 这里是判断线程是否需要被唤醒
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
这里的nativeWake,其实是调用了frameworks/base/core/jni/android_os_MessageQueue.cpp中的
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast(ptr);
nativeMessageQueue->wake();
}
而这里的NativeMessageQueue的wake()方法,其实就是在android_os_MessageQueue.cpp中实现的
void NativeMessageQueue::wake() {
mLooper->wake();
}
Looper.loop()循环处理消息并分发
MessageQueue的出队操作,其实就是next()方法,返回一个Message。在线程中,创建Looper实例之后,通过调用Looper.loop()方法,循环从每个Looper实例中对应的MessageQueue消息缓存队列中读取消息,并且分发给Handler处理
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
// 是否退出的判断
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
// 无限for循环
for (;;) {
if (nextPollTimeoutMillis != 0) {
// 因为下一条Message尚未到处理时间,则会将等待过程中需要处理的内容交给CPU
Binder.flushPendingCommands();
}
// 这里会有一个等待,在这个等待中设置了一个超时时间,即postDelayed等方式发送的延迟处理的消息,其实是通过等待一定的时间再继续执行的方式来进行
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) {
// 如果当前的msg不为空,但是这个msg中的Handler为空,那么直接拿下一个消息,因为这个消息已经没有Handler来进行处理
// 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;
}
// 将当前消息的指向置为null
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
而在Looper的loop()轮循器中通过MessageQueue.next()方法取出消息
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 (;;) {
// 调用MessageQueue的next()方法,取出下一次要处理的消息
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;
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;
try {
// 获取到Message对象之后,调用这个消息对象中所保存的Handler对象,
//然后通过Handler的dispatchMessage方法分发消息
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
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();
}
}
在调用Looper.loop()方法处理消息的时候,会通过Message中缓存的每个消息对应的target(也就是对应的Handler实例)来发送给对应的Handler,因为在发送消息的时候会设置消息的target
Handler的dispatchMessage方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
而handleMessage就是我们在使用Handler的时候重写的那个方法
public void handleMessage(Message msg) {
}
在Handler的dispatchMessage方法中,如果实现了Handler的Callback接口,并且重写该接口的handleMessage方法,那么该方法一般需要返回为false,目的是为了执行Handler类中的handleMessage方法,如果不执行Handler类中的handleMessage方法,则可以返回为true,但是一般返回false
3.主线程创建Looper实例
在android应用程序进程启动过程中,由系统AMS向Zygote进程发送启动应用程序进程的请求,然后在Zygote接收到该请求之后,通过fock自身创建了应用程序进程,并且通过异常捕获处理的方式捕获ActivityThread.main方法,然后调用,在ActivityThread.main方法中,通过Looper.prepareMainLooper()创建主线程的Looper实例
Looper:
这是ActivityThread类:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("");
// 创建Activity的Looper对象
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);
// 调用Looper循环
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
在创建主线程的Looper实例的时候,默认的quitAllowed是传了false,是不允许退出的。因为sMainLooper!=null的时候会抛出异常,说明每个线程就只能有一个Looper对象。
4.主线程为什么不会阻塞问题
Looper.loop()中的for循环实际上是阻塞了整个Android,而Android之所以不会阻塞,是因为Android是一个消息驱动机制,在ActivityThread中有一个H类对象,这个H类是Handler的子类,Android的所有事件都是通过H类接受到Message之后,然后通过Handler的handleMessage方法进行消息处理的。这个过程,就是在ActivityThread中的main方法中会创建一个ActivityThread对象,然后通过ActivityThread.getHandler就可以获得H对象,也就是Handler对象,这个对象就是在ActivityThread中创建的一个常量对象。
5.Handler导致的内存泄漏问题
Handler导致内存泄露的原因:
- (1)因为定义的匿名内部类,会持有外部类的引用this,可以直接调用外部类的方法和属性。
- (2)生命周期的问题。因为Handler.sendMessageAtTime会调用enqueueMessage,然后msg.target=this,说明Message会持有Handler,而Message在传递过程中,会有延时的情况,比如设置20s执行、20分钟执行,这样就会出现Message持有了Handler,而Handler持有了Activity,但是当Activity销毁的时候,可能Message还没处理,还在延迟等待,这样就导致Activity根据JVM可达性分析算法得出Activity不能被GC回收。
下面这段代码是一个内部类,在Java中内部类或者匿名内部类都会隐私的持有外部类对象,而在Android中使用Handler的一般都是Activity,这就导致如果handler还在执行中而actiivty finsh掉,activity就不能被正常销毁回收。进而GC的时候就导致JVM不能回收Activity,有可能多次操作后就OOM了。
解决办法:
(1)把内部类换成static, static不会隐式持有外部对象。
由于Handler不再持有外部类对象的引用,导致程序不允许你在Handler中操作Activity中的对象了。所以你需要在Handler中增加一个对Activity的弱引用
(2)一般Handler导致的内存泄漏都是因为,消息处理是异步的。finsh的时候消息还在处理等待状态,这个时候可以在activity finsh的时候把handler移除掉,调用removeCallbacks方法移除。
(3)关闭Activity的时候停掉你的后台线程。
如何避免在使用Handler的时候造成内存泄漏呢?
使用显形的引用,1.静态内部类。 2. 外部类
使用弱引用 2. WeakReference
两者同时使用
private static class MyHandler extends Handler {
private final WeakReference mActivity;
public MyHandler(HandlerActivity2 activity) {
mActivity = new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
System.out.println(msg);
if (mActivity.get() == null) {
return;
}
mActivity.get().todo();
}
}
使用弱引用,其实就是将对应的Activity存在弱引用中。
并且在Activity被finish的时候,还要清空MessageQueue中的正在排队的消息。
在onDestroy中调用下面的方法清空
mHandler.removeCallbacksAndMessages(null);
Looper:
public void quit() {
mQueue.quit(false);
}
MessageQueue:
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
// 清除消息,这样消息队列为null,就会进入一个无限等待状态
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// 唤醒等待
nativeWake(mPtr);
}
}
6.子线程中维护的Looper,消息队列无消息的时候处理方法是什么?有什么用?
必须要调用Looper.quit()或者quitSafely(),会把mQuitting赋值为true,会把所有的消息全部清空,然后调用nativeWake(mPtr),唤醒。唤醒是因为清空消息的时候,消息队列为null,而Looper.loop()方法依然会在循环取出消息,就会陷入一个无限等待状态,那么就需要唤醒,在next()方法中退出无限死循环,直接调用return null返回一个空的null,返回空的null的时候,在Looper.loop()方法中就会结束。
因为消息队列无消息的时候,主动调用Looper.quit(),其实就会调用MessageQueue的quit方法。
MessageQueue.quit()
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
// 将正在退出的标志置为true,这样在MessageQueue.next取出消息的时候,就可以将消息返回一个null
// 而在Looper.loop()遍历消息的时候,当取出的消息为null的时候,就直接return
mQuitting = true;
// 清空消息队列
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// 唤醒消息队列,这样做的目的是消息队列为null的时候,MessageQueue是处于阻塞状态
// 而这里唤醒的目的就是为了在next方法中返回一个空消息给Looper.loop()进行处理
nativeWake(mPtr);
}
}
7.线程同步问题
Handler机制中最主要的类MessageQueue,这个类是所有消息的存储队列,是采用优先级队列的方式存储消息,而MessageQueue类中主要的就是消息入队和出队,分别是enqueueMessage和next方法,这两个方法中,都是采用的synchronized关键字代码块的方式来保证消息同步的,传入的锁是this,即对象锁,MessageQueue对象锁,因为一个线程对应一个Looper,而一个Looper对应一个MessageQueue,所以一个线程中不管多少个Handler发送和处理消息,最终都是通过synchronized同步代码块保证消息的入队和出队的同步,保证线程安全。这对于在调用同一个MessageQueue的handler对象来说,他们是在同一个线程的,而在这个线程内,入队和出队都是互斥的。并且因为一个线程只有一个Looper对象,一个Looper对象对应一个MessageQueue,所以在一个线程内,不管有多少个Handler对象一次都只会处理一个消息,因为在Looper.loop()循环遍历消息的时候,每次就只能同步出去一个消息。
四、四种引用
强引用(一般垃圾回收器不回收被强引用的对象)
在一般的Java程序中,见到最多的就是强引用(strong reference)。如Date date = new Date(),date就是一个对象的强引用。对象的强引用可以在程序中到处传递。很多情况下,会同时有多个引用指向同一个对象。强引用的存在限制了对象在内存中的存活时间。假如对象A中包含了一个对象B的强引用,那么一般情况下,对象B的存活时间就不会短于对象A。如果对象A没有显式的把对象B的引用设为null的话,就只有当对象A被垃圾回收之后,对象B才不再有引用指向它,才可能获得被垃圾回收的机会。
软引用 (内存不足的时候,对象被回收)
软引用(soft reference)在强度上弱于强引用,通过类SoftReference来表示。它的作用是告诉垃圾回收器,程序中的哪些对象是不那么重要,当内存不足的时候是可以被暂时回收的。当JVM中的内存不足的时候,垃圾回收器会释放那些只被软引用所指向的对象。如果全部释放完这些对象之后,内存还不足,才会抛出OutOfMemory错误。软引用非常适合于创建缓存。当系统内存不足的时候,缓存中的内容是可以被释放的。
弱引用 (回收器只要发现一个对象的引用是全部是弱引用,就会回收此对象、释放内存)
弱引用(weak reference)在强度上弱于软引用,通过类WeakReference来表示。它的作用是引用一个对象,但是并不阻止该对象被回收。如果使用一个强引用的话,只要该引用存在,那么被引用的对象是不能被回收的。弱引用则没有这个问题。在垃圾回收器运行的时候,如果一个对象的所有引用都是弱引用的话,该对象会被回收。
Handler中使用弱引用来保存Activity对象,就是因为Activity对象如果在Handler执行过程中被销毁回收,那么如果不使用弱引用,这时Activity在Handler中就是属于强引用,则不会被回收,这样就会造成了内存泄漏,而使用弱引用的话,就不会阻止Handler中的Activity对象被回收。
五、Handler.postDelayed实现原理
这部分是对上面Handler原理的加以说明
1.Handler.postDelayed方法其方法内部,调用的是Handler的sendMessageDelayed方法:
public final boolean postDelayed(Runnable r, long delayMillis){
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
2.看下sendMessageDelayed源码:
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
从这个方法中可以看出,延迟时间其实是在SystemClock.uptimeMillis()这个时间的基础上,加上延迟时间的,SystemClock.uptimeMillis()是从系统开机启动到现在的时间,而且不包括休眠时间,因此这是一个相对时间。
用这样的方式,而不是用当前时间,主要也是因为Handler会受到阻塞、挂起状态、睡眠等,这些时候是不应该执行的。
3.sendMessageAtTime
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);
}
这个方法做的工作,就是将Message消息存放到MessageQueue中,但是并没有做延迟存放的操作。存放的这个过程传入uptimeMillis时间,主要是为了对所有的message做排序,按需要执行的顺序将message排序放好,以便在next方法中顺序取出。
上面三个步骤是存放Message的,但是并没有做任何延迟的处理,那么延迟的操作则不是在存的时候进行的,那么只有可能是在取message的时候做了延迟处理
而上面关于Handler的消息处理分发部分的Looper.loop()的源码解析可以看出,loop方法内部,并没有等待或者阻塞等操作,而只有MessageQueue的next方法有一个注释:可能会阻塞,那么看MessageQueue的next方法
4.MessageQueue的next()方法
在MessageQueue中,有一个是否阻塞的标志位,这个是用来判断是否在pollOnce()的时候被阻塞等待了。
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
// 是否退出的判断
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
// 无限for循环
for (;;) {
if (nextPollTimeoutMillis != 0) {
// 因为下一条Message尚未到处理时间,则会将等待过程中需要处理的内容交给CPU
Binder.flushPendingCommands();
}
// 这里会有一个等待,在这个等待中设置了一个超时时间,即postDelayed等方式发送的延迟处理的消息,其实是通过等待一定的时间再继续执行的方式来进行
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) {
// 如果当前的msg不为空,但是这个msg中的Handler为空,那么直接拿下一个消息,因为这个消息已经没有Handler来进行处理
// 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;
}
// 将当前消息的指向置为null
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
在这里重复一遍关于 MessageQueue.next()方法
当nextPollTimeoutMillis是大于0的时候,那么下一次循环的时候会调用nativePollOnce方法,让队列阻塞。但是如果当前的队列是阻塞的,这个时候加入了一个不是延迟发送的message的时候,就会将这个message加入队首,然后主动唤醒队列,处理这个消息
六、自定义手写Handler:
Handler实际上就是一个生产者与消费者模式。
public class Handler {
private MessageQueue mQueue;
public Handler() {
Looper mLooper = Looper.myLooper();
mQueue = mLooper.mQueue;
}
/**
* 发送消息,加入队列
* @param msg
*/
public void sendMessage(Message msg){
msg.target = this;
// 传入消息
mQueue.enqueueMessage(msg);
}
/**
* 处理消息
* @param msg
*/
public void handleMessage(Message msg){
}
/**
* 分发消息
* @param msg
*/
public void dispatchMessage(Message msg){
handleMessage(msg);
}
}
Message类:
public class Message {
// Message在使用Handler发送消息的时候,将Handler赋值给对应的Message
//用于在Looper.loop()获取消息之后,通过Handler实例分发消息
Handler target;
public Object obj;
public int what;
public Message() {
}
@Override
public String toString() {
return obj.toString();
}
}
MessageQueue类:
public class MessageQueue {
private Message[] items;
// 入队和出队的索引位置
private int putIndex;
private int takeIndex;
// 计数器
private int count;
private Lock lock;
private Condition notEmpty;
private Condition notFull;
public MessageQueue() {
items = new Message[50];
// 可重入锁
lock = new ReentrantLock();
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
/**
* 取出消息
*
* @return
*/
public Message next() {
Message msg = null;
try {
lock.lock();
// 如果消息队列为null,则等待新消息的入队
while (count == 0) {
notEmpty.await();
}
msg = items[takeIndex];
items[takeIndex] = null;
takeIndex = (++takeIndex == items.length) ? 0 : takeIndex;
count--;
// 已经消费了一个消息,可以继续入队
notFull.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return msg;
}
/**
* 加入队列
*
* @param msg
*/
public void enqueueMessage(Message msg) {
// 使用while循环,这样的话,就可以避免不需要唤醒的子线程继续等待。因为是唤醒了全部,有可能唤醒了不需要被唤醒的子线程
// 但是如果是使用if,则只会等待一次
try {
lock.lock();
// 如果消息队列已满,则等待
while (count == 50) {
notFull.await();
}
items[putIndex] = msg;
putIndex = (++putIndex == items.length) ? 0 : putIndex;
count++;
// 如果消息队列不为null,则可以取出消息
notEmpty.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
Looper类:
public class Looper {
public MessageQueue mQueue;
static ThreadLocal sThreadLocal = new ThreadLocal<>();
private Looper(){
mQueue = new MessageQueue();
}
/**
* 初始化Looper对象
*/
public static void prepare(){
if (sThreadLocal.get() != null){
throw new RuntimeException("一个线程只能有一个looper");
}
sThreadLocal.set(new Looper());
}
/**
* 获取当前线程的Looper对象
* @return
*/
public static Looper myLooper(){
return sThreadLocal.get();
}
public static void loop(){
Looper me = myLooper();
if (me == null){
throw new RuntimeException("当前线程无Looper对象");
}
MessageQueue messageQueue = me.mQueue;
for (;;){
Message msg = messageQueue.next();
if (msg != null){
msg.target.dispatchMessage(msg);
}
}
}
}
七、new Handler()两种方式
private Handler mHandler1 = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
private Handler mHandler2 = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
一种是通过Handler的无参构造器来创建,一种是通过有参构造器来创建。
/**
* 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);
}
}
在Handler中,是在dispatchMessage中来分发消息的,而这里的分发消息,是首先判断mCallback是否为空来执行的。
为什么使用Handler消息机制可以跨进程?
因为在同一个进程里,内存是共享的,Looper对象创建在主线程,Handler对象创建在主线程,然后再子线程中使用这个Handler对象发送消息,因为MessageQueue是在Looper对象中的,而Looper对象是在主线程中创建的,而且Handler对象是在主线程中创建的,所以Handler对象的handleMessage方法是在主线程中执行的,又因为Looper.loop()是在主线程中执行的,每次都是轮循MessageQueue获取Message,然后通过主线程的Handler对象的dispatchMessage分发,发送到handleMessage。
八、Handler内存泄露的原因?为什么其他的内部类没有说过这个问题?
(1)因为定义的匿名内部类,会持有外部类的引用this,可以直接调用外部类的方法和属性。
(2)生命周期的问题。因为Handler.sendMessageAtTime会调用enqueueMessage,然后msg.target=this,说明Message会持有Handler,而Message在传递过程中,会有延时的情况,比如设置20s执行、20分钟执行,这样就会出现Message持有了Handler,而Handler持有了Activity,但是当Activity销毁的时候,可能Message还没处理,还在延迟等待,这样就导致Activity根据JVM可达性分析算法得出Activity不能被GC回收。
https://www.jianshu.com/p/e6877401a0e2
九、Handler#runWithScissors
在创建WMS对象的时候,WMS的main方法中就通过调用DisplayThread.getHandler().runWithScissors()方法创建。
WMS的main方法是在SystemServer进程的主线程调用的,然后在调用WMS的main方法的时候,通过DisplayThread这个HandlerThread,实现异步处理,而这里的异步处理,其实是通过跨线程的一个同步,即通过DisplayThread中的Handler对象,调用runWithScissors方法,该方法其实是可以实现发送消息等待消息处理完成之后再继续执行runWithScissors方法调用位置之后的代码,即等待消息处理完之后再进行return WMS对象。
public static WindowManagerService main(final Context context, final InputManagerService im,
final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
ActivityTaskManagerService atm) {
return main(context, im, showBootMsgs, onlyCore, policy, atm,
SurfaceControl.Transaction::new);
}
@VisibleForTesting
public static WindowManagerService main(final Context context, final InputManagerService im,
final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
ActivityTaskManagerService atm, TransactionFactory transactionFactory) {
// 这里其实就是在DisplayThread线程处理WMS的初始化
DisplayThread.getHandler().runWithScissors(() ->
sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
atm, transactionFactory), 0);
return sInstance;
}
1.Handler#runWithScissors
根据上面的调用,上面的runWithScissors传入的就是一个Runnable实例,只不过是采用了lambda表达式的做法,所以Runnable的run()方法的方法体是就是:
sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy, atm, transactionFactory), 0);
public final boolean runWithScissors(@NonNull Runnable r, long timeout) {
if (r == null) {
throw new IllegalArgumentException("runnable must not be null");
}
if (timeout < 0) {
throw new IllegalArgumentException("timeout must be non-negative");
}
// 如果调用runWithScissors方法的线程是与消息处理线程在同一个线程
// 则直接执行Runnable的run方法。
if (Looper.myLooper() == mLooper) {
r.run();
return true;
}
// 如果调用runWithScissors方法的线程与消息处理线程不是同一个,则创建一个BlockingRunnable
BlockingRunnable br = new BlockingRunnable(r);
// 并且调用BlockingRunnable.postAndWait方法
// 这里的返回值会先等待,等待DisplayThread线程中的Handler处理完
// 消息之后,然后唤醒BlockingRunnable
// 该Handler处理消息是在DisplayThread线程,而BlockingRunnable
// 而BlockingRunnable调用wait并不会让DisplayThread线程中的
// Handler等待,Handler依然会处理消息,处理消息其实就是调用了
// BlockingRunnable.run()方法,当BlockingRunnable.run()执行完成
// 就会调用notifyAll唤醒BlockingRunnable,执行postAndWait的return
return br.postAndWait(this, timeout);
}
2.BlockingRunnable
private static final class BlockingRunnable implements Runnable {
private final Runnable mTask;
private boolean mDone;
public BlockingRunnable(Runnable task) {
mTask = task;
}
@Override
public void run() {
try {
// 这个mTask其实就是runWithScissors中传入的Runnable对象
// BlockingRunnable的run方法的执行,其实就是需要Handler遍历到消息处理的时候
// 然后处理这个BlockingRunnable消息,在处理BlockingRunnable消息
// 的时候,就会处理runWithScissors的参数中的Runnable这个mTask
// 处理完成之后,就会调用notifyAll唤醒等待线程,然后执行postAndWait的return
// 因为BlockingRunnable的run方法是由Handler处理消息
// 这个消息的处理与BlockingRunnable是在不同的线程
// 所以BlockingRunnable等待的时候,其run方法是可以由handler消息处理
mTask.run();
} finally {
synchronized (this) {
mDone = true;
notifyAll();
}
}
}
public boolean postAndWait(Handler handler, long timeout) {
// 调用postAndWait的时候,先调用handler对象的post将消息发送出
// 如果入队成功,则为true,则开始阻塞
if (!handler.post(this)) {
return false;
}
// 在上面的方法调用中,timeout=0,则阻塞。
synchronized (this) {
if (timeout > 0) {
final long expirationTime = SystemClock.uptimeMillis() + timeout;
// 如果mDone为false的时候,则会等待,只有为true的时候,会执行
while (!mDone) {
long delay = expirationTime - SystemClock.uptimeMillis();
if (delay <= 0) {
return false; // timeout
}
try {
wait(delay);
} catch (InterruptedException ex) {
}
}
} else {
while (!mDone) {
try {
wait();
} catch (InterruptedException ex) {
}
}
}
}
return true;
}
}