Handler
因为 Android
只允许在主线程更新 UI
,所以我们在子线程获取到的数据需要切换线程到主线程再做 UI
处理,而 Handler
是一种用于线程间消息传递的机制,能够实现线程切换的功能。
基本使用方式
步骤一: 创建 Handler 实例
// callback 方式
private Handler callbackHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// 更新UI等操作
return true;
}
});
// 成员内部类
private Handler innerHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 更新UI等操作
}
};
// 静态自定义 Handler 内部类
private static class MyHandler extends Handler {
private WeakReference<MainActivity> mReference;
public MyHandler(MainActivity activity) {
mReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
// 更新UI等操作
if (mReference.get() != null) {
switch (msg.what) {
case 0:
Toast.makeText(mReference.get(), msg.obj.toString(), Toast.LENGTH_SHORT).show();
break;
case 1:
Toast.makeText(mReference.get(), "这是空消息方式", Toast.LENGTH_SHORT).show();
break;
case 2:
Toast.makeText(mReference.get(), "这是延时空消息方式", Toast.LENGTH_SHORT).show();
break;
}
}
}
}
总结:通常使用自定义静态内部类的方式来使用,能够避免内存泄漏。
步骤二: 调用 Handler 相关send 或 post 方法
// 子线程中发送消息
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain();
message.what = 0;
message.arg1 = 100;
message.obj = "普通消息方式";
// send 方式发送普通消息和延时消息
mMyHandler.sendMessage(message);
mMyHandler.sendMessageDelayed(message, 2000);
// 发送空消息
mMyHandler.sendEmptyMessage(1);
mMyHandler.sendEmptyMessageDelayed(2, 5000);
// post 方式发送普通消息
mMyHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "Runnable 消息方式", Toast.LENGTH_SHORT).show();
}
});
// post 方式发送延时消息
mMyHandler.postDelayed(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "Runnable 延时消息方式", Toast.LENGTH_SHORT).show();
}
}, 1000);
}
}).start();
步骤三: 移除相关消息
这一步不是必须的,是为了确保避免内存泄露风险。
@Override
protected void onDestroy() {
super.onDestroy();
mMyHandler.removeCallbacksAndMessages(null);
}
总结: 通常发送消息的方式有 3 种,分别是 sendMessage
、 sendEmptyMessage
、post
方法,每种方式又有延时发送的方法,分别是 sendMessageDelayed
、 sendEmptyMessageDelayed
、 postDelayed
,延时发送的时间单位为毫秒,即多少毫秒后再发送。
源码分析
以下相关源码会省略一些跟分析无关的代码,只保留关键代码。
ActivityThread
在应用线程的入口类 ActivityThread
的 main
方法中,有如下调用:
public static void main(String[] args) {
// 创建主线程 Looper
Looper.prepareMainLooper();
// Looper 开始工作
Looper.loop();
}
在 mian
方法中,调用了跟 Looper
相关的两个方法,接下来我们看一下 Looper
类。
Looper
先看一下 Looper
类重要的成员变量
public final class Looper {
// ThreadLocal 绑定线程和 Looper 对象
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
// 主线程的 Looper 对象
private static Looper sMainLooper;
// 消息队列
final MessageQueue mQueue;
// 当前线程
final Thread mThread;
}
我们先看 prepareMainLooper
相关方法:
public static void prepareMainLooper() {
// false 表示不允许退出
prepare(false);
synchronized (Looper.class) {
// 这里判断 Looper 是否已经存在了,只允许有一个
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
// 该方法主要给子线程调用
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
// 这里判断 Looper 只允许有一个,方法只能调用一次
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 初始化 Looper 对象并放进 ThreadLocal 里面
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
// 创建消息队列
mQueue = new MessageQueue(quitAllowed);
// 初始化当前线程 ,线程关联从这里开始
mThread = Thread.currentThread();
}
prepareMainLooper()
方法内容归纳:
判断
Looper
是否已经存在,存在则报异常。初始化
Looper
对象并放进ThreadLocal
里面。Looper
里面初始化了MessageQueue
对象并初始化了当前线程。
现在,我们看一下 loop()
方法,:
public static void loop() {
// 从 ThreadLocal 中获取当前线程的 Loop
final Looper me = myLooper();
// 如果 Looper 还没初始化,则报错
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 获取 Looper 里面的消息队列
final MessageQueue queue = me.mQueue;
for (;;) {// 死循环
// 取出消息,可能会阻塞线程
Message msg = queue.next(); // might block
if (msg == null) {
// 消息为空 ,结束循环
return;
}
try {
// 回调 Handler 的 dispatchMessage 方法
msg.target.dispatchMessage(msg);
} finally {
}
// 对分发了的消息进行回收操作
msg.recycleUnchecked();
}
}
loop()
方法里面的逻辑归纳为:
先判断
Looper
对象是否已经初始化。在死循环里面不断调用
Looper
的成员MessageQueue
的next()
方法来获取Message
对象。取出的消息不为空则回调
Message
对象的成员target
的dispatchMessage
方法 (tartget
其实就是Handler
)。对分发了的消息进行回收操作。
Handler
Looper
中会回调 Handler
的 dispatchMessage
方法,那么我们先看一下 Handler
类重要的成员变量和方法:
public class Handler {
// 当前线程对应的 Looper
final Looper mLooper;
// 指向 Looper 的 MessageQueue
final MessageQueue mQueue;
// Callback 对象
final Callback mCallback;
// 是否是异步消息,默认同步
final boolean mAsynchronous;
// Callback 接口对象
public interface Callback {
public boolean handleMessage(Message msg);
}
// 该方法需要子类覆写实现逻辑操作
public void handleMessage(Message msg) {
}
}
构造方法
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(boolean async) {
this(null, async);
}
public Handler(Callback callback, boolean async) { //该构造给主线程调用
// 指向 Looper 对象
mLooper = Looper.myLooper();
// 必须要有Looper,不然报异常
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// 指向 Looper 的 mQueue
mQueue = mLooper.mQueue;
// 赋值回调接口对象
mCallback = callback;
// 赋值是否异步
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
// 此构造给那些在子线程创建 Handler 时使用,需要主动调用 Looper.prepare() 方法
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handler
类主要面向开发者,所以我们需要全面掌握里面的方法。首先是构造函数,共 6 个构造函数,但是最终会往两个构造函数调用,这两个构造函数分别给主线程和子线程调用,具体看代码注释。
obtainMessage 方法
public final Message obtainMessage()
{
return Message.obtain(this);
}
public final Message obtainMessage(int what)
{
return Message.obtain(this, what);
}
public final Message obtainMessage(int what, Object obj)
{
return Message.obtain(this, what, obj);
}
public final Message obtainMessage(int what, int arg1, int arg2)
{
return Message.obtain(this, what, arg1, arg2);
}
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
{
return Message.obtain(this, what, arg1, arg2, obj);
}
Handler
里面有多个 obtainMessage
重载方法,用于获取 Message
对象,其实内部是调用 Message
的 obtain
静态方法的。所以我们也可以通过 Message
的静态 obtanin
方法来获取 Message
对象,最好不要直接用 new
关键字来创建 Message
对象,原因后面会说。
发送 Message 方法
public final boolean post(Runnable r)
{
// 延迟0秒发送
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(Runnable r, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean sendMessage(Message msg)
{
// 一般使用这个方法,延时值为0
return sendMessageDelayed(msg, 0);
}
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);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
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;
// 检查一下消息队列
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 static Message getPostMessage(Runnable r) {
// 创建 Message 对象并把Runnable对象赋值给 Message 的 callback
Message m = Message.obtain();
m.callback = r;
return m;
}
private static Message getPostMessage(Runnable r, Object token) {
Message m = Message.obtain();
m.obj = token;
m.callback = r;
return m;
}
Handler
里面也有很多发送消息的方法,有 3 种发送方式:
post()
类型的方法,传入的是Runnable
对象,内部会通过getPostMessage()
方法来封装Message
对象,并把Runnable
对象赋值给Message
的callback
成员对象。sendEmpty()
类型的也不需要传入Message
对象,内部会通过Message.obtain()
方法获取Message
对象。send()
类型则需要外部传入Message
对象。
无论调用何种方式发送消息,最终会调用 enqueueMessage
方法将消息对象入队,代码如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 把当前 Handler 传给 target 变量,用于分发回调
msg.target = this;
if (mAsynchronous) {
// 设置为异步消息
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
该方法会调用 MessageQueue
的 enqueueMessage
方法来将消息入队,注意方法内调用了 msg.target = this
,结合上面 Looper
的 loop
方法,会回调 Handler
的 dispatchMessage()
方法, dispatchMessage()
的源码如下:
// 消息分发
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 如果接收的消息中,携带这一个 callback ,就执行
handleCallback(msg);
} else {
if (mCallback != null) {
// 如果实例化 Handler 时实现了接口回调,就执行
if (mCallback.handleMessage(msg)) {
return;
}
}
// 执行这个方法
handleMessage(msg);
}
}
// 运行消息的 Callback 接口对象
private static void handleCallback(Message message) {
message.callback.run();
}
方法归纳:
首先判断
Message
的callback
对象是否为空,由之前分析得知,当用post
方式发送消息时,callback
则不为空,此时会运行callback
的run
方法,即post
传入的Runnable
的run
方法。然后会判断
mCallback
是否为空,如果不为空,则回调mCallback
的handleMessage
方法,此时的mCallback
由创建Handler
的时候通过构造方法传入,如果handleMessage
方法返回true
,则不执行最后的Handler
的handleMessage
方法,如果返回false
,则执行。执行
Handler
的handleMessage
方法,该方法通常会被子类覆写。
总结(消息分发执行的优先级): post 的方式 > callback 方法 > 覆写方式 。
MessageQueue
上述讲到入队的时候,会调用 MessageQueue
的 enqueueMessage
方法,我们先看一下 MessageQueue
的构造方法,只有一个构造方法,如下:
MessageQueue(boolean quitAllowed) {
// 用来标识是否允许退出,主线程是不允许退出的,不然就会退出整个程序
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
入队方法源码如下:
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 ,返回 false
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;
// 单独处理链表头,队列为空或消息执行时间比之前的消息还早
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
// 把 msg 设置为新的链表头
msg.next = p;
// 将 mMessages 指向当前的链表头
mMessages = msg;
needWake = mBlocked;
} else {
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;
}
// 唤醒线程,native 方法,添加Message 到 C环境中的消息队列中
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
方法归纳:
判断
Message
的target
对象,即Handler
是否存在,不存在报异常。判断当前
Message
是否已经在使用,是则报异常,避免重复发送同一个消息。判断是否已经退出循环了,如果是,则直接回收
Message
,返回false
,入队失败。标记当前
Message
已经在使用,然后把当前Message
插入到链表对应的位置上,按时间执行从早到晚排序。
在上述 Looper
的 loop
方法中,会不断循环地调用 MessageQueue
的 next
方法来做出队处理,获取 Message
来分发回调,next
方法如下:
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1;
// 0 表示不会阻塞,立即返回
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 根据 nextPollTimeoutMillis 判断是否让其处于线程阻塞状态
// 取出一个消息 ,native方法,在C环境中创建了一个 NativeMessageQueue 数据对象
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 如果有消息屏障,会过滤掉后面的同步消息,找到下一个异步消息,默认都是同步消息
if (msg != null && msg.target == null) {
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毫秒(超时),如果期间有程序唤醒会立即返回。
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 时间到了,下面的逻辑是把消息从链表中取出来
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 {
没有消息的时候,一直阻塞不会超时
nextPollTimeoutMillis = -1;
}
// 如果调用了 quit 方法了,则返回 null ,Looper 就会结束循环
if (mQuitting) {
dispose();
return null;
}
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// 没有 idle handlers 要处理,Looper 继续阻塞等待
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 处理一些不紧急的任务
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;
//在处理 idle handler 的时候可能会有新的消息过来, 所以需要设置为 0 立即去取一次消息,
//当没有 idle handler 的时候会调 continue 阻塞。
nextPollTimeoutMillis = 0;
}
}
方法归纳:
如果没有消息或没到消息执行时间,会阻塞线程,否则唤醒线程。
获取最早的异步消息并返回。
如果调用过
quit
退出方法,则返回空,Looper
就会结束循环。处理一些不紧急的 idle handler 任务。
注意:主线程调用 quit
方法会报异常,默认不能退出,quit
方法代码如下:
void quit(boolean safe) {
// 主线程不允许退出循环
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
// 设置已经在退出
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
从源码可以看出当我们调用 quit
方法时,如果 safe
为 false
时,执行的是 removeAllMessagesLocked
方法,该方法主要是把 MessageQueue
消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过 sendMessageDelayed
或通过 postDelayed
等方法发送)还是非延迟消息。
当 safe
为 true
时,其内部调用的是 removeAllFutureMessagesLocked
方法,该方法只会清空 MessageQueue
消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让 Handler
去处理完成后才停止 Looper
循环。
Message
在整个 Handler
机制中,Message
就像信件一样在其中传递,作为线程消息传递的物件,我们看看 Message
类。
public final class Message implements Parcelable {
// 让接收者知道消息是做什么的
public int what;
// 用来传递整型参数
public int arg1;
// 用来传递整型参数
public int arg2;
// 用来传递复杂对象
public Object obj;
// 标记消息被使用
/*package*/ static final int FLAG_IN_USE = 1 << 0;
// 标记消息是异步的
/*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;
// 消息当前的标记
/*package*/ int flags;
// 执行的时间
/*package*/ long when;
// 创建Handler时,target会被赋值给对应的 Handler
/*package*/ Handler target;
// Handler 用 post 消息发送消息时,会被赋值
/*package*/ Runnable callback;
// Bundle 对象
/*package*/ Bundle data;
// 指向的下一个 Message ,是个链表结构
/*package*/ Message next;
// 消息池链表头对象
private static Message sPool;
// 当前消息池的消息数量
private static int sPoolSize = 0;
// 消息池最大维护量
private static final int MAX_POOL_SIZE = 50;
public static Message obtain() {
// 尽量用这个方法来创建消息,消息池不为null ,则直接取出来复用消息
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();
}
// 回收资源
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
// 维护消息池
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
/*package*/ boolean isInUse() {
return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
}
/*package*/ void markInUse() {
flags |= FLAG_IN_USE;
}
// 默认 public 构造方法,但是最好不要用
public Message() {
}
}
Handler消息机制的工作原理总结
通过Handler发送消息,此时会将Message入队列到MessageQueue中,并且唤醒等待的Looper
Looper获取的消息会投递给对应的Handler处理
创建与线程绑定的
Looper
,同时会创建一个与之关联的MessageQueue
用于存放消息。开启消息循环,从
MessageQueue
中获取待处理消息,若无消息会阻塞线程。Handler
会调用MessageQueue
的enqueueMessage
方法,将Message
入队并且唤醒等待的Looper
。Looper
会循环调用MessageQueue
的next
方法,将Message
出队并回调Message
的target
对象的dispatchMessage
方法对Message
进行分发处理,target
就是对应的Handler
对象。
时序图如下:
答疑解惑
其实以下这些疑问在上述分析中都能找到答案,这里再简要说明一下原因。
为什么 Handler 能够切换线程执行?
我们在子线程发送的消息,最终会在 Looper
所在的线程进行分发并回调,而 Looper
默认就是运行在主线程的,也就是说回调的 handleMessage()
方法是运行在主线程的,也就达到了线程切换的目的。因此,handleMessage()
方法执行的线程就是 Looper
所在的线程,而与 Handler
创建时所在的线程无关。
示例调用链:
为什么我们能在主线程直接使用 Handler,而不需要创建 Looper?
因为在程序启动的时候,系统会自动初始化 Looper
并调用 loop
方法,上述源码有讲到。
为什么创建 Message 对象推荐使用 Message.obtain()获取?
Message
类内维护了一个 Message
缓存池,用 obtain
方法时,会优先在池内取 Message
对象,达到节省开销、减少内存消耗的目的。缓存池的来源就在 Message
的 recycleUnchecked
方法中,也就是在 Message
对象被回收资源时,会添加到缓存池内,缓存池最大量为 50 。
子线程可以创建 Handler 吗?
可以,创建 Handler
跟线程没关系,只需按照 Handler
的构造创建即可。因为系统只在主线程自动初始化 Looper
并调用 loop
方法,所以在子线程需要主动调用 Looper.prepare()
方法创建 Looper
,然后调用 loop()
方法开始循环工作。
子线程里使用示例代码:
// 子线程中发送消息
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
threadHandler = new Handler(Looper.myLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
Log.d("MainActivity", "当前线程名称 == " + Thread.currentThread().getName());
return true;
}
});
Looper.loop();
Message message = Message.obtain();
message.what = 0;
message.obj = "普通消息方式";
mMyHandler.sendMessage(message);
}
}).start();
mMyHandler.postDelayed(new Runnable() {
@Override
public void run() {
threadHandler.sendEmptyMessage(1);
}
}, 2000);
Handler(Callback) 跟 Handler() 这两个构造方法的区别在哪?
上述分析有讲到,只是执行的优先级不同,callback
方法优先执行,而且如果返回 true
,还可以拦截 handleMessage
的回调。
Handler 引起的内存泄露原因以及最佳解决方案是什么?
Handler
允许我们发送延时消息,如果在延时期间用户关闭了 Activity
,那么该 Activity
会泄露。这个泄露是因为 Message
会持有 Handler
,而又因为 Java
的特性,内部类会持有外部类,使得 Activity
会被 Handler
持有,这样最终就导致 Activity
泄露。
解决该问题的最有效的方法是:将 Handler
定义成静态的内部类,在内部持有 Activity
的弱引用,并及时移除所有消息,具体代码在上述 Handler
使用示例中有。