文章目录
- 一、什么是handler
- 二、handler消息机制原理
- 三、Handler消息机制组件
- Handler
- Message
- Looper
- MessageQueue
- 三、Handler的三种使用方式
- 1、定时运行某一线程
- 2、1不满足时,然后当callBack不为空时,会回调callBack的handleMessage()方法
- 3、以上1、2都不满足时,调用handler的 handleMessage() 方法;
- 四、Handler的内存泄漏问题
- 原因:
- 解决办法
- 方法一:程序逻辑代码。
- 1.在关闭Activity的时候停掉你的后台线程。
- 2.消息对象从消息队列移除
- 方法二:将Handler声明为静态类,activity使用弱引用赋值进去
一、什么是handler
二、handler消息机制原理
handler消息机制原理:本质就是在线程之间实现一个消息队列(MessageQueue)。 生产者Handler在异步线程通过sendMessageDelayed() 将消息添加至MessageQueue, 消费者Looper通过loop()中死循环将MessageQueue中的msg取出后发送给产生此msg的handler的handleMessage() 在主线程进行处理;
三、Handler消息机制组件
Handler
代码语言:javascript复制public Handler(Callback callback, boolean async) {
//mLooper每个线程只有唯一一个Looper
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;
}
sendMessageDelayed将msg发送至消息队列中
代码语言:javascript复制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 boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
looper将msg分发到这里,然后接受消息的线程处理msg
代码语言:javascript复制 public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 接受msg的线程处理消息
handleMessage(msg);
}
}
Message
代码语言:javascript复制public final class Message implements Parcelable {
/**
*用户定义的消息代码,以便收件人可以识别此消息的内容。每个{@link Handler}都有自己的消息代码名称
*空间,因此您不必担心与其他处理程序冲突。
*/
public int what;
/**
* 可选的Messenger,可以发送对此消息的回复。确切如何使用它的*语义取决于发送者和接收者。
*/
public Messenger replyTo;
/**
* 可选字段,指示发送消息的uid。这仅对{@link Messenger}发布的消息有效;否则,它将是-1。
*/
public int sendingUid = -1;
/*package*/ int flags;
/*package*/ Bundle data;
/*package*/ Handler target;
/*package*/ Runnable callback;
// 我们存储这些东西的链表
/*package*/ Message next;
Looper
代码语言:javascript复制private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
代码语言:javascript复制 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
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);
}
try {
//lopper在这里根据msg.target(handler)分发msg
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
MessageQueue
代码语言:javascript复制//底部是链表的形式获取msg
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
for (;;) {
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的三种使用方式
由以下代码知道,handler有3种使用方式:
代码语言:javascript复制Handler中looper最后把msg发送到该方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
//1、线程定时运行
handleCallback(msg);
} else {
if (mCallback != null) {
//2、mCallback的回调方法
if (mCallback.handleMessage(msg)) {
return;
}
}
//3、handler的方法
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
1)、定时运行某一线程:public final boolean post(Runnable r) 2)、1)不满足时,然后当callBack不为空时,会回调callBack的handleMessage()方法:
代码语言:javascript复制public Handler(Callback callback) {
this(callback, false);
}
public interface Callback {
public boolean handleMessage(Message msg);
}
3)、以上1)、2)都不满足时,调用handler的 handleMessage()方法;
1、定时运行某一线程
例1:
代码语言:javascript复制private Runnable mNextRunnable = new Runnable() {
@Override
public void run() {
mHandler.removeCallbacks(mNextRunnable);
if(isCycle()){
setCurrentItem(getCurrentItem() 1, true);
mHandler.postDelayed(mNextRunnable, mDelayTime);
}
}
};
例2:
代码语言:javascript复制new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Activity activity = (Activity)context;
if (activity != null && !activity.isFinishing()) {
ToastUtil.makeToast(context,“ OK ”, Toast.LENGTH_SHORT).show();
}
}
}, 1000);
由以下代码知道:当时间到了才将msg加到队列中
代码语言:javascript复制MessageQueue中:
boolean enqueueMessage(Message msg, long when) {
msg.when = when;
Message p = mMessages;
boolean needWake;
//当时间到了才将msg加到队列中
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
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;
}
2、1不满足时,然后当callBack不为空时,会回调callBack的handleMessage()方法
3、以上1、2都不满足时,调用handler的 handleMessage() 方法;
四、Handler的内存泄漏问题
原因:
1、当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用(不然你怎么可能通过Handler来操作Activity中的View?)。而Handler通常会伴随着一个耗时的后台线程(例如从网络拉取图片)一起出现,这个后台线程在任务执行完毕(例如图片下载完毕)之后,通过消息机制通知Handler,然后Handler把图片更新到界面。然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)。 2、另外,如果你执行了Handler的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,那么在你设定的delay到达之前,会有一条MessageQueue -> Message -> Handler -> Activity的链,导致你的Activity被持有引用而无法被回收。
举例:
代码语言:javascript复制public class LeakCanaryActivity extends AppCompatActivity
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
Message message = Message.obtain();
message.what = 1;
mHandler.sendMessageDelayed(message,10*60*1000);
}
}
解决办法
方法一:程序逻辑代码。
1.在关闭Activity的时候停掉你的后台线程。
线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。
代码语言:javascript复制 private void recycle() {
mExecutor.shutdownNow();
}
2.消息对象从消息队列移除
如果你的Handler是被delay的Message持有了引用,那么使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除就行了。
代码语言:javascript复制/**
*删除消息队列中任何待处理的Runnable
*/
public final void removeCallbacks(Runnable r)
{
mQueue.removeMessages(this, r, null);
}
例:
代码语言:javascript复制 private void recycle() {
if (mHandler != null) {
mHandler.removeCallbacks(null);
}
}
方法二:将Handler声明为静态类,activity使用弱引用赋值进去
静态类不持有外部类的对象,所以你的Activity可以随意被回收。代码如下:
代码语言:javascript复制static class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
mImageView.setImageBitmap(mBitmap);
}
}
但其实没这么简单。使用了以上代码之后,你会发现,由于Handler不再持有外部类对象的引用,导致程序不允许你在Handler中操作Activity中的对象了。所以你需要在Handler中增加一个对Activity的弱引用(WeakReference):
代码语言:javascript复制static class MyHandler extends Handler {
WeakReference<Activity > mActivityReference;
MyHandler(Activity activity) {
mActivityReference= new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
final Activity activity = mActivityReference.get();
if (activity != null) {
mImageView.setImageBitmap(mBitmap);
}
}
}